Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-12-20 21:09:05 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-12-20 21:09:05 +0300
commit883d5720994852248f18cb3053dc9f053f28d6f9 (patch)
tree409c976ddc659f34afaae3b2e97f1d0325f6455c /spec
parent5e97da08cba997aefba6f6d13850f95536a80477 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/abuse_report_spec.rb36
-rw-r--r--spec/features/nav/new_nav_toggle_spec.rb14
-rw-r--r--spec/frontend/abuse_reports/components/abuse_category_selector_spec.js125
-rw-r--r--spec/frontend/behaviors/markdown/render_gfm_spec.js9
-rw-r--r--spec/frontend/ci/pipeline_schedules/components/table/cells/pipeline_schedule_last_pipeline_spec.js12
-rw-r--r--spec/frontend/gfm_auto_complete/mock_data.js24
-rw-r--r--spec/frontend/gfm_auto_complete_spec.js27
-rw-r--r--spec/frontend/jobs/components/table/jobs_table_spec.js6
-rw-r--r--spec/frontend/members/components/table/role_dropdown_spec.js58
-rw-r--r--spec/frontend/members/guest_overage_confirm_action_spec.js7
-rw-r--r--spec/frontend/pipelines/pipelines_table_spec.js8
-rw-r--r--spec/frontend/repository/commits_service_spec.js8
-rw-r--r--spec/frontend/repository/mock_data.js2
-rw-r--r--spec/frontend/repository/utils/ref_switcher_utils_spec.js7
-rw-r--r--spec/frontend/users/profile/components/report_abuse_button_spec.js72
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap163
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled_spec.js8
-rw-r--r--spec/frontend/vue_shared/components/ci_badge_link_spec.js4
-rw-r--r--spec/graphql/types/description_version_type_spec.rb10
-rw-r--r--spec/graphql/types/notes/note_type_spec.rb3
-rw-r--r--spec/graphql/types/notes/system_note_metadata_type_spec.rb11
-rw-r--r--spec/helpers/nav_helper_spec.rb58
-rw-r--r--spec/lib/gitlab/ci/config/entry/cache_spec.rb14
-rw-r--r--spec/lib/gitlab/ci/config/entry/job_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/config/entry/root_spec.rb15
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb21
-rw-r--r--spec/models/abuse_report_spec.rb20
-rw-r--r--spec/models/ci/build_spec.rb13
-rw-r--r--spec/requests/abuse_reports_controller_spec.rb74
-rw-r--r--spec/requests/api/debian_project_packages_spec.rb29
-rw-r--r--spec/requests/api/graphql/project/work_items_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/cache_spec.rb13
33 files changed, 638 insertions, 240 deletions
diff --git a/spec/features/abuse_report_spec.rb b/spec/features/abuse_report_spec.rb
index fdd11b59938..87236530046 100644
--- a/spec/features/abuse_report_spec.rb
+++ b/spec/features/abuse_report_spec.rb
@@ -2,17 +2,43 @@
require 'spec_helper'
-RSpec.describe 'Abuse reports', feature_category: :not_owned do
- let(:another_user) { create(:user) }
+RSpec.describe 'Abuse reports', feature_category: :insider_threat do
+ let_it_be(:another_user) { create(:user) }
+
+ let_it_be(:project) { create(:project, :public) }
+ let_it_be(:issue) { create(:issue, project: project, author: another_user) }
before do
sign_in(create(:user))
end
- it 'report abuse' do
+ it 'report abuse from an issue', :js do
+ visit project_issue_path(project, issue)
+
+ click_button 'Issue actions'
+ click_link 'Report abuse to administrator'
+
+ wait_for_requests
+
+ fill_in 'abuse_report_message', with: 'This user sends spam'
+ click_button 'Send report'
+
+ expect(page).to have_content 'Thank you for your report'
+
visit user_path(another_user)
- click_link 'Report abuse'
+ expect(page).to have_button('Already reported for abuse')
+ end
+
+ it 'report abuse from profile', :js do
+ visit user_path(another_user)
+
+ click_button 'Report abuse to administrator'
+
+ choose "They're posting spam."
+ click_button 'Next'
+
+ wait_for_requests
fill_in 'abuse_report_message', with: 'This user sends spam'
click_button 'Send report'
@@ -21,6 +47,6 @@ RSpec.describe 'Abuse reports', feature_category: :not_owned do
visit user_path(another_user)
- expect(page).to have_button("Already reported for abuse")
+ expect(page).to have_button('Already reported for abuse')
end
end
diff --git a/spec/features/nav/new_nav_toggle_spec.rb b/spec/features/nav/new_nav_toggle_spec.rb
index f040d801cfb..8e5cc7df053 100644
--- a/spec/features/nav/new_nav_toggle_spec.rb
+++ b/spec/features/nav/new_nav_toggle_spec.rb
@@ -48,14 +48,19 @@ RSpec.describe 'new navigation toggle', :js, feature_category: :navigation do
expect(user.reload.use_new_navigation).to eq true
end
+
+ it 'shows the old navigation' do
+ expect(page).to have_selector('.js-navbar')
+ expect(page).not_to have_selector('[data-testid="super-sidebar"]')
+ end
end
context 'when user has new nav enabled' do
let(:user_preference) { true }
it 'allows to disable new nav', :aggregate_failures do
- within '.js-nav-user-dropdown' do
- find('a[data-toggle="dropdown"]').click
+ within '[data-testid="super-sidebar"] [data-testid="user-dropdown"]' do
+ find('button').click
expect(page).to have_content('Navigation redesign')
toggle = page.find('.gl-toggle.is-checked')
@@ -66,6 +71,11 @@ RSpec.describe 'new navigation toggle', :js, feature_category: :navigation do
expect(user.reload.use_new_navigation).to eq false
end
+
+ it 'shows the new navigation' do
+ expect(page).not_to have_selector('.js-navbar')
+ expect(page).to have_selector('[data-testid="super-sidebar"]')
+ end
end
end
end
diff --git a/spec/frontend/abuse_reports/components/abuse_category_selector_spec.js b/spec/frontend/abuse_reports/components/abuse_category_selector_spec.js
new file mode 100644
index 00000000000..4f66348f9cd
--- /dev/null
+++ b/spec/frontend/abuse_reports/components/abuse_category_selector_spec.js
@@ -0,0 +1,125 @@
+import { GlDrawer, GlForm, GlFormGroup, GlFormRadioGroup } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+
+import AbuseCategorySelector from '~/abuse_reports/components/abuse_category_selector.vue';
+
+jest.mock('~/lib/utils/common_utils', () => ({
+ contentTop: jest.fn(),
+}));
+
+jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
+
+describe('AbuseCategorySelector', () => {
+ let wrapper;
+
+ const ACTION_PATH = '/abuse_reports/add_category';
+ const USER_ID = '1';
+ const REPORTED_FROM_URL = 'http://example.com';
+
+ const createComponent = (props) => {
+ wrapper = shallowMountExtended(AbuseCategorySelector, {
+ propsData: {
+ ...props,
+ },
+ provide: {
+ formSubmitPath: ACTION_PATH,
+ userId: USER_ID,
+ reportedFromUrl: REPORTED_FROM_URL,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ createComponent({ showDrawer: true });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findDrawer = () => wrapper.findComponent(GlDrawer);
+ const findTitle = () => wrapper.findByTestId('category-drawer-title');
+
+ const findForm = () => wrapper.findComponent(GlForm);
+ const findFormGroup = () => wrapper.findComponent(GlFormGroup);
+ const findRadioGroup = () => wrapper.findComponent(GlFormRadioGroup);
+
+ const findCSRFToken = () => findForm().find('input[name="authenticity_token"]');
+ const findUserId = () => wrapper.findByTestId('input-user-id');
+ const findReferer = () => wrapper.findByTestId('input-referer');
+
+ const findSubmitFormButton = () => wrapper.findByTestId('submit-form-button');
+
+ describe('Drawer', () => {
+ it('is open when prop showDrawer = true', () => {
+ expect(findDrawer().exists()).toBe(true);
+ expect(findDrawer().props('open')).toBe(true);
+ });
+
+ it('renders title', () => {
+ expect(findTitle().text()).toBe(wrapper.vm.$options.i18n.title);
+ });
+
+ it('emits close-drawer event', async () => {
+ await findDrawer().vm.$emit('close');
+
+ expect(wrapper.emitted('close-drawer')).toHaveLength(1);
+ });
+
+ describe('when props showDrawer = false', () => {
+ beforeEach(() => {
+ createComponent({ showDrawer: false });
+ });
+
+ it('hides the drawer', () => {
+ expect(findDrawer().props('open')).toBe(false);
+ });
+ });
+ });
+
+ describe('Select category form', () => {
+ it('renders POST form with path', () => {
+ expect(findForm().attributes()).toMatchObject({
+ method: 'post',
+ action: ACTION_PATH,
+ });
+ });
+
+ it('renders csrf token', () => {
+ expect(findCSRFToken().attributes('value')).toBe('mock-csrf-token');
+ });
+
+ it('renders label', () => {
+ expect(findFormGroup().exists()).toBe(true);
+ expect(findFormGroup().attributes('label')).toBe(wrapper.vm.$options.i18n.label);
+ });
+
+ it('renders radio group', () => {
+ expect(findRadioGroup().exists()).toBe(true);
+ expect(findRadioGroup().props('options')).toEqual(wrapper.vm.$options.categoryOptions);
+ expect(findRadioGroup().attributes('name')).toBe('abuse_report[category]');
+ expect(findRadioGroup().attributes('required')).not.toBeUndefined();
+ });
+
+ it('renders userId as a hidden fields', () => {
+ expect(findUserId().attributes()).toMatchObject({
+ type: 'hidden',
+ name: 'user_id',
+ value: USER_ID,
+ });
+ });
+
+ it('renders referer as a hidden fields', () => {
+ expect(findReferer().attributes()).toMatchObject({
+ type: 'hidden',
+ name: 'ref_url',
+ value: REPORTED_FROM_URL,
+ });
+ });
+
+ it('renders submit button', () => {
+ expect(findSubmitFormButton().exists()).toBe(true);
+ expect(findSubmitFormButton().text()).toBe(wrapper.vm.$options.i18n.next);
+ });
+ });
+});
diff --git a/spec/frontend/behaviors/markdown/render_gfm_spec.js b/spec/frontend/behaviors/markdown/render_gfm_spec.js
new file mode 100644
index 00000000000..0bbb92282e5
--- /dev/null
+++ b/spec/frontend/behaviors/markdown/render_gfm_spec.js
@@ -0,0 +1,9 @@
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
+
+describe('renderGFM', () => {
+ it('handles a missing element', () => {
+ expect(() => {
+ renderGFM();
+ }).not.toThrow();
+ });
+});
diff --git a/spec/frontend/ci/pipeline_schedules/components/table/cells/pipeline_schedule_last_pipeline_spec.js b/spec/frontend/ci/pipeline_schedules/components/table/cells/pipeline_schedule_last_pipeline_spec.js
index 17bf465baf3..0821c59c8a0 100644
--- a/spec/frontend/ci/pipeline_schedules/components/table/cells/pipeline_schedule_last_pipeline_spec.js
+++ b/spec/frontend/ci/pipeline_schedules/components/table/cells/pipeline_schedule_last_pipeline_spec.js
@@ -1,5 +1,5 @@
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import CiBadge from '~/vue_shared/components/ci_badge_link.vue';
+import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
import PipelineScheduleLastPipeline from '~/ci/pipeline_schedules/components/table/cells/pipeline_schedule_last_pipeline.vue';
import { mockPipelineScheduleNodes } from '../../../mock_data';
@@ -18,7 +18,7 @@ describe('Pipeline schedule last pipeline', () => {
});
};
- const findCIBadge = () => wrapper.findComponent(CiBadge);
+ const findCIBadgeLink = () => wrapper.findComponent(CiBadgeLink);
const findStatusText = () => wrapper.findByTestId('pipeline-schedule-status-text');
afterEach(() => {
@@ -28,8 +28,10 @@ describe('Pipeline schedule last pipeline', () => {
it('displays pipeline status', () => {
createComponent();
- expect(findCIBadge().exists()).toBe(true);
- expect(findCIBadge().props('status')).toBe(defaultProps.schedule.lastPipeline.detailedStatus);
+ expect(findCIBadgeLink().exists()).toBe(true);
+ expect(findCIBadgeLink().props('status')).toBe(
+ defaultProps.schedule.lastPipeline.detailedStatus,
+ );
expect(findStatusText().exists()).toBe(false);
});
@@ -37,6 +39,6 @@ describe('Pipeline schedule last pipeline', () => {
createComponent({ schedule: mockPipelineScheduleNodes[0] });
expect(findStatusText().text()).toBe('None');
- expect(findCIBadge().exists()).toBe(false);
+ expect(findCIBadgeLink().exists()).toBe(false);
});
});
diff --git a/spec/frontend/gfm_auto_complete/mock_data.js b/spec/frontend/gfm_auto_complete/mock_data.js
index 9c5a9d7ef3d..d58ccaf0f39 100644
--- a/spec/frontend/gfm_auto_complete/mock_data.js
+++ b/spec/frontend/gfm_auto_complete/mock_data.js
@@ -37,8 +37,8 @@ export const crmContactsMock = [
{
id: 1,
email: 'contact.1@email.com',
- firstName: 'Contact',
- lastName: 'One',
+ first_name: 'Contact',
+ last_name: 'One',
search: 'contact.1@email.com',
state: 'active',
set: false,
@@ -46,8 +46,8 @@ export const crmContactsMock = [
{
id: 2,
email: 'contact.2@email.com',
- firstName: 'Contact',
- lastName: 'Two',
+ first_name: 'Contact',
+ last_name: 'Two',
search: 'contact.2@email.com',
state: 'active',
set: false,
@@ -55,8 +55,8 @@ export const crmContactsMock = [
{
id: 3,
email: 'contact.3@email.com',
- firstName: 'Contact',
- lastName: 'Three',
+ first_name: 'Contact',
+ last_name: 'Three',
search: 'contact.3@email.com',
state: 'inactive',
set: false,
@@ -64,8 +64,8 @@ export const crmContactsMock = [
{
id: 4,
email: 'contact.4@email.com',
- firstName: 'Contact',
- lastName: 'Four',
+ first_name: 'Contact',
+ last_name: 'Four',
search: 'contact.4@email.com',
state: 'inactive',
set: true,
@@ -73,8 +73,8 @@ export const crmContactsMock = [
{
id: 5,
email: 'contact.5@email.com',
- firstName: 'Contact',
- lastName: 'Five',
+ first_name: 'Contact',
+ last_name: 'Five',
search: 'contact.5@email.com',
state: 'active',
set: true,
@@ -82,8 +82,8 @@ export const crmContactsMock = [
{
id: 5,
email: 'contact.6@email.com',
- firstName: 'Contact',
- lastName: 'Six',
+ first_name: 'Contact',
+ last_name: 'Six',
search: 'contact.6@email.com',
state: 'active',
set: undefined, // On purpose
diff --git a/spec/frontend/gfm_auto_complete_spec.js b/spec/frontend/gfm_auto_complete_spec.js
index eeef92d4183..cc2dc084e47 100644
--- a/spec/frontend/gfm_auto_complete_spec.js
+++ b/spec/frontend/gfm_auto_complete_spec.js
@@ -4,6 +4,7 @@ import $ from 'jquery';
import labelsFixture from 'test_fixtures/autocomplete_sources/labels.json';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import GfmAutoComplete, {
+ escape,
membersBeforeSave,
highlighter,
CONTACT_STATE_ACTIVE,
@@ -21,6 +22,20 @@ import {
crmContactsMock,
} from 'ee_else_ce_jest/gfm_auto_complete/mock_data';
+describe('escape', () => {
+ it.each`
+ xssPayload | escapedPayload
+ ${'<script>alert(1)</script>'} | ${'&lt;script&gt;alert(1)&lt;/script&gt;'}
+ ${'%3Cscript%3E alert(1) %3C%2Fscript%3E'} | ${'&lt;script&gt; alert(1) &lt;/script&gt;'}
+ ${'%253Cscript%253E alert(1) %253C%252Fscript%253E'} | ${'&lt;script&gt; alert(1) &lt;/script&gt;'}
+ `(
+ 'escapes the input string correctly accounting for multiple encoding',
+ ({ xssPayload, escapedPayload }) => {
+ expect(escape(xssPayload)).toBe(escapedPayload);
+ },
+ );
+});
+
describe('GfmAutoComplete', () => {
const fetchDataMock = { fetchData: jest.fn() };
let gfmAutoCompleteCallbacks = GfmAutoComplete.prototype.getDefaultCallbacks.call(fetchDataMock);
@@ -590,7 +605,7 @@ describe('GfmAutoComplete', () => {
id: 5,
title: '${search}<script>oh no $', // eslint-disable-line no-template-curly-in-string
}),
- ).toBe('<li><small>5</small> &dollar;{search}&lt;script&gt;oh no &dollar;</li>');
+ ).toBe('<li><small>5</small> &amp;dollar;{search}&lt;script&gt;oh no &amp;dollar;</li>');
});
});
@@ -636,7 +651,7 @@ describe('GfmAutoComplete', () => {
availabilityStatus: '',
}),
).toBe(
- '<li>IMG my-group <small>&dollar;{search}&lt;script&gt;oh no &dollar;</small> <i class="icon"/></li>',
+ '<li>IMG my-group <small>&amp;dollar;{search}&lt;script&gt;oh no &amp;dollar;</small> <i class="icon"/></li>',
);
});
@@ -813,7 +828,7 @@ describe('GfmAutoComplete', () => {
const title = '${search}<script>oh no $'; // eslint-disable-line no-template-curly-in-string
expect(GfmAutoComplete.Labels.templateFunction(color, title)).toBe(
- '<li><span class="dropdown-label-box" style="background: #123456"></span> &dollar;{search}&lt;script&gt;oh no &dollar;</li>',
+ '<li><span class="dropdown-label-box" style="background: #123456"></span> &amp;dollar;{search}&lt;script&gt;oh no &amp;dollar;</li>',
);
});
});
@@ -868,7 +883,7 @@ describe('GfmAutoComplete', () => {
const title = '${search}<script>oh no $'; // eslint-disable-line no-template-curly-in-string
expect(GfmAutoComplete.Milestones.templateFunction(title, expired)).toBe(
- '<li>&dollar;{search}&lt;script&gt;oh no &dollar;</li>',
+ '<li>&amp;dollar;{search}&lt;script&gt;oh no &amp;dollar;</li>',
);
});
});
@@ -925,7 +940,9 @@ describe('GfmAutoComplete', () => {
const expectContacts = ({ input, output }) => {
triggerDropdown(input);
- expect(getDropdownItems()).toEqual(output.map((contact) => contact.email));
+ expect(getDropdownItems()).toEqual(
+ output.map((contact) => `${contact.first_name} ${contact.last_name} ${contact.email}`),
+ );
};
describe('with no contacts assigned', () => {
diff --git a/spec/frontend/jobs/components/table/jobs_table_spec.js b/spec/frontend/jobs/components/table/jobs_table_spec.js
index 803df3df37f..3c4f2d624fe 100644
--- a/spec/frontend/jobs/components/table/jobs_table_spec.js
+++ b/spec/frontend/jobs/components/table/jobs_table_spec.js
@@ -2,14 +2,14 @@ import { GlTable } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import JobsTable from '~/jobs/components/table/jobs_table.vue';
-import CiBadge from '~/vue_shared/components/ci_badge_link.vue';
+import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
import { mockJobsNodes } from '../../mock_data';
describe('Jobs Table', () => {
let wrapper;
const findTable = () => wrapper.findComponent(GlTable);
- const findStatusBadge = () => wrapper.findComponent(CiBadge);
+ const findCiBadgeLink = () => wrapper.findComponent(CiBadgeLink);
const findTableRows = () => wrapper.findAllByTestId('jobs-table-row');
const findJobStage = () => wrapper.findByTestId('job-stage-name');
const findJobName = () => wrapper.findByTestId('job-name');
@@ -43,7 +43,7 @@ describe('Jobs Table', () => {
});
it('displays job status', () => {
- expect(findStatusBadge().exists()).toBe(true);
+ expect(findCiBadgeLink().exists()).toBe(true);
});
it('displays the job stage and name', () => {
diff --git a/spec/frontend/members/components/table/role_dropdown_spec.js b/spec/frontend/members/components/table/role_dropdown_spec.js
index b254cce4d72..3815064b3f6 100644
--- a/spec/frontend/members/components/table/role_dropdown_spec.js
+++ b/spec/frontend/members/components/table/role_dropdown_spec.js
@@ -4,11 +4,14 @@ import { within } from '@testing-library/dom';
import { mount, createWrapper } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
+import waitForPromises from 'helpers/wait_for_promises';
import RoleDropdown from '~/members/components/table/role_dropdown.vue';
import { MEMBER_TYPES } from '~/members/constants';
+import { guestOverageConfirmAction } from 'ee_else_ce/members/guest_overage_confirm_action';
import { member } from '../../mock_data';
Vue.use(Vuex);
+jest.mock('ee_else_ce/members/guest_overage_confirm_action');
describe('RoleDropdown', () => {
let wrapper;
@@ -63,12 +66,21 @@ describe('RoleDropdown', () => {
const findDropdownToggle = () => wrapper.find('button[aria-haspopup="true"]');
const findDropdown = () => wrapper.findComponent(GlDropdown);
+ let originalGon;
+
+ beforeEach(() => {
+ originalGon = window.gon;
+ gon.features = { showOverageOnRolePromotion: true };
+ });
+
afterEach(() => {
+ window.gon = originalGon;
wrapper.destroy();
});
describe('when dropdown is open', () => {
beforeEach(() => {
+ guestOverageConfirmAction.mockReturnValue(true);
createComponent();
return findDropdownToggle().trigger('click');
@@ -117,8 +129,12 @@ describe('RoleDropdown', () => {
await getDropdownItemByText('Developer').trigger('click');
expect(findDropdown().props('disabled')).toBe(true);
+ });
- await nextTick();
+ it('enables dropdown after `updateMemberRole` resolves', async () => {
+ await getDropdownItemByText('Developer').trigger('click');
+
+ await waitForPromises();
expect(findDropdown().props('disabled')).toBe(false);
});
@@ -148,4 +164,44 @@ describe('RoleDropdown', () => {
expect(findDropdown().props('right')).toBe(false);
});
+
+ describe('guestOverageConfirmAction', () => {
+ const mockConfirmAction = ({ confirmed }) => {
+ guestOverageConfirmAction.mockResolvedValueOnce(confirmed);
+ };
+
+ beforeEach(() => {
+ createComponent();
+
+ findDropdownToggle().trigger('click');
+ });
+
+ afterEach(() => {
+ guestOverageConfirmAction.mockReset();
+ });
+
+ describe('when guestOverageConfirmAction returns true', () => {
+ beforeEach(() => {
+ mockConfirmAction({ confirmed: true });
+
+ getDropdownItemByText('Reporter').trigger('click');
+ });
+
+ it('calls updateMemberRole', () => {
+ expect(actions.updateMemberRole).toHaveBeenCalled();
+ });
+ });
+
+ describe('when guestOverageConfirmAction returns false', () => {
+ beforeEach(() => {
+ mockConfirmAction({ confirmed: false });
+
+ getDropdownItemByText('Reporter').trigger('click');
+ });
+
+ it('does not call updateMemberRole', () => {
+ expect(actions.updateMemberRole).not.toHaveBeenCalled();
+ });
+ });
+ });
});
diff --git a/spec/frontend/members/guest_overage_confirm_action_spec.js b/spec/frontend/members/guest_overage_confirm_action_spec.js
new file mode 100644
index 00000000000..d7ab54fa13b
--- /dev/null
+++ b/spec/frontend/members/guest_overage_confirm_action_spec.js
@@ -0,0 +1,7 @@
+import { guestOverageConfirmAction } from '~/members/guest_overage_confirm_action';
+
+describe('guestOverageConfirmAction', () => {
+ it('returns true', () => {
+ expect(guestOverageConfirmAction()).toBe(true);
+ });
+});
diff --git a/spec/frontend/pipelines/pipelines_table_spec.js b/spec/frontend/pipelines/pipelines_table_spec.js
index 740037a5ac8..9359bd9b95f 100644
--- a/spec/frontend/pipelines/pipelines_table_spec.js
+++ b/spec/frontend/pipelines/pipelines_table_spec.js
@@ -17,7 +17,7 @@ import {
TRACKING_CATEGORIES,
} from '~/pipelines/constants';
-import CiBadge from '~/vue_shared/components/ci_badge_link.vue';
+import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
jest.mock('~/pipelines/event_hub');
@@ -50,7 +50,7 @@ describe('Pipelines Table', () => {
};
const findGlTableLite = () => wrapper.findComponent(GlTableLite);
- const findStatusBadge = () => wrapper.findComponent(CiBadge);
+ const findCiBadgeLink = () => wrapper.findComponent(CiBadgeLink);
const findPipelineInfo = () => wrapper.findComponent(PipelineUrl);
const findTriggerer = () => wrapper.findComponent(PipelineTriggerer);
const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph);
@@ -97,7 +97,7 @@ describe('Pipelines Table', () => {
describe('status cell', () => {
it('should render a status badge', () => {
- expect(findStatusBadge().exists()).toBe(true);
+ expect(findCiBadgeLink().exists()).toBe(true);
});
});
@@ -171,7 +171,7 @@ describe('Pipelines Table', () => {
});
it('tracks status badge click', () => {
- findStatusBadge().vm.$emit('ciStatusBadgeClick');
+ findCiBadgeLink().vm.$emit('ciStatusBadgeClick');
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_ci_status_badge', {
label: TRACKING_CATEGORIES.table,
diff --git a/spec/frontend/repository/commits_service_spec.js b/spec/frontend/repository/commits_service_spec.js
index de7c56f239a..b7343bf3a7e 100644
--- a/spec/frontend/repository/commits_service_spec.js
+++ b/spec/frontend/repository/commits_service_spec.js
@@ -4,6 +4,7 @@ import { loadCommits, isRequested, resetRequestedCommits } from '~/repository/co
import httpStatus from '~/lib/utils/http_status';
import { createAlert } from '~/flash';
import { I18N_COMMIT_DATA_FETCH_ERROR } from '~/repository/constants';
+import { refWithSpecialCharMock, encodedRefWithSpecialCharMock } from './mock_data';
jest.mock('~/flash');
@@ -39,10 +40,11 @@ describe('commits service', () => {
expect(axios.get).toHaveBeenCalledWith(testUrl, { params: { format: 'json', offset } });
});
- it('encodes the path correctly', async () => {
- await requestCommits(1, 'some-project', 'with $peci@l ch@rs/');
+ it('encodes the path and ref', async () => {
+ const encodedUrl = `/some-project/-/refs/${encodedRefWithSpecialCharMock}/logs_tree/with%20%24peci%40l%20ch%40rs%2F`;
+
+ await requestCommits(1, 'some-project', 'with $peci@l ch@rs/', refWithSpecialCharMock);
- const encodedUrl = '/some-project/-/refs/main/logs_tree/with%20%24peci%40l%20ch%40rs%2F';
expect(axios.get).toHaveBeenCalledWith(encodedUrl, expect.anything());
});
diff --git a/spec/frontend/repository/mock_data.js b/spec/frontend/repository/mock_data.js
index cda47a5b0a5..c1b5f89c37f 100644
--- a/spec/frontend/repository/mock_data.js
+++ b/spec/frontend/repository/mock_data.js
@@ -87,6 +87,8 @@ export const applicationInfoMock = { gitpodEnabled: true };
export const propsMock = { path: 'some_file.js', projectPath: 'some/path' };
export const refMock = 'default-ref';
+export const refWithSpecialCharMock = 'selected-#-ref';
+export const encodedRefWithSpecialCharMock = encodeURIComponent(refWithSpecialCharMock);
export const blobControlsDataMock = {
id: '1234',
diff --git a/spec/frontend/repository/utils/ref_switcher_utils_spec.js b/spec/frontend/repository/utils/ref_switcher_utils_spec.js
index 3335059554f..4d0250fffbf 100644
--- a/spec/frontend/repository/utils/ref_switcher_utils_spec.js
+++ b/spec/frontend/repository/utils/ref_switcher_utils_spec.js
@@ -1,5 +1,6 @@
import { generateRefDestinationPath } from '~/repository/utils/ref_switcher_utils';
import setWindowLocation from 'helpers/set_window_location_helper';
+import { refWithSpecialCharMock, encodedRefWithSpecialCharMock } from '../mock_data';
const projectRootPath = 'root/Project1';
const currentRef = 'main';
@@ -19,4 +20,10 @@ describe('generateRefDestinationPath', () => {
setWindowLocation(currentPath);
expect(generateRefDestinationPath(projectRootPath, selectedRef)).toBe(result);
});
+
+ it('encodes the selected ref', () => {
+ const result = `${projectRootPath}/-/tree/${encodedRefWithSpecialCharMock}`;
+
+ expect(generateRefDestinationPath(projectRootPath, refWithSpecialCharMock)).toBe(result);
+ });
});
diff --git a/spec/frontend/users/profile/components/report_abuse_button_spec.js b/spec/frontend/users/profile/components/report_abuse_button_spec.js
new file mode 100644
index 00000000000..bd39a089473
--- /dev/null
+++ b/spec/frontend/users/profile/components/report_abuse_button_spec.js
@@ -0,0 +1,72 @@
+import { GlButton } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+
+import ReportAbuseButton from '~/users/profile/components/report_abuse_button.vue';
+import AbuseCategorySelector from '~/abuse_reports/components/abuse_category_selector.vue';
+
+describe('ReportAbuseButton', () => {
+ let wrapper;
+
+ const ACTION_PATH = '/abuse_reports/add_category';
+ const USER_ID = '1';
+ const REPORTED_FROM_URL = 'http://example.com';
+
+ const createComponent = (props) => {
+ wrapper = shallowMountExtended(ReportAbuseButton, {
+ propsData: {
+ ...props,
+ },
+ provide: {
+ formSubmitPath: ACTION_PATH,
+ userId: USER_ID,
+ reportedFromUrl: REPORTED_FROM_URL,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findReportAbuseButton = () => wrapper.findComponent(GlButton);
+ const findAbuseCategorySelector = () => wrapper.findComponent(AbuseCategorySelector);
+
+ it('renders report abuse button', () => {
+ expect(findReportAbuseButton().exists()).toBe(true);
+
+ expect(findReportAbuseButton().props()).toMatchObject({
+ category: 'primary',
+ icon: 'error',
+ });
+
+ expect(findReportAbuseButton().attributes('aria-label')).toBe(
+ wrapper.vm.$options.i18n.reportAbuse,
+ );
+ });
+
+ it('renders abuse category selector with the drawer initially closed', () => {
+ expect(findAbuseCategorySelector().exists()).toBe(true);
+
+ expect(findAbuseCategorySelector().props('showDrawer')).toBe(false);
+ });
+
+ describe('when button is clicked', () => {
+ beforeEach(async () => {
+ await findReportAbuseButton().vm.$emit('click');
+ });
+
+ it('opens the abuse category selector', () => {
+ expect(findAbuseCategorySelector().props('showDrawer')).toBe(true);
+ });
+
+ it('closes the abuse category selector', async () => {
+ await findAbuseCategorySelector().vm.$emit('close-drawer');
+
+ expect(findAbuseCategorySelector().props('showDrawer')).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap b/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap
deleted file mode 100644
index 4077564486c..00000000000
--- a/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap
+++ /dev/null
@@ -1,163 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`MRWidgetAutoMergeEnabled template should have correct elements 1`] = `
-<div
- class="mr-widget-body media gl-display-flex gl-align-items-center"
->
- <div
- class="gl-w-6 gl-h-6 gl-display-flex gl-align-self-start gl-mr-3"
- >
- <div
- class="gl-display-flex gl-m-auto"
- >
- <div
- class="gl-mr-3 gl-p-2 gl-m-0! gl-text-blue-500 gl-w-6 gl-p-2"
- >
- <div
- class="gl-rounded-full gl-relative gl-display-flex mr-widget-extension-icon"
- >
- <div
- class="gl-absolute gl-top-half gl-left-50p gl-translate-x-n50 gl-display-flex gl-m-auto"
- >
- <div
- class="gl-display-flex gl-m-auto gl-translate-y-n50"
- >
- <svg
- aria-label="Scheduled "
- class="gl-display-block gl-icon s12"
- data-qa-selector="status_scheduled_icon"
- data-testid="status-scheduled-icon"
- role="img"
- >
- <use
- href="#status-scheduled"
- />
- </svg>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
-
- <div
- class="gl-display-flex gl-w-full"
- >
- <div
- class="media-body gl-display-flex gl-align-items-center"
- >
-
- <h4
- class="gl-mr-3"
- data-testid="statusText"
- >
- Set by to be merged automatically when the pipeline succeeds
- </h4>
-
- <div
- class="gl-display-flex gl-font-size-0 gl-ml-auto gl-gap-3"
- >
- <div
- class="gl-display-flex gl-align-items-flex-start"
- >
- <div
- class="dropdown b-dropdown gl-dropdown gl-display-block gl-md-display-none! btn-group"
- lazy=""
- no-caret=""
- title="Options"
- >
- <!---->
- <button
- aria-expanded="false"
- aria-haspopup="true"
- class="btn dropdown-toggle btn-default btn-sm gl-p-2! gl-button gl-dropdown-toggle btn-default-tertiary dropdown-icon-only dropdown-toggle-no-caret"
- type="button"
- >
- <!---->
-
- <svg
- aria-hidden="true"
- class="dropdown-icon gl-icon s16"
- data-testid="ellipsis_v-icon"
- role="img"
- >
- <use
- href="#ellipsis_v"
- />
- </svg>
-
- <span
- class="gl-dropdown-button-text gl-sr-only"
- >
-
- </span>
-
- <svg
- aria-hidden="true"
- class="gl-button-icon dropdown-chevron gl-icon s16"
- data-testid="chevron-down-icon"
- role="img"
- >
- <use
- href="#chevron-down"
- />
- </svg>
- </button>
- <ul
- class="dropdown-menu dropdown-menu-right"
- role="menu"
- tabindex="-1"
- >
- <!---->
- </ul>
- </div>
-
- <button
- class="btn gl-display-none gl-md-display-block gl-float-left btn-confirm btn-sm gl-button btn-confirm-tertiary js-cancel-auto-merge"
- data-qa-selector="cancel_auto_merge_button"
- data-testid="cancelAutomaticMergeButton"
- type="button"
- >
- <!---->
-
- <!---->
-
- <span
- class="gl-button-text"
- >
-
- Cancel auto-merge
-
- </span>
- </button>
- </div>
- </div>
- </div>
-
- <div
- class="gl-md-display-none gl-border-l-1 gl-border-l-solid gl-border-gray-100 gl-ml-3 gl-pl-3 gl-h-6 gl-mt-1"
- >
- <button
- class="btn gl-vertical-align-top btn-default btn-sm gl-button btn-default-tertiary btn-icon"
- title="Collapse merge details"
- type="button"
- >
- <!---->
-
- <svg
- aria-hidden="true"
- class="gl-button-icon gl-icon s16"
- data-testid="chevron-lg-up-icon"
- role="img"
- >
- <use
- href="#chevron-lg-up"
- />
- </svg>
-
- <!---->
- </button>
- </div>
- </div>
-</div>
-`;
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled_spec.js
index 5b9f30dfb86..fef5fee5f19 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled_spec.js
@@ -128,14 +128,6 @@ describe('MRWidgetAutoMergeEnabled', () => {
});
describe('template', () => {
- it('should have correct elements', () => {
- factory({
- ...defaultMrProps(),
- });
-
- expect(wrapper.element).toMatchSnapshot();
- });
-
it('should disable cancel auto merge button when the action is in progress', async () => {
factory({
...defaultMrProps(),
diff --git a/spec/frontend/vue_shared/components/ci_badge_link_spec.js b/spec/frontend/vue_shared/components/ci_badge_link_spec.js
index 07cbfe1e79b..4f24ec2d015 100644
--- a/spec/frontend/vue_shared/components/ci_badge_link_spec.js
+++ b/spec/frontend/vue_shared/components/ci_badge_link_spec.js
@@ -1,6 +1,6 @@
import { GlLink } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
-import CiBadge from '~/vue_shared/components/ci_badge_link.vue';
+import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
jest.mock('~/lib/utils/url_utility', () => ({
@@ -79,7 +79,7 @@ describe('CI Badge Link Component', () => {
const findIcon = () => wrapper.findComponent(CiIcon);
const createComponent = (propsData) => {
- wrapper = shallowMount(CiBadge, { propsData });
+ wrapper = shallowMount(CiBadgeLink, { propsData });
};
afterEach(() => {
diff --git a/spec/graphql/types/description_version_type_spec.rb b/spec/graphql/types/description_version_type_spec.rb
new file mode 100644
index 00000000000..36bb1af7f7b
--- /dev/null
+++ b/spec/graphql/types/description_version_type_spec.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['DescriptionVersion'], feature_category: :team_planning do
+ it { expect(described_class).to have_graphql_field(:id) }
+ it { expect(described_class).to have_graphql_field(:description) }
+
+ specify { expect(described_class).to require_graphql_authorizations(:read_issuable) }
+end
diff --git a/spec/graphql/types/notes/note_type_spec.rb b/spec/graphql/types/notes/note_type_spec.rb
index cbf7f086dbe..dd364be5ae2 100644
--- a/spec/graphql/types/notes/note_type_spec.rb
+++ b/spec/graphql/types/notes/note_type_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe GitlabSchema.types['Note'] do
+RSpec.describe GitlabSchema.types['Note'], feature_category: :team_planning do
it 'exposes the expected fields' do
expected_fields = %i[
author
@@ -24,6 +24,7 @@ RSpec.describe GitlabSchema.types['Note'] do
updated_at
user_permissions
url
+ system_note_metadata
]
expect(described_class).to have_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/notes/system_note_metadata_type_spec.rb b/spec/graphql/types/notes/system_note_metadata_type_spec.rb
new file mode 100644
index 00000000000..d243e926ff5
--- /dev/null
+++ b/spec/graphql/types/notes/system_note_metadata_type_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['SystemNoteMetadata'], feature_category: :team_planning do
+ it { expect(described_class).to have_graphql_field(:id) }
+ it { expect(described_class).to have_graphql_field(:action) }
+ it { expect(described_class).to have_graphql_field(:description_version) }
+
+ specify { expect(described_class).to require_graphql_authorizations(:read_note) }
+end
diff --git a/spec/helpers/nav_helper_spec.rb b/spec/helpers/nav_helper_spec.rb
index 4a37e17fb08..adf784360c2 100644
--- a/spec/helpers/nav_helper_spec.rb
+++ b/spec/helpers/nav_helper_spec.rb
@@ -134,4 +134,62 @@ RSpec.describe NavHelper do
it { is_expected.to eq(true) }
end
end
+
+ describe '#show_super_sidebar?' do
+ shared_examples '#show_super_sidebar returns false' do
+ it 'returns false' do
+ expect(helper.show_super_sidebar?).to eq(false)
+ end
+ end
+
+ it 'returns false by default' do
+ allow(helper).to receive(:current_user).and_return(nil)
+
+ expect(helper.show_super_sidebar?).to be_falsy
+ end
+
+ context 'when used is signed-in' do
+ let_it_be(:user) { create(:user) }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ stub_feature_flags(super_sidebar_nav: new_nav_ff)
+ user.update!(use_new_navigation: user_preference)
+ end
+
+ context 'with feature flag off' do
+ let(:new_nav_ff) { false }
+
+ context 'when user has new nav disabled' do
+ let(:user_preference) { false }
+
+ it_behaves_like '#show_super_sidebar returns false'
+ end
+
+ context 'when user has new nav enabled' do
+ let(:user_preference) { true }
+
+ it_behaves_like '#show_super_sidebar returns false'
+ end
+ end
+
+ context 'with feature flag on' do
+ let(:new_nav_ff) { true }
+
+ context 'when user has new nav disabled' do
+ let(:user_preference) { false }
+
+ it_behaves_like '#show_super_sidebar returns false'
+ end
+
+ context 'when user has new nav enabled' do
+ let(:user_preference) { true }
+
+ it 'returns true' do
+ expect(helper.show_super_sidebar?).to eq(true)
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/config/entry/cache_spec.rb b/spec/lib/gitlab/ci/config/entry/cache_spec.rb
index 414cbb169b9..67252eed938 100644
--- a/spec/lib/gitlab/ci/config/entry/cache_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/cache_spec.rb
@@ -16,12 +16,14 @@ RSpec.describe Gitlab::Ci::Config::Entry::Cache do
let(:policy) { nil }
let(:key) { 'some key' }
let(:when_config) { nil }
+ let(:unprotect) { false }
let(:config) do
{
key: key,
untracked: true,
- paths: ['some/path/']
+ paths: ['some/path/'],
+ unprotect: unprotect
}.tap do |config|
config[:policy] = policy if policy
config[:when] = when_config if when_config
@@ -31,7 +33,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Cache do
describe '#value' do
shared_examples 'hash key value' do
it 'returns hash value' do
- expect(entry.value).to eq(key: key, untracked: true, paths: ['some/path/'], policy: 'pull-push', when: 'on_success')
+ expect(entry.value).to eq(key: key, untracked: true, paths: ['some/path/'], policy: 'pull-push', when: 'on_success', unprotect: false)
end
end
@@ -57,6 +59,14 @@ RSpec.describe Gitlab::Ci::Config::Entry::Cache do
end
end
+ context 'with option `unprotect` specified' do
+ let(:unprotect) { true }
+
+ it 'returns true' do
+ expect(entry.value).to match(a_hash_including(unprotect: true))
+ end
+ end
+
context 'with `policy`' do
where(:policy, :result) do
'pull-push' | 'pull-push'
diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb
index 69c0d05dcdd..c1b9bd58d98 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -631,7 +631,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job, feature_category: :pipeline_autho
it 'overrides default config' do
expect(entry[:image].value).to eq(name: 'some_image')
- expect(entry[:cache].value).to eq([key: 'test', policy: 'pull-push', when: 'on_success'])
+ expect(entry[:cache].value).to eq([key: 'test', policy: 'pull-push', when: 'on_success', unprotect: false])
end
end
@@ -646,7 +646,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job, feature_category: :pipeline_autho
it 'uses config from default entry' do
expect(entry[:image].value).to eq 'specified'
- expect(entry[:cache].value).to eq([key: 'test', policy: 'pull-push', when: 'on_success'])
+ expect(entry[:cache].value).to eq([key: 'test', policy: 'pull-push', when: 'on_success', unprotect: false])
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb
index c40589104cd..9722609aef6 100644
--- a/spec/lib/gitlab/ci/config/entry/root_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb
@@ -127,7 +127,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
image: { name: 'image:1.0' },
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
- cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
+ cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success',
+ unprotect: false }],
job_variables: {},
root_variables_inheritance: true,
ignore: false,
@@ -142,7 +143,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
image: { name: 'image:1.0' },
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
- cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
+ cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success',
+ unprotect: false }],
job_variables: {},
root_variables_inheritance: true,
ignore: false,
@@ -158,7 +160,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
release: { name: "Release $CI_TAG_NAME", tag_name: 'v0.06', description: "./release_changelog.txt" },
image: { name: "image:1.0" },
services: [{ name: "postgres:9.1" }, { name: "mysql:5.5" }],
- cache: [{ key: "k", untracked: true, paths: ["public/"], policy: "pull-push", when: 'on_success' }],
+ cache: [{ key: "k", untracked: true, paths: ["public/"], policy: "pull-push", when: 'on_success',
+ unprotect: false }],
only: { refs: %w(branches tags) },
job_variables: { 'VAR' => { value: 'job' } },
root_variables_inheritance: true,
@@ -206,7 +209,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
image: { name: 'image:1.0' },
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
- cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
+ cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success', unprotect: false }],
job_variables: {},
root_variables_inheritance: true,
ignore: false,
@@ -219,7 +222,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
image: { name: 'image:1.0' },
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
- cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
+ cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success', unprotect: false }],
job_variables: { 'VAR' => { value: 'job' } },
root_variables_inheritance: true,
ignore: false,
@@ -274,7 +277,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
describe '#cache_value' do
it 'returns correct cache definition' do
- expect(root.cache_value).to eq([key: 'a', policy: 'pull-push', when: 'on_success'])
+ expect(root.cache_value).to eq([key: 'a', policy: 'pull-push', when: 'on_success', unprotect: false])
end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb
index fb8020bf43e..c264ea3bece 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/build/cache_spec.rb
@@ -212,6 +212,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build::Cache do
paths: ['vendor/ruby'],
untracked: true,
policy: 'push',
+ unprotect: true,
when: 'on_success'
}
end
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index ae98d2e0cad..41c51340eb6 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -1699,7 +1699,8 @@ module Gitlab
untracked: true,
key: 'key',
policy: 'pull-push',
- when: 'on_success'
+ when: 'on_success',
+ unprotect: false
])
end
@@ -1723,7 +1724,8 @@ module Gitlab
untracked: true,
key: { files: ['file'] },
policy: 'pull-push',
- when: 'on_success'
+ when: 'on_success',
+ unprotect: false
])
end
@@ -1749,14 +1751,16 @@ module Gitlab
untracked: true,
key: 'keya',
policy: 'pull-push',
- when: 'on_success'
+ when: 'on_success',
+ unprotect: false
},
{
paths: ['logs/', 'binaries/'],
untracked: true,
key: 'key',
policy: 'pull-push',
- when: 'on_success'
+ when: 'on_success',
+ unprotect: false
}
]
)
@@ -1783,7 +1787,8 @@ module Gitlab
untracked: true,
key: { files: ['file'] },
policy: 'pull-push',
- when: 'on_success'
+ when: 'on_success',
+ unprotect: false
])
end
@@ -1808,7 +1813,8 @@ module Gitlab
untracked: true,
key: { files: ['file'], prefix: 'prefix' },
policy: 'pull-push',
- when: 'on_success'
+ when: 'on_success',
+ unprotect: false
])
end
@@ -1831,7 +1837,8 @@ module Gitlab
untracked: false,
key: 'local',
policy: 'pull-push',
- when: 'on_success'
+ when: 'on_success',
+ unprotect: false
])
end
end
diff --git a/spec/models/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb
index 3871b18fdd5..32cf3b4c505 100644
--- a/spec/models/abuse_report_spec.rb
+++ b/spec/models/abuse_report_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe AbuseReport do
+RSpec.describe AbuseReport, feature_category: :insider_threat do
let_it_be(:report, reload: true) { create(:abuse_report) }
let_it_be(:user, reload: true) { create(:admin) }
@@ -24,6 +24,7 @@ RSpec.describe AbuseReport do
it { is_expected.to validate_presence_of(:user) }
it { is_expected.to validate_presence_of(:message) }
it { is_expected.to validate_uniqueness_of(:user_id).with_message('has already been reported') }
+ it { is_expected.to validate_presence_of(:category) }
end
describe '#remove_user' do
@@ -54,4 +55,21 @@ RSpec.describe AbuseReport do
report.notify
end
end
+
+ describe 'enums' do
+ let(:categories) do
+ {
+ spam: 1,
+ offensive: 2,
+ phishing: 3,
+ crypto: 4,
+ credentials: 5,
+ copyright: 6,
+ malware: 7,
+ other: 8
+ }
+ end
+
+ it { is_expected.to define_enum_for(:category).with_values(**categories) }
+ end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index a9b322a1a16..534875a9bba 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -1136,6 +1136,19 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration do
it do
is_expected.to all(a_hash_including(key: a_string_matching(/-protected$/)))
end
+
+ context 'and the cache has the `unprotect` option' do
+ let(:options) do
+ { cache: [
+ { key: "key", paths: ["public"], policy: "pull-push", unprotect: true },
+ { key: "key2", paths: ["public"], policy: "pull-push", unprotect: true }
+ ] }
+ end
+
+ it do
+ is_expected.to all(a_hash_including(key: a_string_matching(/-non_protected$/)))
+ end
+ end
end
context 'when pipeline is not on a protected ref' do
diff --git a/spec/requests/abuse_reports_controller_spec.rb b/spec/requests/abuse_reports_controller_spec.rb
index 510855d95e0..71ecf8444bf 100644
--- a/spec/requests/abuse_reports_controller_spec.rb
+++ b/spec/requests/abuse_reports_controller_spec.rb
@@ -40,6 +40,80 @@ RSpec.describe AbuseReportsController, feature_category: :users do
end
end
+ describe 'POST add_category', :aggregate_failures do
+ subject(:request) { post add_category_abuse_reports_path, params: request_params }
+
+ let(:abuse_category) { 'spam' }
+
+ context 'when user is reported for abuse' do
+ let(:ref_url) { 'http://example.com' }
+ let(:request_params) { { user_id: user.id, abuse_report: { category: abuse_category }, ref_url: ref_url } }
+
+ it 'renders new template' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template(:new)
+ end
+
+ it 'sets the instance variables' do
+ subject
+
+ expect(assigns(:abuse_report)).to be_kind_of(AbuseReport)
+ expect(assigns(:abuse_report)).to have_attributes(
+ user_id: user.id,
+ category: abuse_category
+ )
+ expect(assigns(:ref_url)).to eq(ref_url)
+ end
+ end
+
+ context 'when abuse_report is missing in params' do
+ let(:request_params) { { user_id: user.id } }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(ActionController::ParameterMissing)
+ end
+ end
+
+ context 'when user_id is missing in params' do
+ let(:request_params) { { abuse_report: { category: abuse_category } } }
+
+ it 'redirects the reporter to root_path' do
+ subject
+
+ expect(response).to redirect_to root_path
+ expect(flash[:alert]).to eq(_('Cannot create the abuse report. The user has been deleted.'))
+ end
+ end
+
+ context 'when the user has already been deleted' do
+ let(:request_params) { { user_id: user.id, abuse_report: { category: abuse_category } } }
+
+ it 'redirects the reporter to root_path' do
+ user.destroy!
+
+ subject
+
+ expect(response).to redirect_to root_path
+ expect(flash[:alert]).to eq(_('Cannot create the abuse report. The user has been deleted.'))
+ end
+ end
+
+ context 'when the user has already been blocked' do
+ let(:request_params) { { user_id: user.id, abuse_report: { category: abuse_category } } }
+
+ it 'redirects the reporter to the user\'s profile' do
+ user.block
+
+ subject
+
+ expect(response).to redirect_to user
+ expect(flash[:alert]).to eq(_('Cannot create the abuse report. This user has been blocked.'))
+ end
+ end
+ end
+
describe 'POST create' do
context 'with valid attributes' do
it 'saves the abuse report' do
diff --git a/spec/requests/api/debian_project_packages_spec.rb b/spec/requests/api/debian_project_packages_spec.rb
index c27e165b39b..5258d26be17 100644
--- a/spec/requests/api/debian_project_packages_spec.rb
+++ b/spec/requests/api/debian_project_packages_spec.rb
@@ -5,7 +5,17 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
include HttpBasicAuthHelpers
include WorkhorseHelpers
- include_context 'Debian repository shared context', :project, true do
+ include_context 'Debian repository shared context', :project, false do
+ shared_examples 'accept GET request on private project with access to package registry for everyone' do
+ include_context 'Debian repository access', :private, :anonymous, :basic do
+ before do
+ container.project_feature.reload.update!(package_registry_access_level: ProjectFeature::PUBLIC)
+ end
+
+ it_behaves_like 'Debian packages GET request', :success
+ end
+ end
+
context 'with invalid parameter' do
let(:url) { "/projects/1/packages/debian/dists/with+space/InRelease" }
@@ -16,54 +26,63 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/Release.gpg" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^-----BEGIN PGP SIGNATURE-----/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
describe 'GET projects/:id/packages/debian/dists/*distribution/Release' do
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/Release" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Codename: fixture-distribution\n$/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
describe 'GET projects/:id/packages/debian/dists/*distribution/InRelease' do
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/InRelease" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^-----BEGIN PGP SIGNED MESSAGE-----/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/binary-:architecture/Packages' do
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/binary-#{architecture.name}/Packages" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete Packages file/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/binary-:architecture/by-hash/SHA256/:file_sha256' do
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/binary-#{architecture.name}/by-hash/SHA256/#{component_file_older_sha256.file_sha256}" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
- describe 'GET projects/:id/packages/debian/dists/*distribution/source/Sources' do
+ describe 'GET projects/:id/packages/debian/dists/*distribution/:component/source/Sources' do
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/source/Sources" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete Sources file/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
- describe 'GET projects/:id/packages/debian/dists/*distribution/source/by-hash/SHA256/:file_sha256' do
+ describe 'GET projects/:id/packages/debian/dists/*distribution/:component/source/by-hash/SHA256/:file_sha256' do
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/source/by-hash/SHA256/#{component_file_sources_older_sha256.file_sha256}" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/Packages' do
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/debian-installer/binary-#{architecture.name}/Packages" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete D-I Packages file/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/by-hash/SHA256/:file_sha256' do
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/debian-installer/binary-#{architecture.name}/by-hash/SHA256/#{component_file_di_older_sha256.file_sha256}" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
describe 'GET projects/:id/packages/debian/pool/:codename/:letter/:package_name/:package_version/:file_name' do
@@ -90,6 +109,10 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
end
end
end
+
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone' do
+ let(:file_name) { 'sample_1.2.3~alpha2.dsc' }
+ end
end
describe 'PUT projects/:id/packages/debian/:file_name' do
diff --git a/spec/requests/api/graphql/project/work_items_spec.rb b/spec/requests/api/graphql/project/work_items_spec.rb
index a59da706a8a..de35c943749 100644
--- a/spec/requests/api/graphql/project/work_items_spec.rb
+++ b/spec/requests/api/graphql/project/work_items_spec.rb
@@ -263,7 +263,7 @@ RSpec.describe 'getting a work item list for a project', feature_category: :team
GRAPHQL
end
- before do
+ before_all do
create_notes(item1, "some note1")
create_notes(item2, "some note2")
end
diff --git a/spec/services/ci/create_pipeline_service/cache_spec.rb b/spec/services/ci/create_pipeline_service/cache_spec.rb
index 82c3d374636..f9640f99031 100644
--- a/spec/services/ci/create_pipeline_service/cache_spec.rb
+++ b/spec/services/ci/create_pipeline_service/cache_spec.rb
@@ -37,6 +37,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
paths: ['logs/', 'binaries/'],
policy: 'pull-push',
untracked: true,
+ unprotect: false,
when: 'on_success'
}
@@ -69,7 +70,8 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
key: /[a-f0-9]{40}/,
paths: ['logs/'],
policy: 'pull-push',
- when: 'on_success'
+ when: 'on_success',
+ unprotect: false
}
expect(pipeline).to be_persisted
@@ -85,7 +87,8 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
key: /default/,
paths: ['logs/'],
policy: 'pull-push',
- when: 'on_success'
+ when: 'on_success',
+ unprotect: false
}
expect(pipeline).to be_persisted
@@ -118,7 +121,8 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
key: /\$ENV_VAR-[a-f0-9]{40}/,
paths: ['logs/'],
policy: 'pull-push',
- when: 'on_success'
+ when: 'on_success',
+ unprotect: false
}
expect(pipeline).to be_persisted
@@ -134,7 +138,8 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
key: /\$ENV_VAR-default/,
paths: ['logs/'],
policy: 'pull-push',
- when: 'on_success'
+ when: 'on_success',
+ unprotect: false
}
expect(pipeline).to be_persisted