summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-06-03 18:08:54 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-06-03 18:08:54 +0000
commit27484d14658e92177e059ef905e9562c71ad9a3f (patch)
tree085e2a2720796fab97079e964ddf18ce391ad5f1
parentf5f6cb45c73c8aa059c3006a3696014522a41a4b (diff)
downloadgitlab-ce-27484d14658e92177e059ef905e9562c71ad9a3f.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue6
-rw-r--r--app/assets/javascripts/integrations/constants.js34
-rw-r--r--app/assets/javascripts/integrations/edit/components/integration_form.vue4
-rw-r--r--app/assets/javascripts/integrations/edit/components/sections/trigger.vue26
-rw-r--r--app/assets/javascripts/integrations/edit/components/trigger_field.vue46
-rw-r--r--app/assets/javascripts/integrations/edit/index.js2
-rw-r--r--app/controllers/registrations/welcome_controller.rb2
-rw-r--r--app/helpers/nav/top_nav_helper.rb76
-rw-r--r--app/models/user.rb1
-rw-r--r--app/models/user_detail.rb3
-rw-r--r--app/views/layouts/header/_default.html.haml2
-rw-r--r--app/views/registrations/welcome/show.html.haml5
-rw-r--r--app/workers/database/ci_namespace_mirrors_consistency_check_worker.rb2
-rw-r--r--app/workers/database/ci_project_mirrors_consistency_check_worker.rb2
-rw-r--r--config/feature_flags/development/ci_namespace_mirrors_consistency_check.yml8
-rw-r--r--config/feature_flags/development/ci_project_mirrors_consistency_check.yml8
-rw-r--r--config/feature_flags/development/user_other_role_details.yml8
-rw-r--r--config/feature_flags/development/vsa_incremental_worker.yml8
-rw-r--r--doc/development/ruby3_gotchas.md25
-rw-r--r--lib/gitlab/git_access.rb50
-rw-r--r--lib/gitlab/git_access_snippet.rb13
-rw-r--r--lib/gitlab/git_access_wiki.rb25
-rw-r--r--locale/gitlab.pot30
-rw-r--r--package.json2
-rw-r--r--spec/features/projects/pages/user_adds_domain_spec.rb2
-rw-r--r--spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb4
-rw-r--r--spec/frontend/frequent_items/components/frequent_items_list_item_spec.js2
-rw-r--r--spec/frontend/integrations/edit/components/sections/trigger_spec.js38
-rw-r--r--spec/frontend/integrations/edit/components/trigger_field_spec.js71
-rw-r--r--spec/frontend/integrations/edit/mock_data.js5
-rw-r--r--spec/helpers/nav/top_nav_helper_spec.rb113
-rw-r--r--spec/lib/gitlab/git_access_snippet_spec.rb10
-rw-r--r--spec/lib/gitlab/git_access_wiki_spec.rb16
-rw-r--r--spec/requests/api/markdown_snapshot_spec.rb10
-rw-r--r--spec/support/shared_contexts/markdown_snapshot_shared_examples.rb37
-rw-r--r--spec/workers/database/ci_namespace_mirrors_consistency_check_worker_spec.rb29
-rw-r--r--spec/workers/database/ci_project_mirrors_consistency_check_worker_spec.rb29
-rw-r--r--yarn.lock8
38 files changed, 563 insertions, 199 deletions
diff --git a/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue b/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
index 1700437aa84..6b1676eca8a 100644
--- a/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
+++ b/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
@@ -1,5 +1,6 @@
<script>
import { GlButton, GlSafeHtmlDirective } from '@gitlab/ui';
+import { snakeCase } from 'lodash';
import highlight from '~/lib/utils/highlight';
import { truncateNamespace } from '~/lib/utils/text_utility';
import { mapVuexModuleState } from '~/lib/utils/vuex_module_mappers';
@@ -56,6 +57,9 @@ export default {
highlightedItemName() {
return highlight(this.itemName, this.matcher);
},
+ itemTrackingLabel() {
+ return `${this.dropdownType}_dropdown_frequent_items_list_item_${snakeCase(this.itemName)}`;
+ },
},
};
</script>
@@ -66,7 +70,7 @@ export default {
category="tertiary"
:href="webUrl"
class="gl-text-left gl-justify-content-start!"
- @click="track('click_link', { label: `${dropdownType}_dropdown_frequent_items_list_item` })"
+ @click="track('click_link', { label: itemTrackingLabel })"
>
<project-avatar
class="gl-float-left gl-mr-3"
diff --git a/app/assets/javascripts/integrations/constants.js b/app/assets/javascripts/integrations/constants.js
index b9975eed716..87e9f818f96 100644
--- a/app/assets/javascripts/integrations/constants.js
+++ b/app/assets/javascripts/integrations/constants.js
@@ -30,12 +30,46 @@ export const integrationFormSections = {
CONNECTION: 'connection',
JIRA_TRIGGER: 'jira_trigger',
JIRA_ISSUES: 'jira_issues',
+ TRIGGER: 'trigger',
};
export const integrationFormSectionComponents = {
[integrationFormSections.CONNECTION]: 'IntegrationSectionConnection',
[integrationFormSections.JIRA_TRIGGER]: 'IntegrationSectionJiraTrigger',
[integrationFormSections.JIRA_ISSUES]: 'IntegrationSectionJiraIssues',
+ [integrationFormSections.TRIGGER]: 'IntegrationSectionTrigger',
+};
+
+export const integrationTriggerEvents = {
+ PUSH: 'push_events',
+ ISSUE: 'issues_events',
+ CONFIDENTIAL_ISSUE: 'confidential_issues_events',
+ MERGE_REQUEST: 'merge_requests_events',
+ NOTE: 'note_events',
+ CONFIDENTIAL_NOTE: 'confidential_note_events',
+ TAG_PUSH: 'tag_push_events',
+ PIPELINE: 'pipeline_events',
+ WIKI_PAGE: 'wiki_page_events',
+};
+
+export const integrationTriggerEventTitles = {
+ [integrationTriggerEvents.PUSH]: s__('IntegrationEvents|A push is made to the repository'),
+ [integrationTriggerEvents.ISSUE]: s__(
+ 'IntegrationEvents|An issue is created, updated, or closed',
+ ),
+ [integrationTriggerEvents.CONFIDENTIAL_ISSUE]: s__(
+ 'IntegrationEvents|A confidential issue is created, updated, or closed',
+ ),
+ [integrationTriggerEvents.MERGE_REQUEST]: s__(
+ 'IntegrationEvents|A merge request is created, updated, or merged',
+ ),
+ [integrationTriggerEvents.NOTE]: s__('IntegrationEvents|A comment is added on an issue'),
+ [integrationTriggerEvents.CONFIDENTIAL_NOTE]: s__(
+ 'IntegrationEvents|A comment is added on a confidential issue',
+ ),
+ [integrationTriggerEvents.TAG_PUSH]: s__('IntegrationEvents|A tag is pushed to the repository'),
+ [integrationTriggerEvents.PIPELINE]: s__('IntegrationEvents|A pipeline status changes'),
+ [integrationTriggerEvents.WIKI_PAGE]: s__('IntegrationEvents|A wiki page is created or updated'),
};
export const billingPlans = {
diff --git a/app/assets/javascripts/integrations/edit/components/integration_form.vue b/app/assets/javascripts/integrations/edit/components/integration_form.vue
index 9f43360fb73..f751dc6c7a1 100644
--- a/app/assets/javascripts/integrations/edit/components/integration_form.vue
+++ b/app/assets/javascripts/integrations/edit/components/integration_form.vue
@@ -49,6 +49,10 @@ export default {
import(
/* webpackChunkName: 'integrationSectionJiraTrigger' */ '~/integrations/edit/components/sections/jira_trigger.vue'
),
+ IntegrationSectionTrigger: () =>
+ import(
+ /* webpackChunkName: 'integrationSectionTrigger' */ '~/integrations/edit/components/sections/trigger.vue'
+ ),
GlBadge,
GlButton,
GlForm,
diff --git a/app/assets/javascripts/integrations/edit/components/sections/trigger.vue b/app/assets/javascripts/integrations/edit/components/sections/trigger.vue
new file mode 100644
index 00000000000..9af5070d4cf
--- /dev/null
+++ b/app/assets/javascripts/integrations/edit/components/sections/trigger.vue
@@ -0,0 +1,26 @@
+<script>
+import { mapGetters } from 'vuex';
+
+import TriggerField from '../trigger_field.vue';
+
+export default {
+ name: 'IntegrationSectionTrigger',
+ components: {
+ TriggerField,
+ },
+ computed: {
+ ...mapGetters(['currentKey', 'propsSource']),
+ },
+};
+</script>
+
+<template>
+ <div>
+ <trigger-field
+ v-for="event in propsSource.triggerEvents"
+ :key="`${currentKey}-trigger-fields-${event.name}`"
+ :event="event"
+ class="gl-mb-3"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/integrations/edit/components/trigger_field.vue b/app/assets/javascripts/integrations/edit/components/trigger_field.vue
new file mode 100644
index 00000000000..dc5ae2f3a3d
--- /dev/null
+++ b/app/assets/javascripts/integrations/edit/components/trigger_field.vue
@@ -0,0 +1,46 @@
+<script>
+import { GlFormCheckbox } from '@gitlab/ui';
+import { mapGetters } from 'vuex';
+
+import { integrationTriggerEventTitles } from '~/integrations/constants';
+
+export default {
+ name: 'TriggerField',
+ components: {
+ GlFormCheckbox,
+ },
+ props: {
+ event: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ },
+ data() {
+ return {
+ value: false,
+ };
+ },
+ computed: {
+ ...mapGetters(['isInheriting']),
+ name() {
+ return `service[${this.event.name}]`;
+ },
+ title() {
+ return integrationTriggerEventTitles[this.event.name];
+ },
+ },
+ mounted() {
+ this.value = this.event.value || false;
+ },
+};
+</script>
+
+<template>
+ <div>
+ <input :name="name" type="hidden" :value="value" />
+ <gl-form-checkbox v-model="value" :disabled="isInheriting">
+ {{ title }}
+ </gl-form-checkbox>
+ </div>
+</template>
diff --git a/app/assets/javascripts/integrations/edit/index.js b/app/assets/javascripts/integrations/edit/index.js
index 69097ecb8ab..2360588ab39 100644
--- a/app/assets/javascripts/integrations/edit/index.js
+++ b/app/assets/javascripts/integrations/edit/index.js
@@ -83,7 +83,7 @@ function parseDatasetToProps(data) {
learnMorePath,
aboutPricingUrl,
triggerEvents: JSON.parse(triggerEvents),
- sections: JSON.parse(sections, { deep: true }),
+ sections: JSON.parse(sections),
fields: convertObjectPropsToCamelCase(JSON.parse(fields), { deep: true }),
inheritFromId: parseInt(inheritFromId, 10),
integrationLevel,
diff --git a/app/controllers/registrations/welcome_controller.rb b/app/controllers/registrations/welcome_controller.rb
index ea50099120b..a2b25acae64 100644
--- a/app/controllers/registrations/welcome_controller.rb
+++ b/app/controllers/registrations/welcome_controller.rb
@@ -45,7 +45,7 @@ module Registrations
end
def update_params
- params.require(:user).permit(:role, :other_role, :setup_for_company)
+ params.require(:user).permit(:role, :setup_for_company)
end
def requires_confirmation?(user)
diff --git a/app/helpers/nav/top_nav_helper.rb b/app/helpers/nav/top_nav_helper.rb
index 9420c95c9ce..3ceb60251c2 100644
--- a/app/helpers/nav/top_nav_helper.rb
+++ b/app/helpers/nav/top_nav_helper.rb
@@ -127,7 +127,7 @@ module Nav
href: dashboard_milestones_path,
active: active_nav_link?(controller: 'dashboard/milestones'),
icon: 'clock',
- data: { qa_selector: 'milestones_link' },
+ data: { qa_selector: 'milestones_link', **menu_data_tracking_attrs('milestones') },
shortcut_class: 'dashboard-shortcuts-milestones'
)
end
@@ -135,7 +135,7 @@ module Nav
if dashboard_nav_link?(:snippets)
builder.add_primary_menu_item_with_shortcut(
active: active_nav_link?(controller: 'dashboard/snippets'),
- data: { qa_selector: 'snippets_link' },
+ data: { qa_selector: 'snippets_link', **menu_data_tracking_attrs('snippets') },
href: dashboard_snippets_path,
**snippets_menu_item_attrs
)
@@ -148,7 +148,7 @@ module Nav
href: activity_dashboard_path,
active: active_nav_link?(path: 'dashboard#activity'),
icon: 'history',
- data: { qa_selector: 'activity_link' },
+ data: { qa_selector: 'activity_link', **menu_data_tracking_attrs('activity') },
shortcut_class: 'dashboard-shortcuts-activity'
)
end
@@ -158,13 +158,16 @@ module Nav
# we should be good.
# rubocop: disable Cop/UserAdmin
if current_user&.admin?
+ title = _('Admin')
+
builder.add_secondary_menu_item(
id: 'admin',
- title: _('Admin'),
+ title: title,
active: active_nav_link?(controller: 'admin/dashboard'),
icon: 'admin',
css_class: 'qa-admin-area-link',
- href: admin_root_path
+ href: admin_root_path,
+ data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
)
end
@@ -176,15 +179,18 @@ module Nav
active: active_nav_link?(controller: 'admin/sessions'),
icon: 'lock-open',
href: destroy_admin_session_path,
- data: { method: 'post' }
+ data: { method: 'post', **menu_data_tracking_attrs('leave_admin_mode') }
)
elsif current_user.admin?
+ title = _('Enter Admin Mode')
+
builder.add_secondary_menu_item(
id: 'enter_admin_mode',
- title: _('Enter Admin Mode'),
+ title: title,
active: active_nav_link?(controller: 'admin/sessions'),
icon: 'lock',
- href: new_admin_session_path
+ href: new_admin_session_path,
+ data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
)
end
end
@@ -218,6 +224,14 @@ module Nav
}
end
+ def menu_data_tracking_attrs(label)
+ tracking_attrs(
+ "menu_#{label.underscore.parameterize(separator: '_')}",
+ 'click_dropdown',
+ 'navigation'
+ )[:data] || {}
+ end
+
def container_view_props(namespace:, current_item:, submenu:)
{
namespace: namespace,
@@ -260,21 +274,51 @@ module Nav
def projects_submenu_items(builder:)
# These project links come from `app/views/layouts/nav/projects_dropdown/_show.html.haml`
- builder.add_primary_menu_item(id: 'your', title: _('Your projects'), href: dashboard_projects_path)
- builder.add_primary_menu_item(id: 'starred', title: _('Starred projects'), href: starred_dashboard_projects_path)
- builder.add_primary_menu_item(id: 'explore', title: _('Explore projects'), href: explore_root_path)
- builder.add_primary_menu_item(id: 'topics', title: _('Explore topics'), href: topics_explore_projects_path)
- builder.add_secondary_menu_item(id: 'create', title: _('Create new project'), href: new_project_path)
+ [
+ { id: 'your', title: _('Your projects'), href: dashboard_projects_path },
+ { id: 'starred', title: _('Starred projects'), href: starred_dashboard_projects_path },
+ { id: 'explore', title: _('Explore projects'), href: explore_root_path },
+ { id: 'topics', title: _('Explore topics'), href: topics_explore_projects_path }
+ ].each do |item|
+ builder.add_primary_menu_item(
+ **item,
+ data: { qa_selector: 'menu_item_link', qa_title: item[:title], **menu_data_tracking_attrs(item[:title]) }
+ )
+ end
+
+ title = _('Create new project')
+
+ builder.add_secondary_menu_item(
+ id: 'create',
+ title: title,
+ href: new_project_path,
+ data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
+ )
end
def groups_submenu
# These group links come from `app/views/layouts/nav/groups_dropdown/_show.html.haml`
builder = ::Gitlab::Nav::TopNavMenuBuilder.new
- builder.add_primary_menu_item(id: 'your', title: _('Your groups'), href: dashboard_groups_path)
- builder.add_primary_menu_item(id: 'explore', title: _('Explore groups'), href: explore_groups_path)
+
+ [
+ { id: 'your', title: _('Your groups'), href: dashboard_groups_path },
+ { id: 'explore', title: _('Explore groups'), href: explore_groups_path }
+ ].each do |item|
+ builder.add_primary_menu_item(
+ **item,
+ data: { qa_selector: 'menu_item_link', qa_title: item[:title], **menu_data_tracking_attrs(item[:title]) }
+ )
+ end
if current_user.can_create_group?
- builder.add_secondary_menu_item(id: 'create', title: _('Create group'), href: new_group_path)
+ title = _('Create group')
+
+ builder.add_secondary_menu_item(
+ id: 'create',
+ title: title,
+ href: new_group_path,
+ data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
+ )
end
builder.build
diff --git a/app/models/user.rb b/app/models/user.rb
index fcf1f8bfc58..51d312ed597 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -338,7 +338,6 @@ class User < ApplicationRecord
delegate :path, to: :namespace, allow_nil: true, prefix: true
delegate :job_title, :job_title=, to: :user_detail, allow_nil: true
- delegate :other_role, :other_role=, to: :user_detail, allow_nil: true
delegate :bio, :bio=, to: :user_detail, allow_nil: true
delegate :webauthn_xid, :webauthn_xid=, to: :user_detail, allow_nil: true
delegate :pronouns, :pronouns=, to: :user_detail, allow_nil: true
diff --git a/app/models/user_detail.rb b/app/models/user_detail.rb
index 3787ad1c380..b9b69d12729 100644
--- a/app/models/user_detail.rb
+++ b/app/models/user_detail.rb
@@ -2,6 +2,9 @@
class UserDetail < ApplicationRecord
extend ::Gitlab::Utils::Override
+ include IgnorableColumns
+
+ ignore_columns :other_role, remove_after: '2022-07-22', remove_with: '15.3'
REGISTRATION_OBJECTIVE_PAIRS = { basics: 0, move_repository: 1, code_storage: 2, exploring: 3, ci: 4, other: 5, joining_team: 6 }.freeze
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index f59f25f37b5..911cb85de53 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -16,7 +16,7 @@
= _('Next')
- if current_user
- .gl-display-none.gl-sm-display-block{ **tracking_attrs('Menu', 'click_dropdown', 'navigation') }
+ .gl-display-none.gl-sm-display-block
= render "layouts/nav/top_nav"
- else
- experiment(:logged_out_marketing_header, actor: nil) do |e|
diff --git a/app/views/registrations/welcome/show.html.haml b/app/views/registrations/welcome/show.html.haml
index 62499f1a6b6..911ba5e8042 100644
--- a/app/views/registrations/welcome/show.html.haml
+++ b/app/views/registrations/welcome/show.html.haml
@@ -24,11 +24,6 @@
.form-group.col-sm-12
= f.label :role, _('Role'), class: 'label-bold'
= f.select :role, ::User.roles.keys.map { |role| [role.titleize, role] }, { include_blank: _('Select a role') }, class: 'form-control js-user-role-dropdown', autofocus: true, required: true, data: { qa_selector: 'role_dropdown' }
- - if Feature.enabled?(:user_other_role_details)
- .row
- .form-group.col-sm-12.js-other-role-group.hidden
- = f.label :other_role, _('What is your job title? (optional)')
- = f.text_field :other_role, class: 'form-control'
= render_if_exists "registrations/welcome/jobs_to_be_done", f: f
= render_if_exists "registrations/welcome/setup_for_company", f: f
= render_if_exists "registrations/welcome/joining_project"
diff --git a/app/workers/database/ci_namespace_mirrors_consistency_check_worker.rb b/app/workers/database/ci_namespace_mirrors_consistency_check_worker.rb
index b2174be1402..8a4ee77cb70 100644
--- a/app/workers/database/ci_namespace_mirrors_consistency_check_worker.rb
+++ b/app/workers/database/ci_namespace_mirrors_consistency_check_worker.rb
@@ -13,8 +13,6 @@ module Database
version 1
def perform
- return if Feature.disabled?(:ci_namespace_mirrors_consistency_check)
-
results = ConsistencyCheckService.new(
source_model: Namespace,
target_model: Ci::NamespaceMirror,
diff --git a/app/workers/database/ci_project_mirrors_consistency_check_worker.rb b/app/workers/database/ci_project_mirrors_consistency_check_worker.rb
index 84052ab238b..d461ded088a 100644
--- a/app/workers/database/ci_project_mirrors_consistency_check_worker.rb
+++ b/app/workers/database/ci_project_mirrors_consistency_check_worker.rb
@@ -13,8 +13,6 @@ module Database
version 1
def perform
- return if Feature.disabled?(:ci_project_mirrors_consistency_check)
-
results = ConsistencyCheckService.new(
source_model: Project,
target_model: Ci::ProjectMirror,
diff --git a/config/feature_flags/development/ci_namespace_mirrors_consistency_check.yml b/config/feature_flags/development/ci_namespace_mirrors_consistency_check.yml
deleted file mode 100644
index 6d4e50c2753..00000000000
--- a/config/feature_flags/development/ci_namespace_mirrors_consistency_check.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_namespace_mirrors_consistency_check
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81836
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/356577
-milestone: '14.10'
-type: development
-group: group::sharding
-default_enabled: true
diff --git a/config/feature_flags/development/ci_project_mirrors_consistency_check.yml b/config/feature_flags/development/ci_project_mirrors_consistency_check.yml
deleted file mode 100644
index 5fc88ef9bfb..00000000000
--- a/config/feature_flags/development/ci_project_mirrors_consistency_check.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_project_mirrors_consistency_check
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81836
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/356583
-milestone: '14.10'
-type: development
-group: group::sharding
-default_enabled: true
diff --git a/config/feature_flags/development/user_other_role_details.yml b/config/feature_flags/development/user_other_role_details.yml
deleted file mode 100644
index 7c0b417d398..00000000000
--- a/config/feature_flags/development/user_other_role_details.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: user_other_role_details
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45635
-rollout_issue_url: https://gitlab.com/gitlab-org/growth/team-tasks/-/issues/282
-milestone: '13.7'
-type: development
-group: group::conversion
-default_enabled: false
diff --git a/config/feature_flags/development/vsa_incremental_worker.yml b/config/feature_flags/development/vsa_incremental_worker.yml
deleted file mode 100644
index 9caad7818e7..00000000000
--- a/config/feature_flags/development/vsa_incremental_worker.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: vsa_incremental_worker
-introduced_by_url:
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/353453
-milestone: '14.9'
-type: development
-group: group::optimize
-default_enabled: true
diff --git a/doc/development/ruby3_gotchas.md b/doc/development/ruby3_gotchas.md
index e4ed5039e3c..1ab7a06be24 100644
--- a/doc/development/ruby3_gotchas.md
+++ b/doc/development/ruby3_gotchas.md
@@ -138,3 +138,28 @@ installed Ruby manually or via tools like `asdf`. Users of the `gitlab-developme
are also affected by this problem.
Build images are not affected because they include the patch set addressing this bug.
+
+## Deprecations are not caught in DeprecationToolkit if the method is stubbed
+
+We rely on `deprecation_toolkit` to fail fast when using functionality that is deprecated in Ruby 2 and removed in Ruby 3.
+A common issue caught during the transition from Ruby 2 to Ruby 3 relates to
+the [separation of positional and keyword arguments in Ruby 3.0](https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/).
+
+Unfortunately, if the author has stubbed such methods in tests, deprecations would not be caught.
+We run automated detection for this warning in tests via `deprecation_toolkit`,
+but it relies on the fact that `Kernel#warn` emits a warning, so stubbing out this call will effectively remove the call to warn, which means `deprecation_toolkit` will never see the deprecation warnings.
+Stubbing out the implementation removes that warning, and we never pick it up, so the build is green.
+
+Please refer to [issue 364099](https://gitlab.com/gitlab-org/gitlab/-/issues/364099) for more context.
+
+## Testing in `irb` and `rails console`
+
+Another pitfall is that testing in `irb`/`rails c` silences the deprecation warning,
+since `irb` in Ruby 2.7.x has a [bug](https://bugs.ruby-lang.org/issues/17377) that prevents deprecation warnings from showing.
+
+When writing code and performing code reviews, pay extra attention to method calls of the form `f({k: v})`.
+This is valid in Ruby 2 when `f` takes either a `Hash` or keyword arguments, but Ruby 3 only considers this valid if `f` takes a `Hash`.
+For Ruby 3 compliance, this should be changed to one of the following invocations if `f` takes keyword arguments:
+
+- `f(**{k: v})`
+- `f(k: v)`
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index cba63b3c6c7..865cac3691e 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
-# Check a user's access to perform a git action. All public methods in this
-# class return an instance of `GitlabAccessStatus`
+# Checks a user's access to perform a git action.
+# All public methods in this class return an instance of `GitlabAccessStatus`
+
module Gitlab
class GitAccess
include Gitlab::Utils::StrongMemoize
@@ -99,7 +100,7 @@ module Gitlab
@logger ||= Checks::TimedLogger.new(timeout: INTERNAL_TIMEOUT, header: LOG_HEADER)
end
- def guest_can_download_code?
+ def guest_can_download?
Guest.can?(download_ability, container)
end
@@ -110,7 +111,7 @@ module Gitlab
(project? && project&.repository_access_level != ::Featurable::DISABLED)
end
- def user_can_download_code?
+ def user_can_download?
authentication_abilities.include?(:download_code) &&
user_access.can_do_action?(download_ability)
end
@@ -125,10 +126,6 @@ module Gitlab
raise NotImplementedError
end
- def build_can_download_code?
- authentication_abilities.include?(:build_download_code) && user_access.can_do_action?(:build_download_code)
- end
-
def request_from_ci_build?
return false unless protocol == 'http'
@@ -141,6 +138,31 @@ module Gitlab
private
+ # when accessing via the CI_JOB_TOKEN
+ def build_can_download_code?
+ authentication_abilities.include?(:build_download_code) && user_access.can_do_action?(:build_download_code)
+ end
+
+ def build_can_download?
+ build_can_download_code?
+ end
+
+ def deploy_token_can_download?
+ deploy_token?
+ end
+
+ # When overriding this method, be careful using super
+ # as deploy_token_can_download? and build_can_download?
+ # do not consider the download_ability in the inheriting class
+ # for deploy tokens and builds
+ def can_download?
+ deploy_key_can_download_code? ||
+ deploy_token_can_download? ||
+ build_can_download? ||
+ user_can_download? ||
+ guest_can_download?
+ end
+
def check_container!
# Strict nil check, to avoid any surprises with Object#present?
# which can delegate to #empty?
@@ -273,15 +295,9 @@ module Gitlab
end
def check_download_access!
- passed = deploy_key_can_download_code? ||
- deploy_token? ||
- user_can_download_code? ||
- build_can_download_code? ||
- guest_can_download_code?
-
- unless passed
- raise ForbiddenError, download_forbidden_message
- end
+ return if can_download?
+
+ raise ForbiddenError, download_forbidden_message
end
def download_forbidden_message
diff --git a/lib/gitlab/git_access_snippet.rb b/lib/gitlab/git_access_snippet.rb
index 5ae17dbbb91..8c291dd56ba 100644
--- a/lib/gitlab/git_access_snippet.rb
+++ b/lib/gitlab/git_access_snippet.rb
@@ -90,13 +90,14 @@ module Gitlab
super
end
- override :check_download_access!
- def check_download_access!
- passed = guest_can_download_code? || user_can_download_code?
+ override :can_download?
+ def can_download?
+ guest_can_download? || user_can_download?
+ end
- unless passed
- raise ForbiddenError, error_message(:read_snippet)
- end
+ override :download_forbidden_message
+ def download_forbidden_message
+ error_message(:read_snippet)
end
override :check_change_access!
diff --git a/lib/gitlab/git_access_wiki.rb b/lib/gitlab/git_access_wiki.rb
index fdd7e8a8c4a..db0f95cdb70 100644
--- a/lib/gitlab/git_access_wiki.rb
+++ b/lib/gitlab/git_access_wiki.rb
@@ -27,12 +27,16 @@ module Gitlab
:create_wiki
end
- override :check_download_access!
- def check_download_access!
- super
+ private
+
+ override :build_can_download?
+ def build_can_download?
+ super && user_access.can_do_action?(download_ability)
+ end
- raise ForbiddenError, download_forbidden_message if build_cannot_download?
- raise ForbiddenError, download_forbidden_message if deploy_token_cannot_download?
+ override :deploy_token_can_download?
+ def deploy_token_can_download?
+ super && deploy_token.can?(download_ability, container)
end
override :check_change_access!
@@ -53,17 +57,6 @@ module Gitlab
def not_found_message
error_message(:not_found)
end
-
- private
-
- # when accessing via the CI_JOB_TOKEN
- def build_cannot_download?
- build_can_download_code? && !user_access.can_do_action?(download_ability)
- end
-
- def deploy_token_cannot_download?
- deploy_token && !deploy_token.can?(download_ability, container)
- end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 318c563debc..e57a4e00ea3 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -20464,6 +20464,33 @@ msgstr ""
msgid "Integration Settings"
msgstr ""
+msgid "IntegrationEvents|A comment is added on a confidential issue"
+msgstr ""
+
+msgid "IntegrationEvents|A comment is added on an issue"
+msgstr ""
+
+msgid "IntegrationEvents|A confidential issue is created, updated, or closed"
+msgstr ""
+
+msgid "IntegrationEvents|A merge request is created, updated, or merged"
+msgstr ""
+
+msgid "IntegrationEvents|A pipeline status changes"
+msgstr ""
+
+msgid "IntegrationEvents|A push is made to the repository"
+msgstr ""
+
+msgid "IntegrationEvents|A tag is pushed to the repository"
+msgstr ""
+
+msgid "IntegrationEvents|A wiki page is created or updated"
+msgstr ""
+
+msgid "IntegrationEvents|An issue is created, updated, or closed"
+msgstr ""
+
msgid "Integrations"
msgstr ""
@@ -42780,9 +42807,6 @@ msgstr ""
msgid "What is squashing?"
msgstr ""
-msgid "What is your job title? (optional)"
-msgstr ""
-
msgid "What templates can I create?"
msgstr ""
diff --git a/package.json b/package.json
index 87651584a25..d25cf0ef8db 100644
--- a/package.json
+++ b/package.json
@@ -57,7 +57,7 @@
"@gitlab/at.js": "1.5.7",
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/svgs": "2.14.0",
- "@gitlab/ui": "40.6.6",
+ "@gitlab/ui": "40.7.1",
"@gitlab/visual-review-tools": "1.7.3",
"@rails/actioncable": "6.1.4-7",
"@rails/ujs": "6.1.4-7",
diff --git a/spec/features/projects/pages/user_adds_domain_spec.rb b/spec/features/projects/pages/user_adds_domain_spec.rb
index 71bf1c24655..dfa6571545c 100644
--- a/spec/features/projects/pages/user_adds_domain_spec.rb
+++ b/spec/features/projects/pages/user_adds_domain_spec.rb
@@ -95,7 +95,7 @@ RSpec.describe 'User adds pages domain', :js do
fill_in 'Domain', with: 'my.test.domain.com'
- find('.js-auto-ssl-toggle-container .js-project-feature-toggle').click
+ find('.js-auto-ssl-toggle-container .js-project-feature-toggle button').click
fill_in 'Certificate (PEM)', with: certificate_pem
fill_in 'Key (PEM)', with: certificate_key
diff --git a/spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb b/spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb
index bdf280f4fe4..156545ba78f 100644
--- a/spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb
+++ b/spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb
@@ -50,7 +50,7 @@ RSpec.describe "Pages with Let's Encrypt", :https_pages_enabled do
expect(page).to have_selector '.card-header', text: 'Certificate'
expect(page).to have_text domain.subject
- find('.js-auto-ssl-toggle-container .js-project-feature-toggle').click
+ find('.js-auto-ssl-toggle-container .js-project-feature-toggle button').click
expect(find("#pages_domain_auto_ssl_enabled", visible: false).value).to eq 'true'
expect(page).not_to have_selector '.card-header', text: 'Certificate'
@@ -74,7 +74,7 @@ RSpec.describe "Pages with Let's Encrypt", :https_pages_enabled do
expect(page).not_to have_field 'Certificate (PEM)', type: 'textarea'
expect(page).not_to have_field 'Key (PEM)', type: 'textarea'
- find('.js-auto-ssl-toggle-container .js-project-feature-toggle').click
+ find('.js-auto-ssl-toggle-container .js-project-feature-toggle button').click
expect(find("#pages_domain_auto_ssl_enabled", visible: false).value).to eq 'false'
expect(page).to have_field 'Certificate (PEM)', type: 'textarea'
diff --git a/spec/frontend/frequent_items/components/frequent_items_list_item_spec.js b/spec/frontend/frequent_items/components/frequent_items_list_item_spec.js
index 8220ea16342..eef5dc86c1a 100644
--- a/spec/frontend/frequent_items/components/frequent_items_list_item_spec.js
+++ b/spec/frontend/frequent_items/components/frequent_items_list_item_spec.js
@@ -117,7 +117,7 @@ describe('FrequentItemsListItemComponent', () => {
link.vm.$emit('click');
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_link', {
- label: 'projects_dropdown_frequent_items_list_item',
+ label: 'projects_dropdown_frequent_items_list_item_git_lab_community_edition',
});
});
});
diff --git a/spec/frontend/integrations/edit/components/sections/trigger_spec.js b/spec/frontend/integrations/edit/components/sections/trigger_spec.js
new file mode 100644
index 00000000000..883f5c7bf79
--- /dev/null
+++ b/spec/frontend/integrations/edit/components/sections/trigger_spec.js
@@ -0,0 +1,38 @@
+import { shallowMount } from '@vue/test-utils';
+
+import IntegrationSectionTrigger from '~/integrations/edit/components/sections/trigger.vue';
+import TriggerField from '~/integrations/edit/components/trigger_field.vue';
+import { createStore } from '~/integrations/edit/store';
+
+import { mockIntegrationProps } from '../../mock_data';
+
+describe('IntegrationSectionTrigger', () => {
+ let wrapper;
+
+ const createComponent = () => {
+ const store = createStore({
+ customState: { ...mockIntegrationProps },
+ });
+ wrapper = shallowMount(IntegrationSectionTrigger, {
+ store,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findAllTriggerFields = () => wrapper.findAllComponents(TriggerField);
+
+ describe('template', () => {
+ it('renders correct number of TriggerField components', () => {
+ createComponent();
+
+ const fields = findAllTriggerFields();
+ expect(fields.length).toBe(mockIntegrationProps.triggerEvents.length);
+ fields.wrappers.forEach((field, index) => {
+ expect(field.props('event')).toBe(mockIntegrationProps.triggerEvents[index]);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/integrations/edit/components/trigger_field_spec.js b/spec/frontend/integrations/edit/components/trigger_field_spec.js
new file mode 100644
index 00000000000..6a68337813e
--- /dev/null
+++ b/spec/frontend/integrations/edit/components/trigger_field_spec.js
@@ -0,0 +1,71 @@
+import { nextTick } from 'vue';
+import { shallowMount } from '@vue/test-utils';
+import { GlFormCheckbox } from '@gitlab/ui';
+
+import TriggerField from '~/integrations/edit/components/trigger_field.vue';
+import { integrationTriggerEventTitles } from '~/integrations/constants';
+
+describe('TriggerField', () => {
+ let wrapper;
+
+ const defaultProps = {
+ event: { name: 'push_events' },
+ };
+
+ const createComponent = ({ props = {}, isInheriting = false } = {}) => {
+ wrapper = shallowMount(TriggerField, {
+ propsData: { ...defaultProps, ...props },
+ computed: {
+ isInheriting: () => isInheriting,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findGlFormCheckbox = () => wrapper.findComponent(GlFormCheckbox);
+ const findHiddenInput = () => wrapper.find('input[type="hidden"]');
+
+ describe('template', () => {
+ it('renders enabled GlFormCheckbox', () => {
+ createComponent();
+
+ expect(findGlFormCheckbox().attributes('disabled')).toBeUndefined();
+ });
+
+ it('when isInheriting is true, renders disabled GlFormCheckbox', () => {
+ createComponent({ isInheriting: true });
+
+ expect(findGlFormCheckbox().attributes('disabled')).toBe('true');
+ });
+
+ it('renders correct title', () => {
+ createComponent();
+
+ expect(findGlFormCheckbox().text()).toMatchInterpolatedText(
+ integrationTriggerEventTitles[defaultProps.event.name],
+ );
+ });
+
+ it('sets default value for hidden input', () => {
+ createComponent();
+
+ expect(findHiddenInput().attributes('value')).toBe('false');
+ });
+
+ it('toggles value of hidden input on checkbox input', async () => {
+ createComponent({
+ props: { event: { name: 'push_events', value: true } },
+ });
+ await nextTick;
+
+ expect(findHiddenInput().attributes('value')).toBe('true');
+
+ await findGlFormCheckbox().vm.$emit('input', false);
+
+ expect(findHiddenInput().attributes('value')).toBe('false');
+ });
+ });
+});
diff --git a/spec/frontend/integrations/edit/mock_data.js b/spec/frontend/integrations/edit/mock_data.js
index ac0c7d244e3..c276d2e7364 100644
--- a/spec/frontend/integrations/edit/mock_data.js
+++ b/spec/frontend/integrations/edit/mock_data.js
@@ -9,7 +9,10 @@ export const mockIntegrationProps = {
initialEnableComments: false,
},
jiraIssuesProps: {},
- triggerEvents: [],
+ triggerEvents: [
+ { name: 'push_events', title: 'Push', value: true },
+ { name: 'issues_events', title: 'Issue', value: true },
+ ],
sections: [],
fields: [],
type: '',
diff --git a/spec/helpers/nav/top_nav_helper_spec.rb b/spec/helpers/nav/top_nav_helper_spec.rb
index e4422dde407..9d43e057521 100644
--- a/spec/helpers/nav/top_nav_helper_spec.rb
+++ b/spec/helpers/nav/top_nav_helper_spec.rb
@@ -10,6 +10,7 @@ RSpec.describe Nav::TopNavHelper do
let(:current_user) { nil }
before do
+ stub_application_setting(snowplow_enabled: true)
allow(helper).to receive(:current_user) { current_user }
end
@@ -50,49 +51,40 @@ RSpec.describe Nav::TopNavHelper do
context 'when current_user is nil (anonymous)' do
it 'has expected :primary' do
expected_primary = [
- ::Gitlab::Nav::TopNavMenuItem.build(
- href: '/explore',
- icon: 'project',
- id: 'project',
- title: 'Projects'
- ),
- ::Gitlab::Nav::TopNavMenuItem.build(
- href: '/explore/groups',
- icon: 'group',
- id: 'groups',
- title: 'Groups'
- ),
- ::Gitlab::Nav::TopNavMenuItem.build(
- href: '/explore/snippets',
- icon: 'snippet',
- id: 'snippets',
- title: 'Snippets'
- )
- ]
+ { href: '/explore', icon: 'project', id: 'project', title: 'Projects' },
+ { href: '/explore/groups', icon: 'group', id: 'groups', title: 'Groups' },
+ { href: '/explore/snippets', icon: 'snippet', id: 'snippets', title: 'Snippets' }
+ ].map do |item|
+ ::Gitlab::Nav::TopNavMenuItem.build(**item)
+ end
+
expect(subject[:primary]).to eq(expected_primary)
end
it 'has expected :shortcuts' do
expected_shortcuts = [
- ::Gitlab::Nav::TopNavMenuItem.build(
+ {
href: '/explore',
id: 'project-shortcut',
title: 'Projects',
css_class: 'dashboard-shortcuts-projects'
- ),
- ::Gitlab::Nav::TopNavMenuItem.build(
+ },
+ {
href: '/explore/groups',
id: 'groups-shortcut',
title: 'Groups',
css_class: 'dashboard-shortcuts-groups'
- ),
- ::Gitlab::Nav::TopNavMenuItem.build(
+ },
+ {
href: '/explore/snippets',
id: 'snippets-shortcut',
title: 'Snippets',
css_class: 'dashboard-shortcuts-snippets'
- )
- ]
+ }
+ ].map do |item|
+ ::Gitlab::Nav::TopNavMenuItem.build(**item)
+ end
+
expect(subject[:shortcuts]).to eq(expected_shortcuts)
end
@@ -171,21 +163,41 @@ RSpec.describe Nav::TopNavHelper do
it 'has expected :linksPrimary' do
expected_links_primary = [
::Gitlab::Nav::TopNavMenuItem.build(
+ data: {
+ qa_selector: 'menu_item_link',
+ qa_title: 'Your projects',
+ **menu_data_tracking_attrs('your_projects')
+ },
href: '/dashboard/projects',
id: 'your',
title: 'Your projects'
),
::Gitlab::Nav::TopNavMenuItem.build(
+ data: {
+ qa_selector: 'menu_item_link',
+ qa_title: 'Starred projects',
+ **menu_data_tracking_attrs('starred_projects')
+ },
href: '/dashboard/projects/starred',
id: 'starred',
title: 'Starred projects'
),
::Gitlab::Nav::TopNavMenuItem.build(
+ data: {
+ qa_selector: 'menu_item_link',
+ qa_title: 'Explore projects',
+ **menu_data_tracking_attrs('explore_projects')
+ },
href: '/explore',
id: 'explore',
title: 'Explore projects'
),
::Gitlab::Nav::TopNavMenuItem.build(
+ data: {
+ qa_selector: 'menu_item_link',
+ qa_title: 'Explore topics',
+ **menu_data_tracking_attrs('explore_topics')
+ },
href: '/explore/projects/topics',
id: 'topics',
title: 'Explore topics'
@@ -197,6 +209,11 @@ RSpec.describe Nav::TopNavHelper do
it 'has expected :linksSecondary' do
expected_links_secondary = [
::Gitlab::Nav::TopNavMenuItem.build(
+ data: {
+ qa_selector: 'menu_item_link',
+ qa_title: 'Create new project',
+ **menu_data_tracking_attrs('create_new_project')
+ },
href: '/projects/new',
id: 'create',
title: 'Create new project'
@@ -282,11 +299,21 @@ RSpec.describe Nav::TopNavHelper do
it 'has expected :linksPrimary' do
expected_links_primary = [
::Gitlab::Nav::TopNavMenuItem.build(
+ data: {
+ qa_selector: 'menu_item_link',
+ qa_title: 'Your groups',
+ **menu_data_tracking_attrs('your_groups')
+ },
href: '/dashboard/groups',
id: 'your',
title: 'Your groups'
),
::Gitlab::Nav::TopNavMenuItem.build(
+ data: {
+ qa_selector: 'menu_item_link',
+ qa_title: 'Explore groups',
+ **menu_data_tracking_attrs('explore_groups')
+ },
href: '/explore/groups',
id: 'explore',
title: 'Explore groups'
@@ -298,6 +325,11 @@ RSpec.describe Nav::TopNavHelper do
it 'has expected :linksSecondary' do
expected_links_secondary = [
::Gitlab::Nav::TopNavMenuItem.build(
+ data: {
+ qa_selector: 'menu_item_link',
+ qa_title: 'Create group',
+ **menu_data_tracking_attrs('create_group')
+ },
href: '/groups/new',
id: 'create',
title: 'Create group'
@@ -356,7 +388,8 @@ RSpec.describe Nav::TopNavHelper do
it 'has expected :primary' do
expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
data: {
- qa_selector: 'milestones_link'
+ qa_selector: 'milestones_link',
+ **menu_data_tracking_attrs('milestones')
},
href: '/dashboard/milestones',
icon: 'clock',
@@ -383,7 +416,8 @@ RSpec.describe Nav::TopNavHelper do
it 'has expected :primary' do
expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
data: {
- qa_selector: 'snippets_link'
+ qa_selector: 'snippets_link',
+ **menu_data_tracking_attrs('snippets')
},
href: '/dashboard/snippets',
icon: 'snippet',
@@ -410,7 +444,8 @@ RSpec.describe Nav::TopNavHelper do
it 'has expected :primary' do
expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
data: {
- qa_selector: 'activity_link'
+ qa_selector: 'activity_link',
+ **menu_data_tracking_attrs('activity')
},
href: '/dashboard/activity',
icon: 'history',
@@ -439,6 +474,11 @@ RSpec.describe Nav::TopNavHelper do
it 'has admin as first :secondary item' do
expected_admin_item = ::Gitlab::Nav::TopNavMenuItem.build(
+ data: {
+ qa_selector: 'menu_item_link',
+ qa_title: 'Admin',
+ **menu_data_tracking_attrs('admin')
+ },
id: 'admin',
title: 'Admin',
icon: 'admin',
@@ -458,7 +498,7 @@ RSpec.describe Nav::TopNavHelper do
title: 'Leave Admin Mode',
icon: 'lock-open',
href: '/admin/session/destroy',
- data: { method: 'post' }
+ data: { method: 'post', **menu_data_tracking_attrs('leave_admin_mode') }
)
expect(subject[:secondary].last).to eq(expected_leave_admin_mode_item)
end
@@ -469,6 +509,11 @@ RSpec.describe Nav::TopNavHelper do
it 'has enter_admin_mode as last :secondary item' do
expected_enter_admin_mode_item = ::Gitlab::Nav::TopNavMenuItem.build(
+ data: {
+ qa_selector: 'menu_item_link',
+ qa_title: 'Enter Admin Mode',
+ **menu_data_tracking_attrs('enter_admin_mode')
+ },
id: 'enter_admin_mode',
title: 'Enter Admin Mode',
icon: 'lock',
@@ -533,4 +578,12 @@ RSpec.describe Nav::TopNavHelper do
end
end
end
+
+ def menu_data_tracking_attrs(label)
+ {
+ track_label: "menu_#{label}",
+ track_action: 'click_dropdown',
+ track_property: 'navigation'
+ }
+ end
end
diff --git a/spec/lib/gitlab/git_access_snippet_spec.rb b/spec/lib/gitlab/git_access_snippet_spec.rb
index b6a61de87a6..a7036a4f20a 100644
--- a/spec/lib/gitlab/git_access_snippet_spec.rb
+++ b/spec/lib/gitlab/git_access_snippet_spec.rb
@@ -121,13 +121,19 @@ RSpec.describe Gitlab::GitAccessSnippet do
if Ability.allowed?(user, :update_snippet, snippet)
expect { push_access_check }.not_to raise_error
else
- expect { push_access_check }.to raise_error(described_class::ForbiddenError)
+ expect { push_access_check }.to raise_error(
+ described_class::ForbiddenError,
+ described_class::ERROR_MESSAGES[:update_snippet]
+ )
end
if Ability.allowed?(user, :read_snippet, snippet)
expect { pull_access_check }.not_to raise_error
else
- expect { pull_access_check }.to raise_error(described_class::ForbiddenError)
+ expect { pull_access_check }.to raise_error(
+ described_class::ForbiddenError,
+ described_class::ERROR_MESSAGES[:read_snippet]
+ )
end
end
end
diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb
index de3e674c3a7..ae67483cce0 100644
--- a/spec/lib/gitlab/git_access_wiki_spec.rb
+++ b/spec/lib/gitlab/git_access_wiki_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe Gitlab::GitAccessWiki do
redirected_path: redirected_path)
end
- RSpec.shared_examples 'wiki access by level' do
+ RSpec.shared_examples 'download wiki access by level' do
where(:project_visibility, :project_member?, :wiki_access_level, :wiki_repo?, :expected_behavior) do
[
# Private project - is a project member
@@ -103,7 +103,7 @@ RSpec.describe Gitlab::GitAccessWiki do
subject { access.check('git-upload-pack', Gitlab::GitAccess::ANY) }
context 'when actor is a user' do
- it_behaves_like 'wiki access by level'
+ it_behaves_like 'download wiki access by level'
end
context 'when the actor is a deploy token' do
@@ -116,17 +116,23 @@ RSpec.describe Gitlab::GitAccessWiki do
subject { access.check('git-upload-pack', changes) }
- context 'when the wiki is enabled' do
+ context 'when the wiki feature is enabled' do
let(:wiki_access_level) { ProjectFeature::ENABLED }
it { expect { subject }.not_to raise_error }
end
- context 'when the wiki is disabled' do
+ context 'when the wiki feature is disabled' do
let(:wiki_access_level) { ProjectFeature::DISABLED }
it { expect { subject }.to raise_wiki_forbidden }
end
+
+ context 'when the wiki feature is private' do
+ let(:wiki_access_level) { ProjectFeature::PRIVATE }
+
+ it { expect { subject }.to raise_wiki_forbidden }
+ end
end
describe 'when actor is a user provided by build via CI_JOB_TOKEN' do
@@ -140,7 +146,7 @@ RSpec.describe Gitlab::GitAccessWiki do
subject { access.check('git-upload-pack', changes) }
- it_behaves_like 'wiki access by level'
+ it_behaves_like 'download wiki access by level'
end
end
diff --git a/spec/requests/api/markdown_snapshot_spec.rb b/spec/requests/api/markdown_snapshot_spec.rb
new file mode 100644
index 00000000000..fdb55a62802
--- /dev/null
+++ b/spec/requests/api/markdown_snapshot_spec.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+# See https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#markdown-snapshot-testing
+# for documentation on this spec.
+RSpec.describe API::Markdown, 'Snapshot' do
+ glfm_example_snapshots_dir = File.expand_path('../../fixtures/glfm/example_snapshots', __dir__)
+ include_context 'API::Markdown Snapshot shared context', glfm_example_snapshots_dir
+end
diff --git a/spec/support/shared_contexts/markdown_snapshot_shared_examples.rb b/spec/support/shared_contexts/markdown_snapshot_shared_examples.rb
new file mode 100644
index 00000000000..531a176d76b
--- /dev/null
+++ b/spec/support/shared_contexts/markdown_snapshot_shared_examples.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+# See https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#markdown-snapshot-testing
+# for documentation on this spec.
+RSpec.shared_context 'API::Markdown Snapshot shared context' do |glfm_example_snapshots_dir|
+ include ApiHelpers
+
+ markdown_examples, html_examples = %w[markdown.yml html.yml].map do |file_name|
+ yaml = File.read("#{glfm_example_snapshots_dir}/#{file_name}")
+ YAML.safe_load(yaml, symbolize_names: true, aliases: true)
+ end
+
+ if focused_markdown_examples_string = ENV['FOCUSED_MARKDOWN_EXAMPLES']
+ focused_markdown_examples = focused_markdown_examples_string.split(',').map(&:strip).map(&:to_sym)
+ markdown_examples.select! { |example_name| focused_markdown_examples.include?(example_name) }
+ end
+
+ markdown_examples.each do |name, markdown|
+ context "for #{name}" do
+ let(:html) { html_examples.fetch(name).fetch(:static) }
+
+ it "verifies conversion of GLFM to HTML", :unlimited_max_formatted_output_length do
+ api_url = api "/markdown"
+
+ post api_url, params: { text: markdown, gfm: true }
+ expect(response).to be_successful
+ response_body = Gitlab::Json.parse(response.body)
+ # Some requests have the HTML in the `html` key, others in the `body` key.
+ response_html = response_body['body'] ? response_body.fetch('body') : response_body.fetch('html')
+
+ expect(response_html).to eq(html)
+ end
+ end
+ end
+end
diff --git a/spec/workers/database/ci_namespace_mirrors_consistency_check_worker_spec.rb b/spec/workers/database/ci_namespace_mirrors_consistency_check_worker_spec.rb
index e5024c568cb..1c083d1d8a3 100644
--- a/spec/workers/database/ci_namespace_mirrors_consistency_check_worker_spec.rb
+++ b/spec/workers/database/ci_namespace_mirrors_consistency_check_worker_spec.rb
@@ -6,29 +6,11 @@ RSpec.describe Database::CiNamespaceMirrorsConsistencyCheckWorker do
let(:worker) { described_class.new }
describe '#perform' do
- context 'feature flag is disabled' do
- before do
- stub_feature_flags(ci_namespace_mirrors_consistency_check: false)
- end
-
- it 'does not perform the consistency check on namespaces' do
- expect(Database::ConsistencyCheckService).not_to receive(:new)
- expect(worker).not_to receive(:log_extra_metadata_on_done)
- worker.perform
- end
- end
-
- context 'feature flag is enabled' do
- before do
- stub_feature_flags(ci_namespace_mirrors_consistency_check: true)
- end
-
- it 'executes the consistency check on namespaces' do
- expect(Database::ConsistencyCheckService).to receive(:new).and_call_original
- expected_result = { batches: 0, matches: 0, mismatches: 0, mismatches_details: [] }
- expect(worker).to receive(:log_extra_metadata_on_done).with(:results, expected_result)
- worker.perform
- end
+ it 'executes the consistency check on namespaces' do
+ expect(Database::ConsistencyCheckService).to receive(:new).and_call_original
+ expected_result = { batches: 0, matches: 0, mismatches: 0, mismatches_details: [] }
+ expect(worker).to receive(:log_extra_metadata_on_done).with(:results, expected_result)
+ worker.perform
end
context 'logs should contain the detailed mismatches' do
@@ -37,7 +19,6 @@ RSpec.describe Database::CiNamespaceMirrorsConsistencyCheckWorker do
before do
redis_shared_state_cleanup!
- stub_feature_flags(ci_namespace_mirrors_consistency_check: true)
create_list(:namespace, 10) # This will also create Ci::NameSpaceMirror objects
missing_namespace.delete
diff --git a/spec/workers/database/ci_project_mirrors_consistency_check_worker_spec.rb b/spec/workers/database/ci_project_mirrors_consistency_check_worker_spec.rb
index f8e950d8917..8c839410ccd 100644
--- a/spec/workers/database/ci_project_mirrors_consistency_check_worker_spec.rb
+++ b/spec/workers/database/ci_project_mirrors_consistency_check_worker_spec.rb
@@ -6,29 +6,11 @@ RSpec.describe Database::CiProjectMirrorsConsistencyCheckWorker do
let(:worker) { described_class.new }
describe '#perform' do
- context 'feature flag is disabled' do
- before do
- stub_feature_flags(ci_project_mirrors_consistency_check: false)
- end
-
- it 'does not perform the consistency check on projects' do
- expect(Database::ConsistencyCheckService).not_to receive(:new)
- expect(worker).not_to receive(:log_extra_metadata_on_done)
- worker.perform
- end
- end
-
- context 'feature flag is enabled' do
- before do
- stub_feature_flags(ci_project_mirrors_consistency_check: true)
- end
-
- it 'executes the consistency check on projects' do
- expect(Database::ConsistencyCheckService).to receive(:new).and_call_original
- expected_result = { batches: 0, matches: 0, mismatches: 0, mismatches_details: [] }
- expect(worker).to receive(:log_extra_metadata_on_done).with(:results, expected_result)
- worker.perform
- end
+ it 'executes the consistency check on projects' do
+ expect(Database::ConsistencyCheckService).to receive(:new).and_call_original
+ expected_result = { batches: 0, matches: 0, mismatches: 0, mismatches_details: [] }
+ expect(worker).to receive(:log_extra_metadata_on_done).with(:results, expected_result)
+ worker.perform
end
context 'logs should contain the detailed mismatches' do
@@ -37,7 +19,6 @@ RSpec.describe Database::CiProjectMirrorsConsistencyCheckWorker do
before do
redis_shared_state_cleanup!
- stub_feature_flags(ci_project_mirrors_consistency_check: true)
create_list(:project, 10) # This will also create Ci::ProjectMirror objects
missing_project.delete
diff --git a/yarn.lock b/yarn.lock
index 0cff0d4993b..5e3f1f1ed0c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -968,10 +968,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-2.14.0.tgz#92b36bc98ccbed49a4dbca310862146275091cb2"
integrity sha512-U9EYmEIiTMl7R3X5DmCrw6fz7gz8c1kjvQtaF6HfJ15xDtR7trRAyCNbn3z7YGk1QJ8Cv/Ifw2/T5SxXwYd7dw==
-"@gitlab/ui@40.6.6":
- version "40.6.6"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-40.6.6.tgz#07e95edd1b0afe8cb2f42880f634657ffbcaa3cd"
- integrity sha512-hHwu63oldEtALYS3KPkU0CUVIznpoScAURzCKscw2/wNmXc+siVOA4iGPJ012h5Jza7itTeQg0UFCWGjigUaYA==
+"@gitlab/ui@40.7.1":
+ version "40.7.1"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-40.7.1.tgz#e6595c5cc37d994e3f0ba780626fbf4e8174df2a"
+ integrity sha512-u100mDpdI7RfNVcAYi8n0RRH2FfIiYuMVgt5jPrQ7AAL+QrwLAkqfBZtkT9pSMpycBuuQsxSMHJK5FlnXum46g==
dependencies:
"@popperjs/core" "^2.11.2"
bootstrap-vue "2.20.1"