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>2023-05-23 00:08:01 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-05-23 00:08:01 +0300
commitc50e042a392687730db9b8c2607883485b258ae4 (patch)
tree519b069aa0a400241a2f8dc0f900f09625e3d8ed /spec
parent7e2f555a6dc37839727dee130d8ed4421b680d42 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/nav/pinned_nav_items_spec.rb8
-rw-r--r--spec/frontend/ci/runner/components/cells/runner_status_cell_spec.js4
-rw-r--r--spec/frontend/ci/runner/components/runner_delete_button_spec.js2
-rw-r--r--spec/frontend/ci/runner/components/runner_list_spec.js2
-rw-r--r--spec/frontend/ci/runner/components/runner_pause_button_spec.js40
-rw-r--r--spec/frontend/ci/runner/components/runner_update_form_spec.js6
-rw-r--r--spec/frontend/ci/runner/runner_update_form_utils_spec.js4
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/details/package_files_spec.js206
-rw-r--r--spec/frontend/packages_and_registries/package_registry/mock_data.js15
-rw-r--r--spec/frontend/packages_and_registries/package_registry/pages/details_spec.js12
-rw-r--r--spec/graphql/resolvers/users/participants_resolver_spec.rb67
-rw-r--r--spec/lib/sidebars/groups/super_sidebar_menus/deploy_menu_spec.rb21
-rw-r--r--spec/lib/sidebars/groups/super_sidebar_menus/operations_menu_spec.rb3
-rw-r--r--spec/lib/sidebars/groups/super_sidebar_panel_spec.rb1
-rw-r--r--spec/lib/sidebars/projects/super_sidebar_menus/analyze_menu_spec.rb3
-rw-r--r--spec/lib/sidebars/projects/super_sidebar_menus/build_menu_spec.rb3
-rw-r--r--spec/lib/sidebars/projects/super_sidebar_menus/deploy_menu_spec.rb25
-rw-r--r--spec/lib/sidebars/projects/super_sidebar_menus/operations_menu_spec.rb5
-rw-r--r--spec/lib/sidebars/projects/super_sidebar_panel_spec.rb1
-rw-r--r--spec/models/integrations/jira_spec.rb12
-rw-r--r--spec/models/integrations/pipelines_email_spec.rb19
-rw-r--r--spec/requests/api/v3/github_spec.rb23
-rw-r--r--spec/support/shared_examples/models/concerns/participable_shared_examples.rb11
23 files changed, 308 insertions, 185 deletions
diff --git a/spec/features/nav/pinned_nav_items_spec.rb b/spec/features/nav/pinned_nav_items_spec.rb
index 308350d5166..cf53e0a322a 100644
--- a/spec/features/nav/pinned_nav_items_spec.rb
+++ b/spec/features/nav/pinned_nav_items_spec.rb
@@ -89,7 +89,7 @@ RSpec.describe 'Navigation menu item pinning', :js, feature_category: :navigatio
before do
within '#super-sidebar' do
click_on 'Operate'
- add_pin('Package Registry')
+ add_pin('Terraform states')
add_pin('Terraform modules')
wait_for_requests
end
@@ -97,8 +97,8 @@ RSpec.describe 'Navigation menu item pinning', :js, feature_category: :navigatio
it 'can be unpinned from within the pinned section' do
within '[data-testid="pinned-nav-items"]' do
- remove_pin('Package Registry')
- expect(page).not_to have_content 'Package Registry'
+ remove_pin('Terraform states')
+ expect(page).not_to have_content 'Terraform states'
end
end
@@ -117,7 +117,7 @@ RSpec.describe 'Navigation menu item pinning', :js, feature_category: :navigatio
it 'can be reordered' do
within '[data-testid="pinned-nav-items"]' do
pinned_items = page.find_all('a').map(&:text)
- item2 = page.find('a', text: 'Package Registry')
+ item2 = page.find('a', text: 'Terraform states')
item3 = page.find('a', text: 'Terraform modules')
expect(pinned_items[1..2]).to eq [item2.text, item3.text]
drag_item(item3, to: item2)
diff --git a/spec/frontend/ci/runner/components/cells/runner_status_cell_spec.js b/spec/frontend/ci/runner/components/cells/runner_status_cell_spec.js
index c435dd57de2..88d4398aa70 100644
--- a/spec/frontend/ci/runner/components/cells/runner_status_cell_spec.js
+++ b/spec/frontend/ci/runner/components/cells/runner_status_cell_spec.js
@@ -24,7 +24,7 @@ describe('RunnerStatusCell', () => {
propsData: {
runner: {
runnerType: INSTANCE_TYPE,
- active: true,
+ paused: false,
status: STATUS_ONLINE,
jobExecutionStatus: JOB_STATUS_IDLE,
...runner,
@@ -59,7 +59,7 @@ describe('RunnerStatusCell', () => {
it('Displays paused status', () => {
createComponent({
runner: {
- active: false,
+ paused: true,
status: STATUS_ONLINE,
},
});
diff --git a/spec/frontend/ci/runner/components/runner_delete_button_spec.js b/spec/frontend/ci/runner/components/runner_delete_button_spec.js
index 3123f2894fb..3b3f3b1770d 100644
--- a/spec/frontend/ci/runner/components/runner_delete_button_spec.js
+++ b/spec/frontend/ci/runner/components/runner_delete_button_spec.js
@@ -236,7 +236,7 @@ describe('RunnerDeleteButton', () => {
createComponent({
props: {
runner: {
- active: true,
+ paused: false,
},
compact: true,
},
diff --git a/spec/frontend/ci/runner/components/runner_list_spec.js b/spec/frontend/ci/runner/components/runner_list_spec.js
index 0f4ec717c3e..9da640afeb7 100644
--- a/spec/frontend/ci/runner/components/runner_list_spec.js
+++ b/spec/frontend/ci/runner/components/runner_list_spec.js
@@ -18,7 +18,6 @@ import { I18N_PROJECT_TYPE, I18N_STATUS_NEVER_CONTACTED } from '~/ci/runner/cons
import { allRunnersData, onlineContactTimeoutSecs, staleTimeoutSecs } from '../mock_data';
const mockRunners = allRunnersData.data.runners.nodes;
-const mockActiveRunnersCount = mockRunners.length;
describe('RunnerList', () => {
let wrapper;
@@ -44,7 +43,6 @@ describe('RunnerList', () => {
apolloProvider: createMockApollo([], {}, cacheConfig),
propsData: {
runners: mockRunners,
- activeRunnersCount: mockActiveRunnersCount,
...props,
},
provide: {
diff --git a/spec/frontend/ci/runner/components/runner_pause_button_spec.js b/spec/frontend/ci/runner/components/runner_pause_button_spec.js
index 350d029f3fc..1ea870e004a 100644
--- a/spec/frontend/ci/runner/components/runner_pause_button_spec.js
+++ b/spec/frontend/ci/runner/components/runner_pause_button_spec.js
@@ -4,7 +4,7 @@ import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
-import runnerToggleActiveMutation from '~/ci/runner/graphql/shared/runner_toggle_active.mutation.graphql';
+import runnerTogglePausedMutation from '~/ci/runner/graphql/shared/runner_toggle_paused.mutation.graphql';
import waitForPromises from 'helpers/wait_for_promises';
import { captureException } from '~/ci/runner/sentry_utils';
import { createAlert } from '~/alert';
@@ -27,7 +27,7 @@ jest.mock('~/ci/runner/sentry_utils');
describe('RunnerPauseButton', () => {
let wrapper;
- let runnerToggleActiveHandler;
+ let runnerTogglePausedHandler;
const getTooltip = () => getBinding(wrapper.element, 'gl-tooltip').value;
const findBtn = () => wrapper.findComponent(GlButton);
@@ -39,12 +39,12 @@ describe('RunnerPauseButton', () => {
propsData: {
runner: {
id: mockRunner.id,
- active: mockRunner.active,
+ paused: mockRunner.paused,
...runner,
},
...propsData,
},
- apolloProvider: createMockApollo([[runnerToggleActiveMutation, runnerToggleActiveHandler]]),
+ apolloProvider: createMockApollo([[runnerTogglePausedMutation, runnerTogglePausedHandler]]),
directives: {
GlTooltip: createMockDirective('gl-tooltip'),
},
@@ -57,13 +57,13 @@ describe('RunnerPauseButton', () => {
};
beforeEach(() => {
- runnerToggleActiveHandler = jest.fn().mockImplementation(({ input }) => {
+ runnerTogglePausedHandler = jest.fn().mockImplementation(({ input }) => {
return Promise.resolve({
data: {
runnerUpdate: {
runner: {
id: input.id,
- active: input.active,
+ paused: !input.paused,
},
errors: [],
},
@@ -76,15 +76,15 @@ describe('RunnerPauseButton', () => {
describe('Pause/Resume action', () => {
describe.each`
- runnerState | icon | content | tooltip | isActive | newActiveValue
- ${'paused'} | ${'play'} | ${I18N_RESUME} | ${I18N_RESUME_TOOLTIP} | ${false} | ${true}
- ${'active'} | ${'pause'} | ${I18N_PAUSE} | ${I18N_PAUSE_TOOLTIP} | ${true} | ${false}
- `('When the runner is $runnerState', ({ icon, content, tooltip, isActive, newActiveValue }) => {
+ runnerState | icon | content | tooltip | isPaused | newPausedValue
+ ${'paused'} | ${'play'} | ${I18N_RESUME} | ${I18N_RESUME_TOOLTIP} | ${true} | ${false}
+ ${'active'} | ${'pause'} | ${I18N_PAUSE} | ${I18N_PAUSE_TOOLTIP} | ${false} | ${true}
+ `('When the runner is $runnerState', ({ icon, content, tooltip, isPaused, newPausedValue }) => {
beforeEach(() => {
createComponent({
props: {
runner: {
- active: isActive,
+ paused: isPaused,
},
},
});
@@ -106,7 +106,7 @@ describe('RunnerPauseButton', () => {
describe(`Before the ${icon} button is clicked`, () => {
it('The mutation has not been called', () => {
- expect(runnerToggleActiveHandler).toHaveBeenCalledTimes(0);
+ expect(runnerTogglePausedHandler).not.toHaveBeenCalled();
});
});
@@ -134,12 +134,12 @@ describe('RunnerPauseButton', () => {
await clickAndWait();
});
- it(`The mutation to that sets active to ${newActiveValue} is called`, () => {
- expect(runnerToggleActiveHandler).toHaveBeenCalledTimes(1);
- expect(runnerToggleActiveHandler).toHaveBeenCalledWith({
+ it(`The mutation to that sets "paused" to ${newPausedValue} is called`, () => {
+ expect(runnerTogglePausedHandler).toHaveBeenCalledTimes(1);
+ expect(runnerTogglePausedHandler).toHaveBeenCalledWith({
input: {
id: mockRunner.id,
- active: newActiveValue,
+ paused: newPausedValue,
},
});
});
@@ -158,7 +158,7 @@ describe('RunnerPauseButton', () => {
const mockErrorMsg = 'Update error!';
beforeEach(async () => {
- runnerToggleActiveHandler.mockRejectedValueOnce(new Error(mockErrorMsg));
+ runnerTogglePausedHandler.mockRejectedValueOnce(new Error(mockErrorMsg));
await clickAndWait();
});
@@ -180,12 +180,12 @@ describe('RunnerPauseButton', () => {
const mockErrorMsg2 = 'User not allowed!';
beforeEach(async () => {
- runnerToggleActiveHandler.mockResolvedValueOnce({
+ runnerTogglePausedHandler.mockResolvedValueOnce({
data: {
runnerUpdate: {
runner: {
id: mockRunner.id,
- active: isActive,
+ paused: isPaused,
},
errors: [mockErrorMsg, mockErrorMsg2],
},
@@ -215,7 +215,7 @@ describe('RunnerPauseButton', () => {
createComponent({
props: {
runner: {
- active: true,
+ paused: false,
},
compact: true,
},
diff --git a/spec/frontend/ci/runner/components/runner_update_form_spec.js b/spec/frontend/ci/runner/components/runner_update_form_spec.js
index ee37d6241b5..d1d4e38f47c 100644
--- a/spec/frontend/ci/runner/components/runner_update_form_spec.js
+++ b/spec/frontend/ci/runner/components/runner_update_form_spec.js
@@ -56,7 +56,7 @@ describe('RunnerUpdateForm', () => {
const submitFormAndWait = () => submitForm().then(waitForPromises);
const getFieldsModel = () => ({
- active: !findPausedCheckbox().element.checked,
+ paused: findPausedCheckbox().element.checked,
accessLevel: findProtectedCheckbox().element.checked
? ACCESS_LEVEL_REF_PROTECTED
: ACCESS_LEVEL_NOT_PROTECTED,
@@ -179,8 +179,8 @@ describe('RunnerUpdateForm', () => {
describe('On submit, runner gets updated', () => {
it.each`
test | initialValue | findCheckbox | checked | submitted
- ${'pauses'} | ${{ active: true }} | ${findPausedCheckbox} | ${true} | ${{ active: false }}
- ${'activates'} | ${{ active: false }} | ${findPausedCheckbox} | ${false} | ${{ active: true }}
+ ${'pauses'} | ${{ paused: false }} | ${findPausedCheckbox} | ${true} | ${{ paused: true }}
+ ${'activates'} | ${{ paused: true }} | ${findPausedCheckbox} | ${false} | ${{ paused: false }}
${'unprotects'} | ${{ accessLevel: ACCESS_LEVEL_NOT_PROTECTED }} | ${findProtectedCheckbox} | ${true} | ${{ accessLevel: ACCESS_LEVEL_REF_PROTECTED }}
${'protects'} | ${{ accessLevel: ACCESS_LEVEL_REF_PROTECTED }} | ${findProtectedCheckbox} | ${false} | ${{ accessLevel: ACCESS_LEVEL_NOT_PROTECTED }}
${'"runs untagged jobs"'} | ${{ runUntagged: true }} | ${findRunUntaggedCheckbox} | ${false} | ${{ runUntagged: false }}
diff --git a/spec/frontend/ci/runner/runner_update_form_utils_spec.js b/spec/frontend/ci/runner/runner_update_form_utils_spec.js
index b2f7bbc49a9..80c492bb431 100644
--- a/spec/frontend/ci/runner/runner_update_form_utils_spec.js
+++ b/spec/frontend/ci/runner/runner_update_form_utils_spec.js
@@ -12,7 +12,7 @@ const mockRunner = {
description: mockDescription,
maximumTimeout: 100,
accessLevel: ACCESS_LEVEL_NOT_PROTECTED,
- active: true,
+ paused: false,
locked: true,
runUntagged: true,
tagList: ['tag-1', 'tag-2'],
@@ -79,7 +79,7 @@ describe('~/ci/runner/runner_update_form_utils', () => {
${',,,,, commas'} | ${['commas']}
${'more ,,,,, commas'} | ${['more', 'commas']}
${' trimmed , trimmed2 '} | ${['trimmed', 'trimmed2']}
- `('collect tags separated by commas for "$value"', ({ tagList, tagListInput }) => {
+ `('collect comma-separated tags "$tagList" as $tagListInput', ({ tagList, tagListInput }) => {
const variables = modelToUpdateMutationVariables({
...mockModel,
tagList,
diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/package_files_spec.js b/spec/frontend/packages_and_registries/package_registry/components/details/package_files_spec.js
index 1dcac017ccf..5c36dbf9c9c 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/details/package_files_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/components/details/package_files_spec.js
@@ -1,22 +1,34 @@
-import { GlDropdown, GlButton, GlFormCheckbox } from '@gitlab/ui';
-import { nextTick } from 'vue';
+import { GlAlert, GlDropdown, GlButton, GlFormCheckbox, GlLoadingIcon } from '@gitlab/ui';
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
import { stubComponent } from 'helpers/stub_component';
import { mountExtended, extendedWrapper } from 'helpers/vue_test_utils_helper';
-import { packageFiles as packageFilesMock } from 'jest/packages_and_registries/package_registry/mock_data';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import { s__ } from '~/locale';
+import {
+ packageFiles as packageFilesMock,
+ packageFilesQuery,
+} from 'jest/packages_and_registries/package_registry/mock_data';
import PackageFiles from '~/packages_and_registries/package_registry/components/details/package_files.vue';
import FileIcon from '~/vue_shared/components/file_icon.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import getPackageFiles from '~/packages_and_registries/package_registry/graphql/queries/get_package_files.query.graphql';
+
+Vue.use(VueApollo);
+
describe('Package Files', () => {
let wrapper;
+ let apolloProvider;
const findAllRows = () => wrapper.findAllByTestId('file-row');
const findDeleteSelectedButton = () => wrapper.findByTestId('delete-selected');
const findFirstRow = () => extendedWrapper(findAllRows().at(0));
const findSecondRow = () => extendedWrapper(findAllRows().at(1));
+ const findPackageFilesAlert = () => wrapper.findComponent(GlAlert);
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findFirstRowDownloadLink = () => findFirstRow().findByTestId('download-link');
- const findFirstRowCommitLink = () => findFirstRow().findByTestId('commit-link');
- const findSecondRowCommitLink = () => findSecondRow().findByTestId('commit-link');
const findFirstRowFileIcon = () => findFirstRow().findComponent(FileIcon);
const findFirstRowCreatedAt = () => findFirstRow().findComponent(TimeAgoTooltip);
const findFirstActionMenu = () => extendedWrapper(findFirstRow().findComponent(GlDropdown));
@@ -30,16 +42,23 @@ describe('Package Files', () => {
const [file] = files;
const createComponent = ({
- packageFiles = [file],
+ packageId = '1',
+ packageType = 'NPM',
isLoading = false,
canDelete = true,
stubs,
+ resolver = jest.fn().mockResolvedValue(packageFilesQuery([file])),
} = {}) => {
+ const requestHandlers = [[getPackageFiles, resolver]];
+ apolloProvider = createMockApollo(requestHandlers);
+
wrapper = mountExtended(PackageFiles, {
+ apolloProvider,
propsData: {
canDelete,
isLoading,
- packageFiles,
+ packageId,
+ packageType,
},
stubs: {
GlTable: false,
@@ -49,35 +68,61 @@ describe('Package Files', () => {
};
describe('rows', () => {
- it('renders a single file for an npm package', () => {
+ it('do not get rendered when query is loading', () => {
createComponent();
+ expect(findLoadingIcon().exists()).toBe(true);
+ expect(findDeleteSelectedButton().props('disabled')).toBe(true);
+ });
+
+ it('renders a single file for an npm package', async () => {
+ createComponent();
+ await waitForPromises();
+
expect(findAllRows()).toHaveLength(1);
+ expect(findLoadingIcon().exists()).toBe(false);
});
- it('renders multiple files for a package that contains more than one file', () => {
- createComponent({ packageFiles: files });
+ it('renders multiple files for a package that contains more than one file', async () => {
+ createComponent({ resolver: jest.fn().mockResolvedValue(packageFilesQuery()) });
+ await waitForPromises();
expect(findAllRows()).toHaveLength(2);
});
+
+ it('does not render gl-alert', async () => {
+ createComponent();
+ await waitForPromises();
+
+ expect(findPackageFilesAlert().exists()).toBe(false);
+ });
+
+ it('renders gl-alert if load fails', async () => {
+ createComponent({ resolver: jest.fn().mockRejectedValue() });
+ await waitForPromises();
+
+ expect(findPackageFilesAlert().exists()).toBe(true);
+ expect(findPackageFilesAlert().text()).toBe(
+ s__('PackageRegistry|Something went wrong while fetching package assets.'),
+ );
+ });
});
describe('link', () => {
- it('exists', () => {
+ beforeEach(async () => {
createComponent();
+ await waitForPromises();
+ });
+ it('exists', () => {
expect(findFirstRowDownloadLink().exists()).toBe(true);
});
it('has the correct attrs bound', () => {
- createComponent();
-
expect(findFirstRowDownloadLink().attributes('href')).toBe(file.downloadPath);
});
it('emits "download-file" event on click', () => {
- createComponent();
-
findFirstRowDownloadLink().vm.$emit('click');
expect(wrapper.emitted('download-file')).toEqual([[]]);
@@ -85,90 +130,43 @@ describe('Package Files', () => {
});
describe('file-icon', () => {
- it('exists', () => {
+ beforeEach(async () => {
createComponent();
+ await waitForPromises();
+ });
+ it('exists', () => {
expect(findFirstRowFileIcon().exists()).toBe(true);
});
it('has the correct props bound', () => {
- createComponent();
-
expect(findFirstRowFileIcon().props('fileName')).toBe(file.fileName);
});
});
describe('time-ago tooltip', () => {
- it('exists', () => {
+ beforeEach(async () => {
createComponent();
+ await waitForPromises();
+ });
+ it('exists', () => {
expect(findFirstRowCreatedAt().exists()).toBe(true);
});
it('has the correct props bound', () => {
- createComponent();
-
expect(findFirstRowCreatedAt().props('time')).toBe(file.createdAt);
});
});
- describe('commit', () => {
- const withPipeline = {
- ...file,
- pipelines: [
- {
- sha: 'sha',
- id: 1,
- commitPath: 'commitPath',
- },
- ],
- };
-
- describe('when package file has a pipeline associated', () => {
- it('exists', () => {
- createComponent({ packageFiles: [withPipeline] });
-
- expect(findFirstRowCommitLink().exists()).toBe(true);
- });
-
- it('the link points to the commit path', () => {
- createComponent({ packageFiles: [withPipeline] });
-
- expect(findFirstRowCommitLink().attributes('href')).toBe(
- withPipeline.pipelines[0].commitPath,
- );
- });
-
- it('the text is the pipeline sha', () => {
- createComponent({ packageFiles: [withPipeline] });
-
- expect(findFirstRowCommitLink().text()).toBe(withPipeline.pipelines[0].sha);
- });
- });
-
- describe('when package file has no pipeline associated', () => {
- it('does not exist', () => {
- createComponent();
-
- expect(findFirstRowCommitLink().exists()).toBe(false);
- });
- });
-
- describe('when only one file lacks an associated pipeline', () => {
- it('renders the commit when it exists and not otherwise', () => {
- createComponent({ packageFiles: [withPipeline, file] });
-
- expect(findFirstRowCommitLink().exists()).toBe(true);
- expect(findSecondRowCommitLink().exists()).toBe(false);
- });
- });
- });
-
describe('action menu', () => {
describe('when the user can delete', () => {
- it('exists', () => {
+ beforeEach(async () => {
createComponent();
+ await waitForPromises();
+ });
+ it('exists', () => {
expect(findFirstActionMenu().exists()).toBe(true);
expect(findFirstActionMenu().props('icon')).toBe('ellipsis_v');
expect(findFirstActionMenu().props('textSrOnly')).toBe(true);
@@ -178,14 +176,10 @@ describe('Package Files', () => {
describe('menu items', () => {
describe('delete file', () => {
it('exists', () => {
- createComponent();
-
expect(findActionMenuDelete().exists()).toBe(true);
});
it('emits a delete event when clicked', async () => {
- createComponent();
-
await findActionMenuDelete().trigger('click');
const [[items]] = wrapper.emitted('delete-files');
@@ -199,8 +193,9 @@ describe('Package Files', () => {
describe('when the user can not delete', () => {
const canDelete = false;
- it('does not exist', () => {
+ it('does not exist', async () => {
createComponent({ canDelete });
+ await waitForPromises();
expect(findFirstActionMenu().exists()).toBe(false);
});
@@ -209,22 +204,33 @@ describe('Package Files', () => {
describe('multi select', () => {
describe('when user can delete', () => {
- it('delete selected button exists & is disabled', () => {
+ it('delete selected button exists & is disabled', async () => {
createComponent();
+ await waitForPromises();
expect(findDeleteSelectedButton().exists()).toBe(true);
expect(findDeleteSelectedButton().text()).toMatchInterpolatedText('Delete selected');
expect(findDeleteSelectedButton().props('disabled')).toBe(true);
});
- it('delete selected button exists & is disabled when isLoading prop is true', () => {
- createComponent({ isLoading: true });
+ it('delete selected button exists & is disabled when isLoading prop is true', async () => {
+ createComponent();
+ await waitForPromises();
+ const first = findAllRowCheckboxes().at(0);
+
+ await first.setChecked(true);
+
+ expect(findDeleteSelectedButton().props('disabled')).toBe(false);
+
+ await wrapper.setProps({ isLoading: true });
expect(findDeleteSelectedButton().props('disabled')).toBe(true);
+ expect(findLoadingIcon().exists()).toBe(true);
});
- it('checkboxes to select file are visible', () => {
- createComponent({ packageFiles: files });
+ it('checkboxes to select file are visible', async () => {
+ createComponent({ resolver: jest.fn().mockResolvedValue(packageFilesQuery()) });
+ await waitForPromises();
expect(findCheckAllCheckbox().exists()).toBe(true);
expect(findAllRowCheckboxes()).toHaveLength(2);
@@ -232,6 +238,7 @@ describe('Package Files', () => {
it('selecting a checkbox enables delete selected button', async () => {
createComponent();
+ await waitForPromises();
const first = findAllRowCheckboxes().at(0);
@@ -244,7 +251,8 @@ describe('Package Files', () => {
it('will toggle between selecting all and deselecting all files', async () => {
const getChecked = () => findAllRowCheckboxes().filter((x) => x.element.checked === true);
- createComponent({ packageFiles: files });
+ createComponent({ resolver: jest.fn().mockResolvedValue(packageFilesQuery()) });
+ await waitForPromises();
expect(getChecked()).toHaveLength(0);
@@ -262,9 +270,10 @@ describe('Package Files', () => {
expect(findCheckAllCheckbox().props('indeterminate')).toBe(state);
createComponent({
- packageFiles: files,
+ resolver: jest.fn().mockResolvedValue(packageFilesQuery()),
stubs: { GlFormCheckbox: stubComponent(GlFormCheckbox, { props: ['indeterminate'] }) },
});
+ await waitForPromises();
expectIndeterminateState(false);
@@ -288,6 +297,7 @@ describe('Package Files', () => {
it('emits a delete event when selected', async () => {
createComponent();
+ await waitForPromises();
const first = findAllRowCheckboxes().at(0);
@@ -301,7 +311,8 @@ describe('Package Files', () => {
});
it('emits delete event with both items when all are selected', async () => {
- createComponent({ packageFiles: files });
+ createComponent({ resolver: jest.fn().mockResolvedValue(packageFilesQuery()) });
+ await waitForPromises();
await findCheckAllCheckbox().setChecked(true);
@@ -315,14 +326,16 @@ describe('Package Files', () => {
describe('when user cannot delete', () => {
const canDelete = false;
- it('delete selected button does not exist', () => {
+ it('delete selected button does not exist', async () => {
createComponent({ canDelete });
+ await waitForPromises();
expect(findDeleteSelectedButton().exists()).toBe(false);
});
- it('checkboxes to select file are not visible', () => {
- createComponent({ packageFiles: files, canDelete });
+ it('checkboxes to select file are not visible', async () => {
+ createComponent({ resolver: jest.fn().mockResolvedValue(packageFilesQuery()), canDelete });
+ await waitForPromises();
expect(findCheckAllCheckbox().exists()).toBe(false);
expect(findAllRowCheckboxes()).toHaveLength(0);
@@ -332,24 +345,27 @@ describe('Package Files', () => {
describe('additional details', () => {
describe('details toggle button', () => {
- it('exists', () => {
+ it('exists', async () => {
createComponent();
+ await waitForPromises();
expect(findFirstToggleDetailsButton().exists()).toBe(true);
});
- it('is hidden when no details is present', () => {
+ it('is hidden when no details is present', async () => {
const { ...noShaFile } = file;
noShaFile.fileSha256 = null;
noShaFile.fileMd5 = null;
noShaFile.fileSha1 = null;
- createComponent({ packageFiles: [noShaFile] });
+ createComponent({ resolver: jest.fn().mockResolvedValue(packageFilesQuery([noShaFile])) });
+ await waitForPromises();
expect(findFirstToggleDetailsButton().exists()).toBe(false);
});
it('toggles the details row', async () => {
createComponent();
+ await waitForPromises();
expect(findFirstToggleDetailsButton().props('icon')).toBe('chevron-down');
@@ -380,6 +396,7 @@ describe('Package Files', () => {
${'sha-1'} | ${'SHA-1'} | ${'be93151dc23ac34a82752444556fe79b32c7a1ad'}
`('has a $title row', async ({ selector, title, sha }) => {
createComponent();
+ await waitForPromises();
await showShaFiles();
@@ -393,7 +410,8 @@ describe('Package Files', () => {
const { ...missingMd5 } = file;
missingMd5.fileMd5 = null;
- createComponent({ packageFiles: [missingMd5] });
+ createComponent({ resolver: jest.fn().mockResolvedValue(packageFilesQuery([missingMd5])) });
+ await waitForPromises();
await showShaFiles();
diff --git a/spec/frontend/packages_and_registries/package_registry/mock_data.js b/spec/frontend/packages_and_registries/package_registry/mock_data.js
index 5fb53566d4e..fa6a69b1a1f 100644
--- a/spec/frontend/packages_and_registries/package_registry/mock_data.js
+++ b/spec/frontend/packages_and_registries/package_registry/mock_data.js
@@ -257,7 +257,7 @@ export const packageDetailsQuery = ({
pageInfo: {
hasNextPage: true,
},
- nodes: packageFiles(),
+ nodes: packageFiles().map(({ id, size }) => ({ id, size })),
__typename: 'PackageFileConnection',
},
versions: {
@@ -285,6 +285,19 @@ export const packagePipelinesQuery = (pipelines = packagePipelines()) => ({
},
});
+export const packageFilesQuery = (files = packageFiles()) => ({
+ data: {
+ package: {
+ id: 'gid://gitlab/Packages::Package/111',
+ packageFiles: {
+ nodes: files,
+ __typename: 'PackageFileConnection',
+ },
+ __typename: 'PackageDetailsType',
+ },
+ },
+});
+
export const emptyPackageDetailsQuery = () => ({
data: {
package: {
diff --git a/spec/frontend/packages_and_registries/package_registry/pages/details_spec.js b/spec/frontend/packages_and_registries/package_registry/pages/details_spec.js
index 0962b4fa757..8b15dfd7d4a 100644
--- a/spec/frontend/packages_and_registries/package_registry/pages/details_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/pages/details_spec.js
@@ -328,18 +328,18 @@ describe('PackagesApp', () => {
describe('package files', () => {
it('renders the package files component and has the right props', async () => {
- const expectedFile = { ...packageFiles()[0] };
- // eslint-disable-next-line no-underscore-dangle
- delete expectedFile.__typename;
createComponent();
await waitForPromises();
expect(findPackageFiles().exists()).toBe(true);
- expect(findPackageFiles().props('packageFiles')[0]).toMatchObject(expectedFile);
- expect(findPackageFiles().props('canDelete')).toBe(packageData().canDestroy);
- expect(findPackageFiles().props('isLoading')).toEqual(false);
+ expect(findPackageFiles().props()).toMatchObject({
+ canDelete: packageData().canDestroy,
+ isLoading: false,
+ packageId: packageData().id,
+ packageType: packageData().packageType,
+ });
});
it('does not render the package files table when the package is composer', async () => {
diff --git a/spec/graphql/resolvers/users/participants_resolver_spec.rb b/spec/graphql/resolvers/users/participants_resolver_spec.rb
index 224213d1521..63a14daabba 100644
--- a/spec/graphql/resolvers/users/participants_resolver_spec.rb
+++ b/spec/graphql/resolvers/users/participants_resolver_spec.rb
@@ -8,39 +8,54 @@ RSpec.describe Resolvers::Users::ParticipantsResolver do
describe '#resolve' do
let_it_be(:user) { create(:user) }
let_it_be(:guest) { create(:user) }
- let_it_be(:project) { create(:project, :public) }
+ let_it_be(:project) do
+ create(:project, :public).tap do |r|
+ r.add_developer(user)
+ r.add_guest(guest)
+ end
+ end
+
+ let_it_be(:private_project) { create(:project, :private).tap { |r| r.add_developer(user) } }
+
let_it_be(:issue) { create(:issue, project: project) }
+ let_it_be(:private_issue) { create(:issue, project: private_project) }
let_it_be(:public_note_author) { create(:user) }
let_it_be(:public_reply_author) { create(:user) }
let_it_be(:internal_note_author) { create(:user) }
let_it_be(:internal_reply_author) { create(:user) }
+ let_it_be(:system_note_author) { create(:user) }
+ let_it_be(:internal_system_note_author) { create(:user) }
let_it_be(:public_note) { create(:note, project: project, noteable: issue, author: public_note_author) }
let_it_be(:internal_note) { create(:note, :confidential, project: project, noteable: issue, author: internal_note_author) }
- let_it_be(:public_reply) { create(:note, noteable: issue, in_reply_to: public_note, project: project, author: public_reply_author) }
- let_it_be(:internal_reply) { create(:note, :confidential, noteable: issue, in_reply_to: internal_note, project: project, author: internal_reply_author) }
-
- let_it_be(:note_metadata2) { create(:system_note_metadata, note: public_note) }
+ let_it_be(:public_reply) do
+ create(:note, noteable: issue, in_reply_to: public_note, project: project, author: public_reply_author)
+ end
- let_it_be(:issue_emoji) { create(:award_emoji, name: 'thumbsup', awardable: issue) }
- let_it_be(:note_emoji1) { create(:award_emoji, name: 'thumbsup', awardable: public_note) }
- let_it_be(:note_emoji2) { create(:award_emoji, name: 'thumbsup', awardable: internal_note) }
- let_it_be(:note_emoji3) { create(:award_emoji, name: 'thumbsup', awardable: public_reply) }
- let_it_be(:note_emoji4) { create(:award_emoji, name: 'thumbsup', awardable: internal_reply) }
+ let_it_be(:internal_reply) do
+ create(:note, :confidential, noteable: issue, in_reply_to: internal_note, project: project, author: internal_reply_author)
+ end
- let_it_be(:issue_emoji_author) { issue_emoji.user }
- let_it_be(:public_note_emoji_author) { note_emoji1.user }
- let_it_be(:internal_note_emoji_author) { note_emoji2.user }
- let_it_be(:public_reply_emoji_author) { note_emoji3.user }
- let_it_be(:internal_reply_emoji_author) { note_emoji4.user }
+ let_it_be(:issue_emoji_author) { create(:award_emoji, name: 'thumbsup', awardable: issue).user }
+ let_it_be(:public_note_emoji_author) { create(:award_emoji, name: 'thumbsup', awardable: public_note).user }
+ let_it_be(:internal_note_emoji_author) { create(:award_emoji, name: 'thumbsup', awardable: internal_note).user }
+ let_it_be(:public_reply_emoji_author) { create(:award_emoji, name: 'thumbsup', awardable: public_reply).user }
+ let_it_be(:internal_reply_emoji_author) { create(:award_emoji, name: 'thumbsup', awardable: internal_reply).user }
- subject(:resolved_items) { resolve(described_class, args: {}, ctx: { current_user: current_user }, obj: issue)&.items }
+ subject(:resolved_items) do
+ resolve(described_class, args: {}, ctx: { current_user: current_user }, obj: issue)&.items
+ end
- before do
- project.add_guest(guest)
- project.add_developer(user)
+ before_all do
+ create(:system_note, project: project, noteable: issue, author: system_note_author)
+ create(
+ :system_note,
+ note: "mentioned in issue #{private_issue.to_reference(full: true)}",
+ project: project, noteable: issue, author: internal_system_note_author
+ )
+ create(:system_note_metadata, note: public_note)
end
context 'when current user is not set' do
@@ -54,7 +69,8 @@ RSpec.describe Resolvers::Users::ParticipantsResolver do
public_note_author,
public_note_emoji_author,
public_reply_author,
- public_reply_emoji_author
+ public_reply_emoji_author,
+ system_note_author
]
)
end
@@ -71,7 +87,8 @@ RSpec.describe Resolvers::Users::ParticipantsResolver do
public_note_author,
public_note_emoji_author,
public_reply_author,
- public_reply_emoji_author
+ public_reply_emoji_author,
+ system_note_author
]
)
end
@@ -92,13 +109,17 @@ RSpec.describe Resolvers::Users::ParticipantsResolver do
internal_note_emoji_author,
internal_reply_author,
public_reply_emoji_author,
- internal_reply_emoji_author
+ internal_reply_emoji_author,
+ system_note_author,
+ internal_system_note_author
]
)
end
context 'N+1 queries' do
- let(:query) { -> { resolve(described_class, args: {}, ctx: { current_user: current_user }, obj: issue)&.items } }
+ let(:query) do
+ -> { resolve(described_class, args: {}, ctx: { current_user: current_user }, obj: issue)&.items }
+ end
before do
# warm-up
diff --git a/spec/lib/sidebars/groups/super_sidebar_menus/deploy_menu_spec.rb b/spec/lib/sidebars/groups/super_sidebar_menus/deploy_menu_spec.rb
new file mode 100644
index 00000000000..ec3f911d8dc
--- /dev/null
+++ b/spec/lib/sidebars/groups/super_sidebar_menus/deploy_menu_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::Groups::SuperSidebarMenus::DeployMenu, feature_category: :navigation do
+ subject { described_class.new({}) }
+
+ let(:items) { subject.instance_variable_get(:@items) }
+
+ it 'has title and sprite_icon' do
+ expect(subject.title).to eq(s_("Navigation|Deploy"))
+ expect(subject.sprite_icon).to eq("deployments")
+ end
+
+ it 'defines list of NilMenuItem placeholders' do
+ expect(items.map(&:class).uniq).to eq([Sidebars::NilMenuItem])
+ expect(items.map(&:item_id)).to eq([
+ :packages_registry
+ ])
+ end
+end
diff --git a/spec/lib/sidebars/groups/super_sidebar_menus/operations_menu_spec.rb b/spec/lib/sidebars/groups/super_sidebar_menus/operations_menu_spec.rb
index e9c2701021c..df37d5f1b0d 100644
--- a/spec/lib/sidebars/groups/super_sidebar_menus/operations_menu_spec.rb
+++ b/spec/lib/sidebars/groups/super_sidebar_menus/operations_menu_spec.rb
@@ -9,14 +9,13 @@ RSpec.describe Sidebars::Groups::SuperSidebarMenus::OperationsMenu, feature_cate
it 'has title and sprite_icon' do
expect(subject.title).to eq(s_("Navigation|Operate"))
- expect(subject.sprite_icon).to eq("deployments")
+ expect(subject.sprite_icon).to eq("cloud-pod")
end
it 'defines list of NilMenuItem placeholders' do
expect(items.map(&:class).uniq).to eq([Sidebars::NilMenuItem])
expect(items.map(&:item_id)).to eq([
:dependency_proxy,
- :packages_registry,
:container_registry,
:group_kubernetes_clusters
])
diff --git a/spec/lib/sidebars/groups/super_sidebar_panel_spec.rb b/spec/lib/sidebars/groups/super_sidebar_panel_spec.rb
index 5035da9c488..245d1eca0a4 100644
--- a/spec/lib/sidebars/groups/super_sidebar_panel_spec.rb
+++ b/spec/lib/sidebars/groups/super_sidebar_panel_spec.rb
@@ -36,6 +36,7 @@ RSpec.describe Sidebars::Groups::SuperSidebarPanel, feature_category: :navigatio
Sidebars::Groups::SuperSidebarMenus::PlanMenu,
Sidebars::Groups::SuperSidebarMenus::CodeMenu,
Sidebars::Groups::SuperSidebarMenus::BuildMenu,
+ Sidebars::Groups::SuperSidebarMenus::DeployMenu,
Sidebars::Groups::SuperSidebarMenus::SecureMenu,
Sidebars::Groups::SuperSidebarMenus::OperationsMenu,
Sidebars::Groups::SuperSidebarMenus::MonitorMenu,
diff --git a/spec/lib/sidebars/projects/super_sidebar_menus/analyze_menu_spec.rb b/spec/lib/sidebars/projects/super_sidebar_menus/analyze_menu_spec.rb
index d459d47c31a..b7d05867d77 100644
--- a/spec/lib/sidebars/projects/super_sidebar_menus/analyze_menu_spec.rb
+++ b/spec/lib/sidebars/projects/super_sidebar_menus/analyze_menu_spec.rb
@@ -23,8 +23,7 @@ RSpec.describe Sidebars::Projects::SuperSidebarMenus::AnalyzeMenu, feature_categ
:code_review,
:merge_request_analytics,
:issues,
- :insights,
- :model_experiments
+ :insights
])
end
end
diff --git a/spec/lib/sidebars/projects/super_sidebar_menus/build_menu_spec.rb b/spec/lib/sidebars/projects/super_sidebar_menus/build_menu_spec.rb
index 3f2a40e1c7d..06b87003d83 100644
--- a/spec/lib/sidebars/projects/super_sidebar_menus/build_menu_spec.rb
+++ b/spec/lib/sidebars/projects/super_sidebar_menus/build_menu_spec.rb
@@ -18,10 +18,7 @@ RSpec.describe Sidebars::Projects::SuperSidebarMenus::BuildMenu, feature_categor
:pipelines,
:jobs,
:pipelines_editor,
- :releases,
- :environments,
:pipeline_schedules,
- :feature_flags,
:test_cases,
:artifacts
])
diff --git a/spec/lib/sidebars/projects/super_sidebar_menus/deploy_menu_spec.rb b/spec/lib/sidebars/projects/super_sidebar_menus/deploy_menu_spec.rb
new file mode 100644
index 00000000000..50eee173d31
--- /dev/null
+++ b/spec/lib/sidebars/projects/super_sidebar_menus/deploy_menu_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::Projects::SuperSidebarMenus::DeployMenu, feature_category: :navigation do
+ subject { described_class.new({}) }
+
+ let(:items) { subject.instance_variable_get(:@items) }
+
+ it 'has title and sprite_icon' do
+ expect(subject.title).to eq(s_("Navigation|Deploy"))
+ expect(subject.sprite_icon).to eq("deployments")
+ end
+
+ it 'defines list of NilMenuItem placeholders' do
+ expect(items.map(&:class).uniq).to eq([Sidebars::NilMenuItem])
+ expect(items.map(&:item_id)).to eq([
+ :releases,
+ :feature_flags,
+ :packages_registry,
+ :container_registry,
+ :model_experiments
+ ])
+ end
+end
diff --git a/spec/lib/sidebars/projects/super_sidebar_menus/operations_menu_spec.rb b/spec/lib/sidebars/projects/super_sidebar_menus/operations_menu_spec.rb
index 6ab070c40ae..68ca4fe2aa0 100644
--- a/spec/lib/sidebars/projects/super_sidebar_menus/operations_menu_spec.rb
+++ b/spec/lib/sidebars/projects/super_sidebar_menus/operations_menu_spec.rb
@@ -9,14 +9,13 @@ RSpec.describe Sidebars::Projects::SuperSidebarMenus::OperationsMenu, feature_ca
it 'has title and sprite_icon' do
expect(subject.title).to eq(s_("Navigation|Operate"))
- expect(subject.sprite_icon).to eq("deployments")
+ expect(subject.sprite_icon).to eq("cloud-pod")
end
it 'defines list of NilMenuItem placeholders' do
expect(items.map(&:class).uniq).to eq([Sidebars::NilMenuItem])
expect(items.map(&:item_id)).to eq([
- :packages_registry,
- :container_registry,
+ :environments,
:kubernetes,
:terraform_states,
:infrastructure_registry,
diff --git a/spec/lib/sidebars/projects/super_sidebar_panel_spec.rb b/spec/lib/sidebars/projects/super_sidebar_panel_spec.rb
index 93f0072a111..9ed328f5090 100644
--- a/spec/lib/sidebars/projects/super_sidebar_panel_spec.rb
+++ b/spec/lib/sidebars/projects/super_sidebar_panel_spec.rb
@@ -47,6 +47,7 @@ RSpec.describe Sidebars::Projects::SuperSidebarPanel, feature_category: :navigat
Sidebars::Projects::SuperSidebarMenus::PlanMenu,
Sidebars::Projects::SuperSidebarMenus::CodeMenu,
Sidebars::Projects::SuperSidebarMenus::BuildMenu,
+ Sidebars::Projects::SuperSidebarMenus::DeployMenu,
Sidebars::Projects::SuperSidebarMenus::SecureMenu,
Sidebars::Projects::SuperSidebarMenus::OperationsMenu,
Sidebars::Projects::SuperSidebarMenus::MonitorMenu,
diff --git a/spec/models/integrations/jira_spec.rb b/spec/models/integrations/jira_spec.rb
index d3cb386e8e0..71dd543b3ec 100644
--- a/spec/models/integrations/jira_spec.rb
+++ b/spec/models/integrations/jira_spec.rb
@@ -326,6 +326,18 @@ RSpec.describe Integrations::Jira, feature_category: :integrations do
end
end
end
+
+ context 'with long running regex' do
+ let(:key) { "JIRAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1\nanother line\n" }
+
+ before do
+ jira_integration.jira_issue_regex = '((a|b)+|c)+$'
+ end
+
+ it 'handles long inputs' do
+ expect(jira_integration.reference_pattern.match(key).to_s).to eq('')
+ end
+ end
end
describe '.valid_jira_cloud_url?' do
diff --git a/spec/models/integrations/pipelines_email_spec.rb b/spec/models/integrations/pipelines_email_spec.rb
index 37a3849a768..7e80defcb87 100644
--- a/spec/models/integrations/pipelines_email_spec.rb
+++ b/spec/models/integrations/pipelines_email_spec.rb
@@ -84,7 +84,7 @@ RSpec.describe Integrations::PipelinesEmail, :mailer do
end
it 'sends email' do
- emails = receivers.map { |r| double(notification_email_or_default: r) }
+ emails = receivers.map { |r| double(notification_email_or_default: r, username: r, id: r) }
should_only_email(*emails)
end
@@ -206,10 +206,6 @@ RSpec.describe Integrations::PipelinesEmail, :mailer do
end
context 'with recipients' do
- context 'with failed pipeline' do
- it_behaves_like 'sending email'
- end
-
context 'with succeeded pipeline' do
before do
data[:object_attributes][:status] = 'success'
@@ -240,10 +236,7 @@ RSpec.describe Integrations::PipelinesEmail, :mailer do
context 'when the pipeline failed' do
context 'on default branch' do
- before do
- data[:object_attributes][:ref] = project.default_branch
- pipeline.update!(ref: project.default_branch)
- end
+ it_behaves_like 'sending email'
context 'notifications are enabled only for default branch' do
it_behaves_like 'sending email', branches_to_be_notified: "default"
@@ -253,7 +246,7 @@ RSpec.describe Integrations::PipelinesEmail, :mailer do
it_behaves_like 'not sending email', branches_to_be_notified: "protected"
end
- context 'notifications are enabled only for default and protected branches ' do
+ context 'notifications are enabled only for default and protected branches' do
it_behaves_like 'sending email', branches_to_be_notified: "default_and_protected"
end
@@ -273,11 +266,13 @@ RSpec.describe Integrations::PipelinesEmail, :mailer do
it_behaves_like 'not sending email', branches_to_be_notified: "default"
end
- context 'notifications are enabled only for protected branch' do
+ context 'notifications are enabled only for protected branch',
+ quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/411331' do
it_behaves_like 'sending email', branches_to_be_notified: "protected"
end
- context 'notifications are enabled only for default and protected branches ' do
+ context 'notifications are enabled only for default and protected branches',
+ quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/411331' do
it_behaves_like 'sending email', branches_to_be_notified: "default_and_protected"
end
diff --git a/spec/requests/api/v3/github_spec.rb b/spec/requests/api/v3/github_spec.rb
index b6fccd9b7cb..fbda291e901 100644
--- a/spec/requests/api/v3/github_spec.rb
+++ b/spec/requests/api/v3/github_spec.rb
@@ -13,16 +13,33 @@ RSpec.describe API::V3::Github, :aggregate_failures, feature_category: :integrat
end
describe 'GET /orgs/:namespace/repos' do
+ let_it_be(:group) { create(:group) }
+
it_behaves_like 'a GitHub Enterprise Jira DVCS reversible end of life endpoint' do
subject do
- group = create(:group)
jira_get v3_api("/orgs/#{group.path}/repos", user)
end
end
- it 'returns an empty array' do
- group = create(:group)
+ it 'logs when the endpoint is hit and `jira_dvcs_end_of_life_amnesty` is enabled' do
+ expect(Gitlab::JsonLogger).to receive(:info).with(
+ including(
+ namespace: group.path,
+ user_id: user.id,
+ message: 'Deprecated Jira DVCS endpoint request'
+ )
+ )
+
+ jira_get v3_api("/orgs/#{group.path}/repos", user)
+
+ stub_feature_flags(jira_dvcs_end_of_life_amnesty: false)
+ expect(Gitlab::JsonLogger).not_to receive(:info)
+
+ jira_get v3_api("/orgs/#{group.path}/repos", user)
+ end
+
+ it 'returns an empty array' do
jira_get v3_api("/orgs/#{group.path}/repos", user)
expect(response).to have_gitlab_http_status(:ok)
diff --git a/spec/support/shared_examples/models/concerns/participable_shared_examples.rb b/spec/support/shared_examples/models/concerns/participable_shared_examples.rb
index ec7a9105bb2..f772cfc6bbd 100644
--- a/spec/support/shared_examples/models/concerns/participable_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/participable_shared_examples.rb
@@ -10,13 +10,14 @@ RSpec.shared_examples 'visible participants for issuable with read ability' do |
allow(model).to receive(:participant_attrs).and_return([:bar])
end
- shared_examples 'check for participables read ability' do |ability_name|
+ shared_examples 'check for participables read ability' do |ability_name, ability_source: nil|
it 'receives expected ability' do
instance = model.new
+ source = ability_source == :participable_source ? participable_source : instance
allow(instance).to receive(:bar).and_return(participable_source)
- expect(Ability).to receive(:allowed?).with(anything, ability_name, instance)
+ expect(Ability).to receive(:allowed?).with(anything, ability_name, source)
expect(instance.visible_participants(user1)).to be_empty
end
@@ -39,4 +40,10 @@ RSpec.shared_examples 'visible participants for issuable with read ability' do |
it_behaves_like 'check for participables read ability', :read_internal_note
end
+
+ context 'when source is a system note' do
+ let(:participable_source) { build(:system_note) }
+
+ it_behaves_like 'check for participables read ability', :read_note, ability_source: :participable_source
+ end
end