Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
f5f6cb45c7
commit
27484d1465
38 changed files with 562 additions and 198 deletions
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { GlButton, GlSafeHtmlDirective } from '@gitlab/ui';
|
import { GlButton, GlSafeHtmlDirective } from '@gitlab/ui';
|
||||||
|
import { snakeCase } from 'lodash';
|
||||||
import highlight from '~/lib/utils/highlight';
|
import highlight from '~/lib/utils/highlight';
|
||||||
import { truncateNamespace } from '~/lib/utils/text_utility';
|
import { truncateNamespace } from '~/lib/utils/text_utility';
|
||||||
import { mapVuexModuleState } from '~/lib/utils/vuex_module_mappers';
|
import { mapVuexModuleState } from '~/lib/utils/vuex_module_mappers';
|
||||||
|
@ -56,6 +57,9 @@ export default {
|
||||||
highlightedItemName() {
|
highlightedItemName() {
|
||||||
return highlight(this.itemName, this.matcher);
|
return highlight(this.itemName, this.matcher);
|
||||||
},
|
},
|
||||||
|
itemTrackingLabel() {
|
||||||
|
return `${this.dropdownType}_dropdown_frequent_items_list_item_${snakeCase(this.itemName)}`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -66,7 +70,7 @@ export default {
|
||||||
category="tertiary"
|
category="tertiary"
|
||||||
:href="webUrl"
|
:href="webUrl"
|
||||||
class="gl-text-left gl-justify-content-start!"
|
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
|
<project-avatar
|
||||||
class="gl-float-left gl-mr-3"
|
class="gl-float-left gl-mr-3"
|
||||||
|
|
|
@ -30,12 +30,46 @@ export const integrationFormSections = {
|
||||||
CONNECTION: 'connection',
|
CONNECTION: 'connection',
|
||||||
JIRA_TRIGGER: 'jira_trigger',
|
JIRA_TRIGGER: 'jira_trigger',
|
||||||
JIRA_ISSUES: 'jira_issues',
|
JIRA_ISSUES: 'jira_issues',
|
||||||
|
TRIGGER: 'trigger',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const integrationFormSectionComponents = {
|
export const integrationFormSectionComponents = {
|
||||||
[integrationFormSections.CONNECTION]: 'IntegrationSectionConnection',
|
[integrationFormSections.CONNECTION]: 'IntegrationSectionConnection',
|
||||||
[integrationFormSections.JIRA_TRIGGER]: 'IntegrationSectionJiraTrigger',
|
[integrationFormSections.JIRA_TRIGGER]: 'IntegrationSectionJiraTrigger',
|
||||||
[integrationFormSections.JIRA_ISSUES]: 'IntegrationSectionJiraIssues',
|
[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 = {
|
export const billingPlans = {
|
||||||
|
|
|
@ -49,6 +49,10 @@ export default {
|
||||||
import(
|
import(
|
||||||
/* webpackChunkName: 'integrationSectionJiraTrigger' */ '~/integrations/edit/components/sections/jira_trigger.vue'
|
/* webpackChunkName: 'integrationSectionJiraTrigger' */ '~/integrations/edit/components/sections/jira_trigger.vue'
|
||||||
),
|
),
|
||||||
|
IntegrationSectionTrigger: () =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: 'integrationSectionTrigger' */ '~/integrations/edit/components/sections/trigger.vue'
|
||||||
|
),
|
||||||
GlBadge,
|
GlBadge,
|
||||||
GlButton,
|
GlButton,
|
||||||
GlForm,
|
GlForm,
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -83,7 +83,7 @@ function parseDatasetToProps(data) {
|
||||||
learnMorePath,
|
learnMorePath,
|
||||||
aboutPricingUrl,
|
aboutPricingUrl,
|
||||||
triggerEvents: JSON.parse(triggerEvents),
|
triggerEvents: JSON.parse(triggerEvents),
|
||||||
sections: JSON.parse(sections, { deep: true }),
|
sections: JSON.parse(sections),
|
||||||
fields: convertObjectPropsToCamelCase(JSON.parse(fields), { deep: true }),
|
fields: convertObjectPropsToCamelCase(JSON.parse(fields), { deep: true }),
|
||||||
inheritFromId: parseInt(inheritFromId, 10),
|
inheritFromId: parseInt(inheritFromId, 10),
|
||||||
integrationLevel,
|
integrationLevel,
|
||||||
|
|
|
@ -45,7 +45,7 @@ module Registrations
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_params
|
def update_params
|
||||||
params.require(:user).permit(:role, :other_role, :setup_for_company)
|
params.require(:user).permit(:role, :setup_for_company)
|
||||||
end
|
end
|
||||||
|
|
||||||
def requires_confirmation?(user)
|
def requires_confirmation?(user)
|
||||||
|
|
|
@ -127,7 +127,7 @@ module Nav
|
||||||
href: dashboard_milestones_path,
|
href: dashboard_milestones_path,
|
||||||
active: active_nav_link?(controller: 'dashboard/milestones'),
|
active: active_nav_link?(controller: 'dashboard/milestones'),
|
||||||
icon: 'clock',
|
icon: 'clock',
|
||||||
data: { qa_selector: 'milestones_link' },
|
data: { qa_selector: 'milestones_link', **menu_data_tracking_attrs('milestones') },
|
||||||
shortcut_class: 'dashboard-shortcuts-milestones'
|
shortcut_class: 'dashboard-shortcuts-milestones'
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
@ -135,7 +135,7 @@ module Nav
|
||||||
if dashboard_nav_link?(:snippets)
|
if dashboard_nav_link?(:snippets)
|
||||||
builder.add_primary_menu_item_with_shortcut(
|
builder.add_primary_menu_item_with_shortcut(
|
||||||
active: active_nav_link?(controller: 'dashboard/snippets'),
|
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,
|
href: dashboard_snippets_path,
|
||||||
**snippets_menu_item_attrs
|
**snippets_menu_item_attrs
|
||||||
)
|
)
|
||||||
|
@ -148,7 +148,7 @@ module Nav
|
||||||
href: activity_dashboard_path,
|
href: activity_dashboard_path,
|
||||||
active: active_nav_link?(path: 'dashboard#activity'),
|
active: active_nav_link?(path: 'dashboard#activity'),
|
||||||
icon: 'history',
|
icon: 'history',
|
||||||
data: { qa_selector: 'activity_link' },
|
data: { qa_selector: 'activity_link', **menu_data_tracking_attrs('activity') },
|
||||||
shortcut_class: 'dashboard-shortcuts-activity'
|
shortcut_class: 'dashboard-shortcuts-activity'
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
@ -158,13 +158,16 @@ module Nav
|
||||||
# we should be good.
|
# we should be good.
|
||||||
# rubocop: disable Cop/UserAdmin
|
# rubocop: disable Cop/UserAdmin
|
||||||
if current_user&.admin?
|
if current_user&.admin?
|
||||||
|
title = _('Admin')
|
||||||
|
|
||||||
builder.add_secondary_menu_item(
|
builder.add_secondary_menu_item(
|
||||||
id: 'admin',
|
id: 'admin',
|
||||||
title: _('Admin'),
|
title: title,
|
||||||
active: active_nav_link?(controller: 'admin/dashboard'),
|
active: active_nav_link?(controller: 'admin/dashboard'),
|
||||||
icon: 'admin',
|
icon: 'admin',
|
||||||
css_class: 'qa-admin-area-link',
|
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
|
end
|
||||||
|
|
||||||
|
@ -176,15 +179,18 @@ module Nav
|
||||||
active: active_nav_link?(controller: 'admin/sessions'),
|
active: active_nav_link?(controller: 'admin/sessions'),
|
||||||
icon: 'lock-open',
|
icon: 'lock-open',
|
||||||
href: destroy_admin_session_path,
|
href: destroy_admin_session_path,
|
||||||
data: { method: 'post' }
|
data: { method: 'post', **menu_data_tracking_attrs('leave_admin_mode') }
|
||||||
)
|
)
|
||||||
elsif current_user.admin?
|
elsif current_user.admin?
|
||||||
|
title = _('Enter Admin Mode')
|
||||||
|
|
||||||
builder.add_secondary_menu_item(
|
builder.add_secondary_menu_item(
|
||||||
id: 'enter_admin_mode',
|
id: 'enter_admin_mode',
|
||||||
title: _('Enter Admin Mode'),
|
title: title,
|
||||||
active: active_nav_link?(controller: 'admin/sessions'),
|
active: active_nav_link?(controller: 'admin/sessions'),
|
||||||
icon: 'lock',
|
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
|
||||||
end
|
end
|
||||||
|
@ -218,6 +224,14 @@ module Nav
|
||||||
}
|
}
|
||||||
end
|
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:)
|
def container_view_props(namespace:, current_item:, submenu:)
|
||||||
{
|
{
|
||||||
namespace: namespace,
|
namespace: namespace,
|
||||||
|
@ -260,21 +274,51 @@ module Nav
|
||||||
|
|
||||||
def projects_submenu_items(builder:)
|
def projects_submenu_items(builder:)
|
||||||
# These project links come from `app/views/layouts/nav/projects_dropdown/_show.html.haml`
|
# 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)
|
{ id: 'your', title: _('Your projects'), href: dashboard_projects_path },
|
||||||
builder.add_primary_menu_item(id: 'explore', title: _('Explore projects'), href: explore_root_path)
|
{ id: 'starred', title: _('Starred projects'), href: starred_dashboard_projects_path },
|
||||||
builder.add_primary_menu_item(id: 'topics', title: _('Explore topics'), href: topics_explore_projects_path)
|
{ id: 'explore', title: _('Explore projects'), href: explore_root_path },
|
||||||
builder.add_secondary_menu_item(id: 'create', title: _('Create new project'), href: new_project_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
|
end
|
||||||
|
|
||||||
def groups_submenu
|
def groups_submenu
|
||||||
# These group links come from `app/views/layouts/nav/groups_dropdown/_show.html.haml`
|
# These group links come from `app/views/layouts/nav/groups_dropdown/_show.html.haml`
|
||||||
builder = ::Gitlab::Nav::TopNavMenuBuilder.new
|
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?
|
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
|
end
|
||||||
|
|
||||||
builder.build
|
builder.build
|
||||||
|
|
|
@ -338,7 +338,6 @@ class User < ApplicationRecord
|
||||||
|
|
||||||
delegate :path, to: :namespace, allow_nil: true, prefix: true
|
delegate :path, to: :namespace, allow_nil: true, prefix: true
|
||||||
delegate :job_title, :job_title=, to: :user_detail, allow_nil: 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 :bio, :bio=, to: :user_detail, allow_nil: true
|
||||||
delegate :webauthn_xid, :webauthn_xid=, 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
|
delegate :pronouns, :pronouns=, to: :user_detail, allow_nil: true
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
class UserDetail < ApplicationRecord
|
class UserDetail < ApplicationRecord
|
||||||
extend ::Gitlab::Utils::Override
|
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
|
REGISTRATION_OBJECTIVE_PAIRS = { basics: 0, move_repository: 1, code_storage: 2, exploring: 3, ci: 4, other: 5, joining_team: 6 }.freeze
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
= _('Next')
|
= _('Next')
|
||||||
|
|
||||||
- if current_user
|
- 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"
|
= render "layouts/nav/top_nav"
|
||||||
- else
|
- else
|
||||||
- experiment(:logged_out_marketing_header, actor: nil) do |e|
|
- experiment(:logged_out_marketing_header, actor: nil) do |e|
|
||||||
|
|
|
@ -24,11 +24,6 @@
|
||||||
.form-group.col-sm-12
|
.form-group.col-sm-12
|
||||||
= f.label :role, _('Role'), class: 'label-bold'
|
= 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' }
|
= 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/jobs_to_be_done", f: f
|
||||||
= render_if_exists "registrations/welcome/setup_for_company", f: f
|
= render_if_exists "registrations/welcome/setup_for_company", f: f
|
||||||
= render_if_exists "registrations/welcome/joining_project"
|
= render_if_exists "registrations/welcome/joining_project"
|
||||||
|
|
|
@ -13,8 +13,6 @@ module Database
|
||||||
version 1
|
version 1
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
return if Feature.disabled?(:ci_namespace_mirrors_consistency_check)
|
|
||||||
|
|
||||||
results = ConsistencyCheckService.new(
|
results = ConsistencyCheckService.new(
|
||||||
source_model: Namespace,
|
source_model: Namespace,
|
||||||
target_model: Ci::NamespaceMirror,
|
target_model: Ci::NamespaceMirror,
|
||||||
|
|
|
@ -13,8 +13,6 @@ module Database
|
||||||
version 1
|
version 1
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
return if Feature.disabled?(:ci_project_mirrors_consistency_check)
|
|
||||||
|
|
||||||
results = ConsistencyCheckService.new(
|
results = ConsistencyCheckService.new(
|
||||||
source_model: Project,
|
source_model: Project,
|
||||||
target_model: Ci::ProjectMirror,
|
target_model: Ci::ProjectMirror,
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -138,3 +138,28 @@ installed Ruby manually or via tools like `asdf`. Users of the `gitlab-developme
|
||||||
are also affected by this problem.
|
are also affected by this problem.
|
||||||
|
|
||||||
Build images are not affected because they include the patch set addressing this bug.
|
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)`
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# Check a user's access to perform a git action. All public methods in this
|
# Checks a user's access to perform a git action.
|
||||||
# class return an instance of `GitlabAccessStatus`
|
# All public methods in this class return an instance of `GitlabAccessStatus`
|
||||||
|
|
||||||
module Gitlab
|
module Gitlab
|
||||||
class GitAccess
|
class GitAccess
|
||||||
include Gitlab::Utils::StrongMemoize
|
include Gitlab::Utils::StrongMemoize
|
||||||
|
@ -99,7 +100,7 @@ module Gitlab
|
||||||
@logger ||= Checks::TimedLogger.new(timeout: INTERNAL_TIMEOUT, header: LOG_HEADER)
|
@logger ||= Checks::TimedLogger.new(timeout: INTERNAL_TIMEOUT, header: LOG_HEADER)
|
||||||
end
|
end
|
||||||
|
|
||||||
def guest_can_download_code?
|
def guest_can_download?
|
||||||
Guest.can?(download_ability, container)
|
Guest.can?(download_ability, container)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -110,7 +111,7 @@ module Gitlab
|
||||||
(project? && project&.repository_access_level != ::Featurable::DISABLED)
|
(project? && project&.repository_access_level != ::Featurable::DISABLED)
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_can_download_code?
|
def user_can_download?
|
||||||
authentication_abilities.include?(:download_code) &&
|
authentication_abilities.include?(:download_code) &&
|
||||||
user_access.can_do_action?(download_ability)
|
user_access.can_do_action?(download_ability)
|
||||||
end
|
end
|
||||||
|
@ -125,10 +126,6 @@ module Gitlab
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
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?
|
def request_from_ci_build?
|
||||||
return false unless protocol == 'http'
|
return false unless protocol == 'http'
|
||||||
|
|
||||||
|
@ -141,6 +138,31 @@ module Gitlab
|
||||||
|
|
||||||
private
|
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!
|
def check_container!
|
||||||
# Strict nil check, to avoid any surprises with Object#present?
|
# Strict nil check, to avoid any surprises with Object#present?
|
||||||
# which can delegate to #empty?
|
# which can delegate to #empty?
|
||||||
|
@ -273,16 +295,10 @@ module Gitlab
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_download_access!
|
def check_download_access!
|
||||||
passed = deploy_key_can_download_code? ||
|
return if can_download?
|
||||||
deploy_token? ||
|
|
||||||
user_can_download_code? ||
|
|
||||||
build_can_download_code? ||
|
|
||||||
guest_can_download_code?
|
|
||||||
|
|
||||||
unless passed
|
|
||||||
raise ForbiddenError, download_forbidden_message
|
raise ForbiddenError, download_forbidden_message
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def download_forbidden_message
|
def download_forbidden_message
|
||||||
error_message(:download)
|
error_message(:download)
|
||||||
|
|
|
@ -90,13 +90,14 @@ module Gitlab
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
override :check_download_access!
|
override :can_download?
|
||||||
def check_download_access!
|
def can_download?
|
||||||
passed = guest_can_download_code? || user_can_download_code?
|
guest_can_download? || user_can_download?
|
||||||
|
|
||||||
unless passed
|
|
||||||
raise ForbiddenError, error_message(:read_snippet)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
override :download_forbidden_message
|
||||||
|
def download_forbidden_message
|
||||||
|
error_message(:read_snippet)
|
||||||
end
|
end
|
||||||
|
|
||||||
override :check_change_access!
|
override :check_change_access!
|
||||||
|
|
|
@ -27,12 +27,16 @@ module Gitlab
|
||||||
:create_wiki
|
:create_wiki
|
||||||
end
|
end
|
||||||
|
|
||||||
override :check_download_access!
|
private
|
||||||
def check_download_access!
|
|
||||||
super
|
|
||||||
|
|
||||||
raise ForbiddenError, download_forbidden_message if build_cannot_download?
|
override :build_can_download?
|
||||||
raise ForbiddenError, download_forbidden_message if deploy_token_cannot_download?
|
def build_can_download?
|
||||||
|
super && user_access.can_do_action?(download_ability)
|
||||||
|
end
|
||||||
|
|
||||||
|
override :deploy_token_can_download?
|
||||||
|
def deploy_token_can_download?
|
||||||
|
super && deploy_token.can?(download_ability, container)
|
||||||
end
|
end
|
||||||
|
|
||||||
override :check_change_access!
|
override :check_change_access!
|
||||||
|
@ -53,17 +57,6 @@ module Gitlab
|
||||||
def not_found_message
|
def not_found_message
|
||||||
error_message(:not_found)
|
error_message(:not_found)
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -20464,6 +20464,33 @@ msgstr ""
|
||||||
msgid "Integration Settings"
|
msgid "Integration Settings"
|
||||||
msgstr ""
|
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"
|
msgid "Integrations"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -42780,9 +42807,6 @@ msgstr ""
|
||||||
msgid "What is squashing?"
|
msgid "What is squashing?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "What is your job title? (optional)"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "What templates can I create?"
|
msgid "What templates can I create?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
"@gitlab/at.js": "1.5.7",
|
"@gitlab/at.js": "1.5.7",
|
||||||
"@gitlab/favicon-overlay": "2.0.0",
|
"@gitlab/favicon-overlay": "2.0.0",
|
||||||
"@gitlab/svgs": "2.14.0",
|
"@gitlab/svgs": "2.14.0",
|
||||||
"@gitlab/ui": "40.6.6",
|
"@gitlab/ui": "40.7.1",
|
||||||
"@gitlab/visual-review-tools": "1.7.3",
|
"@gitlab/visual-review-tools": "1.7.3",
|
||||||
"@rails/actioncable": "6.1.4-7",
|
"@rails/actioncable": "6.1.4-7",
|
||||||
"@rails/ujs": "6.1.4-7",
|
"@rails/ujs": "6.1.4-7",
|
||||||
|
|
|
@ -95,7 +95,7 @@ RSpec.describe 'User adds pages domain', :js do
|
||||||
|
|
||||||
fill_in 'Domain', with: 'my.test.domain.com'
|
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 'Certificate (PEM)', with: certificate_pem
|
||||||
fill_in 'Key (PEM)', with: certificate_key
|
fill_in 'Key (PEM)', with: certificate_key
|
||||||
|
|
|
@ -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_selector '.card-header', text: 'Certificate'
|
||||||
expect(page).to have_text domain.subject
|
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(find("#pages_domain_auto_ssl_enabled", visible: false).value).to eq 'true'
|
||||||
expect(page).not_to have_selector '.card-header', text: 'Certificate'
|
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 'Certificate (PEM)', type: 'textarea'
|
||||||
expect(page).not_to have_field 'Key (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(find("#pages_domain_auto_ssl_enabled", visible: false).value).to eq 'false'
|
||||||
expect(page).to have_field 'Certificate (PEM)', type: 'textarea'
|
expect(page).to have_field 'Certificate (PEM)', type: 'textarea'
|
||||||
|
|
|
@ -117,7 +117,7 @@ describe('FrequentItemsListItemComponent', () => {
|
||||||
link.vm.$emit('click');
|
link.vm.$emit('click');
|
||||||
|
|
||||||
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_link', {
|
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_link', {
|
||||||
label: 'projects_dropdown_frequent_items_list_item',
|
label: 'projects_dropdown_frequent_items_list_item_git_lab_community_edition',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -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');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -9,7 +9,10 @@ export const mockIntegrationProps = {
|
||||||
initialEnableComments: false,
|
initialEnableComments: false,
|
||||||
},
|
},
|
||||||
jiraIssuesProps: {},
|
jiraIssuesProps: {},
|
||||||
triggerEvents: [],
|
triggerEvents: [
|
||||||
|
{ name: 'push_events', title: 'Push', value: true },
|
||||||
|
{ name: 'issues_events', title: 'Issue', value: true },
|
||||||
|
],
|
||||||
sections: [],
|
sections: [],
|
||||||
fields: [],
|
fields: [],
|
||||||
type: '',
|
type: '',
|
||||||
|
|
|
@ -10,6 +10,7 @@ RSpec.describe Nav::TopNavHelper do
|
||||||
let(:current_user) { nil }
|
let(:current_user) { nil }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_application_setting(snowplow_enabled: true)
|
||||||
allow(helper).to receive(:current_user) { current_user }
|
allow(helper).to receive(:current_user) { current_user }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -50,49 +51,40 @@ RSpec.describe Nav::TopNavHelper do
|
||||||
context 'when current_user is nil (anonymous)' do
|
context 'when current_user is nil (anonymous)' do
|
||||||
it 'has expected :primary' do
|
it 'has expected :primary' do
|
||||||
expected_primary = [
|
expected_primary = [
|
||||||
::Gitlab::Nav::TopNavMenuItem.build(
|
{ href: '/explore', icon: 'project', id: 'project', title: 'Projects' },
|
||||||
href: '/explore',
|
{ href: '/explore/groups', icon: 'group', id: 'groups', title: 'Groups' },
|
||||||
icon: 'project',
|
{ href: '/explore/snippets', icon: 'snippet', id: 'snippets', title: 'Snippets' }
|
||||||
id: 'project',
|
].map do |item|
|
||||||
title: 'Projects'
|
::Gitlab::Nav::TopNavMenuItem.build(**item)
|
||||||
),
|
end
|
||||||
::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'
|
|
||||||
)
|
|
||||||
]
|
|
||||||
expect(subject[:primary]).to eq(expected_primary)
|
expect(subject[:primary]).to eq(expected_primary)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'has expected :shortcuts' do
|
it 'has expected :shortcuts' do
|
||||||
expected_shortcuts = [
|
expected_shortcuts = [
|
||||||
::Gitlab::Nav::TopNavMenuItem.build(
|
{
|
||||||
href: '/explore',
|
href: '/explore',
|
||||||
id: 'project-shortcut',
|
id: 'project-shortcut',
|
||||||
title: 'Projects',
|
title: 'Projects',
|
||||||
css_class: 'dashboard-shortcuts-projects'
|
css_class: 'dashboard-shortcuts-projects'
|
||||||
),
|
},
|
||||||
::Gitlab::Nav::TopNavMenuItem.build(
|
{
|
||||||
href: '/explore/groups',
|
href: '/explore/groups',
|
||||||
id: 'groups-shortcut',
|
id: 'groups-shortcut',
|
||||||
title: 'Groups',
|
title: 'Groups',
|
||||||
css_class: 'dashboard-shortcuts-groups'
|
css_class: 'dashboard-shortcuts-groups'
|
||||||
),
|
},
|
||||||
::Gitlab::Nav::TopNavMenuItem.build(
|
{
|
||||||
href: '/explore/snippets',
|
href: '/explore/snippets',
|
||||||
id: 'snippets-shortcut',
|
id: 'snippets-shortcut',
|
||||||
title: 'Snippets',
|
title: 'Snippets',
|
||||||
css_class: 'dashboard-shortcuts-snippets'
|
css_class: 'dashboard-shortcuts-snippets'
|
||||||
)
|
}
|
||||||
]
|
].map do |item|
|
||||||
|
::Gitlab::Nav::TopNavMenuItem.build(**item)
|
||||||
|
end
|
||||||
|
|
||||||
expect(subject[:shortcuts]).to eq(expected_shortcuts)
|
expect(subject[:shortcuts]).to eq(expected_shortcuts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -171,21 +163,41 @@ RSpec.describe Nav::TopNavHelper do
|
||||||
it 'has expected :linksPrimary' do
|
it 'has expected :linksPrimary' do
|
||||||
expected_links_primary = [
|
expected_links_primary = [
|
||||||
::Gitlab::Nav::TopNavMenuItem.build(
|
::Gitlab::Nav::TopNavMenuItem.build(
|
||||||
|
data: {
|
||||||
|
qa_selector: 'menu_item_link',
|
||||||
|
qa_title: 'Your projects',
|
||||||
|
**menu_data_tracking_attrs('your_projects')
|
||||||
|
},
|
||||||
href: '/dashboard/projects',
|
href: '/dashboard/projects',
|
||||||
id: 'your',
|
id: 'your',
|
||||||
title: 'Your projects'
|
title: 'Your projects'
|
||||||
),
|
),
|
||||||
::Gitlab::Nav::TopNavMenuItem.build(
|
::Gitlab::Nav::TopNavMenuItem.build(
|
||||||
|
data: {
|
||||||
|
qa_selector: 'menu_item_link',
|
||||||
|
qa_title: 'Starred projects',
|
||||||
|
**menu_data_tracking_attrs('starred_projects')
|
||||||
|
},
|
||||||
href: '/dashboard/projects/starred',
|
href: '/dashboard/projects/starred',
|
||||||
id: 'starred',
|
id: 'starred',
|
||||||
title: 'Starred projects'
|
title: 'Starred projects'
|
||||||
),
|
),
|
||||||
::Gitlab::Nav::TopNavMenuItem.build(
|
::Gitlab::Nav::TopNavMenuItem.build(
|
||||||
|
data: {
|
||||||
|
qa_selector: 'menu_item_link',
|
||||||
|
qa_title: 'Explore projects',
|
||||||
|
**menu_data_tracking_attrs('explore_projects')
|
||||||
|
},
|
||||||
href: '/explore',
|
href: '/explore',
|
||||||
id: 'explore',
|
id: 'explore',
|
||||||
title: 'Explore projects'
|
title: 'Explore projects'
|
||||||
),
|
),
|
||||||
::Gitlab::Nav::TopNavMenuItem.build(
|
::Gitlab::Nav::TopNavMenuItem.build(
|
||||||
|
data: {
|
||||||
|
qa_selector: 'menu_item_link',
|
||||||
|
qa_title: 'Explore topics',
|
||||||
|
**menu_data_tracking_attrs('explore_topics')
|
||||||
|
},
|
||||||
href: '/explore/projects/topics',
|
href: '/explore/projects/topics',
|
||||||
id: 'topics',
|
id: 'topics',
|
||||||
title: 'Explore topics'
|
title: 'Explore topics'
|
||||||
|
@ -197,6 +209,11 @@ RSpec.describe Nav::TopNavHelper do
|
||||||
it 'has expected :linksSecondary' do
|
it 'has expected :linksSecondary' do
|
||||||
expected_links_secondary = [
|
expected_links_secondary = [
|
||||||
::Gitlab::Nav::TopNavMenuItem.build(
|
::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',
|
href: '/projects/new',
|
||||||
id: 'create',
|
id: 'create',
|
||||||
title: 'Create new project'
|
title: 'Create new project'
|
||||||
|
@ -282,11 +299,21 @@ RSpec.describe Nav::TopNavHelper do
|
||||||
it 'has expected :linksPrimary' do
|
it 'has expected :linksPrimary' do
|
||||||
expected_links_primary = [
|
expected_links_primary = [
|
||||||
::Gitlab::Nav::TopNavMenuItem.build(
|
::Gitlab::Nav::TopNavMenuItem.build(
|
||||||
|
data: {
|
||||||
|
qa_selector: 'menu_item_link',
|
||||||
|
qa_title: 'Your groups',
|
||||||
|
**menu_data_tracking_attrs('your_groups')
|
||||||
|
},
|
||||||
href: '/dashboard/groups',
|
href: '/dashboard/groups',
|
||||||
id: 'your',
|
id: 'your',
|
||||||
title: 'Your groups'
|
title: 'Your groups'
|
||||||
),
|
),
|
||||||
::Gitlab::Nav::TopNavMenuItem.build(
|
::Gitlab::Nav::TopNavMenuItem.build(
|
||||||
|
data: {
|
||||||
|
qa_selector: 'menu_item_link',
|
||||||
|
qa_title: 'Explore groups',
|
||||||
|
**menu_data_tracking_attrs('explore_groups')
|
||||||
|
},
|
||||||
href: '/explore/groups',
|
href: '/explore/groups',
|
||||||
id: 'explore',
|
id: 'explore',
|
||||||
title: 'Explore groups'
|
title: 'Explore groups'
|
||||||
|
@ -298,6 +325,11 @@ RSpec.describe Nav::TopNavHelper do
|
||||||
it 'has expected :linksSecondary' do
|
it 'has expected :linksSecondary' do
|
||||||
expected_links_secondary = [
|
expected_links_secondary = [
|
||||||
::Gitlab::Nav::TopNavMenuItem.build(
|
::Gitlab::Nav::TopNavMenuItem.build(
|
||||||
|
data: {
|
||||||
|
qa_selector: 'menu_item_link',
|
||||||
|
qa_title: 'Create group',
|
||||||
|
**menu_data_tracking_attrs('create_group')
|
||||||
|
},
|
||||||
href: '/groups/new',
|
href: '/groups/new',
|
||||||
id: 'create',
|
id: 'create',
|
||||||
title: 'Create group'
|
title: 'Create group'
|
||||||
|
@ -356,7 +388,8 @@ RSpec.describe Nav::TopNavHelper do
|
||||||
it 'has expected :primary' do
|
it 'has expected :primary' do
|
||||||
expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
|
expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
|
||||||
data: {
|
data: {
|
||||||
qa_selector: 'milestones_link'
|
qa_selector: 'milestones_link',
|
||||||
|
**menu_data_tracking_attrs('milestones')
|
||||||
},
|
},
|
||||||
href: '/dashboard/milestones',
|
href: '/dashboard/milestones',
|
||||||
icon: 'clock',
|
icon: 'clock',
|
||||||
|
@ -383,7 +416,8 @@ RSpec.describe Nav::TopNavHelper do
|
||||||
it 'has expected :primary' do
|
it 'has expected :primary' do
|
||||||
expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
|
expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
|
||||||
data: {
|
data: {
|
||||||
qa_selector: 'snippets_link'
|
qa_selector: 'snippets_link',
|
||||||
|
**menu_data_tracking_attrs('snippets')
|
||||||
},
|
},
|
||||||
href: '/dashboard/snippets',
|
href: '/dashboard/snippets',
|
||||||
icon: 'snippet',
|
icon: 'snippet',
|
||||||
|
@ -410,7 +444,8 @@ RSpec.describe Nav::TopNavHelper do
|
||||||
it 'has expected :primary' do
|
it 'has expected :primary' do
|
||||||
expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
|
expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
|
||||||
data: {
|
data: {
|
||||||
qa_selector: 'activity_link'
|
qa_selector: 'activity_link',
|
||||||
|
**menu_data_tracking_attrs('activity')
|
||||||
},
|
},
|
||||||
href: '/dashboard/activity',
|
href: '/dashboard/activity',
|
||||||
icon: 'history',
|
icon: 'history',
|
||||||
|
@ -439,6 +474,11 @@ RSpec.describe Nav::TopNavHelper do
|
||||||
|
|
||||||
it 'has admin as first :secondary item' do
|
it 'has admin as first :secondary item' do
|
||||||
expected_admin_item = ::Gitlab::Nav::TopNavMenuItem.build(
|
expected_admin_item = ::Gitlab::Nav::TopNavMenuItem.build(
|
||||||
|
data: {
|
||||||
|
qa_selector: 'menu_item_link',
|
||||||
|
qa_title: 'Admin',
|
||||||
|
**menu_data_tracking_attrs('admin')
|
||||||
|
},
|
||||||
id: 'admin',
|
id: 'admin',
|
||||||
title: 'Admin',
|
title: 'Admin',
|
||||||
icon: 'admin',
|
icon: 'admin',
|
||||||
|
@ -458,7 +498,7 @@ RSpec.describe Nav::TopNavHelper do
|
||||||
title: 'Leave Admin Mode',
|
title: 'Leave Admin Mode',
|
||||||
icon: 'lock-open',
|
icon: 'lock-open',
|
||||||
href: '/admin/session/destroy',
|
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)
|
expect(subject[:secondary].last).to eq(expected_leave_admin_mode_item)
|
||||||
end
|
end
|
||||||
|
@ -469,6 +509,11 @@ RSpec.describe Nav::TopNavHelper do
|
||||||
|
|
||||||
it 'has enter_admin_mode as last :secondary item' do
|
it 'has enter_admin_mode as last :secondary item' do
|
||||||
expected_enter_admin_mode_item = ::Gitlab::Nav::TopNavMenuItem.build(
|
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',
|
id: 'enter_admin_mode',
|
||||||
title: 'Enter Admin Mode',
|
title: 'Enter Admin Mode',
|
||||||
icon: 'lock',
|
icon: 'lock',
|
||||||
|
@ -533,4 +578,12 @@ RSpec.describe Nav::TopNavHelper do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def menu_data_tracking_attrs(label)
|
||||||
|
{
|
||||||
|
track_label: "menu_#{label}",
|
||||||
|
track_action: 'click_dropdown',
|
||||||
|
track_property: 'navigation'
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -121,13 +121,19 @@ RSpec.describe Gitlab::GitAccessSnippet do
|
||||||
if Ability.allowed?(user, :update_snippet, snippet)
|
if Ability.allowed?(user, :update_snippet, snippet)
|
||||||
expect { push_access_check }.not_to raise_error
|
expect { push_access_check }.not_to raise_error
|
||||||
else
|
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
|
end
|
||||||
|
|
||||||
if Ability.allowed?(user, :read_snippet, snippet)
|
if Ability.allowed?(user, :read_snippet, snippet)
|
||||||
expect { pull_access_check }.not_to raise_error
|
expect { pull_access_check }.not_to raise_error
|
||||||
else
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,7 +18,7 @@ RSpec.describe Gitlab::GitAccessWiki do
|
||||||
redirected_path: redirected_path)
|
redirected_path: redirected_path)
|
||||||
end
|
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
|
where(:project_visibility, :project_member?, :wiki_access_level, :wiki_repo?, :expected_behavior) do
|
||||||
[
|
[
|
||||||
# Private project - is a project member
|
# Private project - is a project member
|
||||||
|
@ -103,7 +103,7 @@ RSpec.describe Gitlab::GitAccessWiki do
|
||||||
subject { access.check('git-upload-pack', Gitlab::GitAccess::ANY) }
|
subject { access.check('git-upload-pack', Gitlab::GitAccess::ANY) }
|
||||||
|
|
||||||
context 'when actor is a user' do
|
context 'when actor is a user' do
|
||||||
it_behaves_like 'wiki access by level'
|
it_behaves_like 'download wiki access by level'
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the actor is a deploy token' do
|
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) }
|
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 }
|
let(:wiki_access_level) { ProjectFeature::ENABLED }
|
||||||
|
|
||||||
it { expect { subject }.not_to raise_error }
|
it { expect { subject }.not_to raise_error }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the wiki is disabled' do
|
context 'when the wiki feature is disabled' do
|
||||||
let(:wiki_access_level) { ProjectFeature::DISABLED }
|
let(:wiki_access_level) { ProjectFeature::DISABLED }
|
||||||
|
|
||||||
it { expect { subject }.to raise_wiki_forbidden }
|
it { expect { subject }.to raise_wiki_forbidden }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when the wiki feature is private' do
|
||||||
|
let(:wiki_access_level) { ProjectFeature::PRIVATE }
|
||||||
|
|
||||||
|
it { expect { subject }.to raise_wiki_forbidden }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when actor is a user provided by build via CI_JOB_TOKEN' do
|
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) }
|
subject { access.check('git-upload-pack', changes) }
|
||||||
|
|
||||||
it_behaves_like 'wiki access by level'
|
it_behaves_like 'download wiki access by level'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
10
spec/requests/api/markdown_snapshot_spec.rb
Normal file
10
spec/requests/api/markdown_snapshot_spec.rb
Normal file
|
@ -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
|
|
@ -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
|
|
@ -6,30 +6,12 @@ RSpec.describe Database::CiNamespaceMirrorsConsistencyCheckWorker do
|
||||||
let(:worker) { described_class.new }
|
let(:worker) { described_class.new }
|
||||||
|
|
||||||
describe '#perform' do
|
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
|
it 'executes the consistency check on namespaces' do
|
||||||
expect(Database::ConsistencyCheckService).to receive(:new).and_call_original
|
expect(Database::ConsistencyCheckService).to receive(:new).and_call_original
|
||||||
expected_result = { batches: 0, matches: 0, mismatches: 0, mismatches_details: [] }
|
expected_result = { batches: 0, matches: 0, mismatches: 0, mismatches_details: [] }
|
||||||
expect(worker).to receive(:log_extra_metadata_on_done).with(:results, expected_result)
|
expect(worker).to receive(:log_extra_metadata_on_done).with(:results, expected_result)
|
||||||
worker.perform
|
worker.perform
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
context 'logs should contain the detailed mismatches' do
|
context 'logs should contain the detailed mismatches' do
|
||||||
let(:first_namespace) { Namespace.all.order(:id).limit(1).first }
|
let(:first_namespace) { Namespace.all.order(:id).limit(1).first }
|
||||||
|
@ -37,7 +19,6 @@ RSpec.describe Database::CiNamespaceMirrorsConsistencyCheckWorker do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
redis_shared_state_cleanup!
|
redis_shared_state_cleanup!
|
||||||
stub_feature_flags(ci_namespace_mirrors_consistency_check: true)
|
|
||||||
create_list(:namespace, 10) # This will also create Ci::NameSpaceMirror objects
|
create_list(:namespace, 10) # This will also create Ci::NameSpaceMirror objects
|
||||||
missing_namespace.delete
|
missing_namespace.delete
|
||||||
|
|
||||||
|
|
|
@ -6,30 +6,12 @@ RSpec.describe Database::CiProjectMirrorsConsistencyCheckWorker do
|
||||||
let(:worker) { described_class.new }
|
let(:worker) { described_class.new }
|
||||||
|
|
||||||
describe '#perform' do
|
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
|
it 'executes the consistency check on projects' do
|
||||||
expect(Database::ConsistencyCheckService).to receive(:new).and_call_original
|
expect(Database::ConsistencyCheckService).to receive(:new).and_call_original
|
||||||
expected_result = { batches: 0, matches: 0, mismatches: 0, mismatches_details: [] }
|
expected_result = { batches: 0, matches: 0, mismatches: 0, mismatches_details: [] }
|
||||||
expect(worker).to receive(:log_extra_metadata_on_done).with(:results, expected_result)
|
expect(worker).to receive(:log_extra_metadata_on_done).with(:results, expected_result)
|
||||||
worker.perform
|
worker.perform
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
context 'logs should contain the detailed mismatches' do
|
context 'logs should contain the detailed mismatches' do
|
||||||
let(:first_project) { Project.all.order(:id).limit(1).first }
|
let(:first_project) { Project.all.order(:id).limit(1).first }
|
||||||
|
@ -37,7 +19,6 @@ RSpec.describe Database::CiProjectMirrorsConsistencyCheckWorker do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
redis_shared_state_cleanup!
|
redis_shared_state_cleanup!
|
||||||
stub_feature_flags(ci_project_mirrors_consistency_check: true)
|
|
||||||
create_list(:project, 10) # This will also create Ci::ProjectMirror objects
|
create_list(:project, 10) # This will also create Ci::ProjectMirror objects
|
||||||
missing_project.delete
|
missing_project.delete
|
||||||
|
|
||||||
|
|
|
@ -968,10 +968,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-2.14.0.tgz#92b36bc98ccbed49a4dbca310862146275091cb2"
|
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-2.14.0.tgz#92b36bc98ccbed49a4dbca310862146275091cb2"
|
||||||
integrity sha512-U9EYmEIiTMl7R3X5DmCrw6fz7gz8c1kjvQtaF6HfJ15xDtR7trRAyCNbn3z7YGk1QJ8Cv/Ifw2/T5SxXwYd7dw==
|
integrity sha512-U9EYmEIiTMl7R3X5DmCrw6fz7gz8c1kjvQtaF6HfJ15xDtR7trRAyCNbn3z7YGk1QJ8Cv/Ifw2/T5SxXwYd7dw==
|
||||||
|
|
||||||
"@gitlab/ui@40.6.6":
|
"@gitlab/ui@40.7.1":
|
||||||
version "40.6.6"
|
version "40.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-40.6.6.tgz#07e95edd1b0afe8cb2f42880f634657ffbcaa3cd"
|
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-40.7.1.tgz#e6595c5cc37d994e3f0ba780626fbf4e8174df2a"
|
||||||
integrity sha512-hHwu63oldEtALYS3KPkU0CUVIznpoScAURzCKscw2/wNmXc+siVOA4iGPJ012h5Jza7itTeQg0UFCWGjigUaYA==
|
integrity sha512-u100mDpdI7RfNVcAYi8n0RRH2FfIiYuMVgt5jPrQ7AAL+QrwLAkqfBZtkT9pSMpycBuuQsxSMHJK5FlnXum46g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@popperjs/core" "^2.11.2"
|
"@popperjs/core" "^2.11.2"
|
||||||
bootstrap-vue "2.20.1"
|
bootstrap-vue "2.20.1"
|
||||||
|
|
Loading…
Reference in a new issue