Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-06-03 18:08:54 +00:00
parent f5f6cb45c7
commit 27484d1465
38 changed files with 562 additions and 198 deletions

View file

@ -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"

View file

@ -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 = {

View file

@ -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,

View file

@ -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>

View file

@ -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>

View file

@ -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,

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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|

View file

@ -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"

View file

@ -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,

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)`

View file

@ -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,16 +295,10 @@ 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?
return if can_download?
unless passed
raise ForbiddenError, download_forbidden_message
end
end
def download_forbidden_message
error_message(:download)

View file

@ -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?
unless passed
raise ForbiddenError, error_message(:read_snippet)
override :can_download?
def can_download?
guest_can_download? || user_can_download?
end
override :download_forbidden_message
def download_forbidden_message
error_message(:read_snippet)
end
override :check_change_access!

View file

@ -27,12 +27,16 @@ module Gitlab
:create_wiki
end
override :check_download_access!
def check_download_access!
super
private
raise ForbiddenError, download_forbidden_message if build_cannot_download?
raise ForbiddenError, download_forbidden_message if deploy_token_cannot_download?
override :build_can_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
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

View file

@ -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 ""

View file

@ -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",

View file

@ -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

View file

@ -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'

View file

@ -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',
});
});
});

View file

@ -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]);
});
});
});
});

View file

@ -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');
});
});
});

View file

@ -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: '',

View file

@ -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

View file

@ -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

View file

@ -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

View 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

View file

@ -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

View file

@ -6,30 +6,12 @@ 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
end
context 'logs should contain the detailed mismatches' do
let(:first_namespace) { Namespace.all.order(:id).limit(1).first }
@ -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

View file

@ -6,30 +6,12 @@ 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
end
context 'logs should contain the detailed mismatches' do
let(:first_project) { Project.all.order(:id).limit(1).first }
@ -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

View file

@ -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"