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-03-08 15:11:30 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-03-08 15:11:30 +0300
commit012f9a4b9ec4a78d9593d882b38f95e376c2cfe2 (patch)
tree07f1df76cb12d94744e8069eec540d36092bbaba /spec
parent3c050fb24b757425987a7df4cb3497e1d792be8e (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/frontend/blob/components/blob_header_viewer_switcher_spec.js53
-rw-r--r--spec/frontend/ci/runner/admin_register_runner/admin_register_runner_app_spec.js64
-rw-r--r--spec/frontend/ci/runner/components/registration/__snapshots__/utils_spec.js.snap178
-rw-r--r--spec/frontend/ci/runner/components/registration/platforms_drawer_spec.js92
-rw-r--r--spec/frontend/ci/runner/components/registration/registration_instructions_spec.js27
-rw-r--r--spec/frontend/ci/runner/components/registration/utils_spec.js73
-rw-r--r--spec/frontend/monitoring/components/variables/text_field_spec.js6
-rw-r--r--spec/frontend/vue_shared/issuable/show/components/issuable_title_spec.js26
-rw-r--r--spec/graphql/types/work_items/widget_interface_spec.rb11
-rw-r--r--spec/graphql/types/work_items/widgets/notifications_type_spec.rb12
-rw-r--r--spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb16
-rw-r--r--spec/migrations/20230228142350_add_notifications_work_item_widget_spec.rb27
-rw-r--r--spec/models/concerns/subscribable_spec.rb26
-rw-r--r--spec/models/work_items/widget_definition_spec.rb3
-rw-r--r--spec/models/work_items/widgets/notifications_spec.rb19
-rw-r--r--spec/policies/metrics/dashboard/annotation_policy_spec.rb16
-rw-r--r--spec/policies/project_policy_spec.rb31
-rw-r--r--spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/work_items_spec.rb28
-rw-r--r--spec/requests/api/graphql/work_item_spec.rb26
-rw-r--r--spec/requests/api/terraform/state_spec.rb8
-rw-r--r--spec/requests/api/terraform/state_version_spec.rb8
-rw-r--r--spec/support/shared_contexts/policies/group_policy_shared_context.rb4
-rw-r--r--spec/support/shared_contexts/policies/project_policy_shared_context.rb11
-rw-r--r--spec/support/shared_contexts/rack_attack_shared_context.rb3
-rw-r--r--spec/support/shared_examples/lib/api/terraform_state_enabled_shared_examples.rb29
27 files changed, 678 insertions, 123 deletions
diff --git a/spec/frontend/blob/components/blob_header_viewer_switcher_spec.js b/spec/frontend/blob/components/blob_header_viewer_switcher_spec.js
index 1eac0733646..2ef87f6664b 100644
--- a/spec/frontend/blob/components/blob_header_viewer_switcher_spec.js
+++ b/spec/frontend/blob/components/blob_header_viewer_switcher_spec.js
@@ -18,14 +18,14 @@ describe('Blob Header Viewer Switcher', () => {
});
}
- afterEach(() => {
- wrapper.destroy();
- });
+ const findSimpleViewerButton = () => wrapper.findComponent('[data-viewer="simple"]');
+ const findRichViewerButton = () => wrapper.findComponent('[data-viewer="rich"]');
describe('intiialization', () => {
it('is initialized with simple viewer as active', () => {
createComponent();
- expect(wrapper.vm.value).toBe(SIMPLE_BLOB_VIEWER);
+ expect(findSimpleViewerButton().props('selected')).toBe(true);
+ expect(findRichViewerButton().props('selected')).toBe(false);
});
});
@@ -52,45 +52,34 @@ describe('Blob Header Viewer Switcher', () => {
});
describe('viewer changes', () => {
- let buttons;
- let simpleBtn;
- let richBtn;
+ it('does not switch the viewer if the selected one is already active', async () => {
+ createComponent();
+ expect(findSimpleViewerButton().props('selected')).toBe(true);
- function factory(propsData = {}) {
- createComponent(propsData);
- buttons = wrapper.findAllComponents(GlButton);
- simpleBtn = buttons.at(0);
- richBtn = buttons.at(1);
-
- jest.spyOn(wrapper.vm, '$emit');
- }
-
- it('does not switch the viewer if the selected one is already active', () => {
- factory();
- expect(wrapper.vm.value).toBe(SIMPLE_BLOB_VIEWER);
- simpleBtn.vm.$emit('click');
- expect(wrapper.vm.value).toBe(SIMPLE_BLOB_VIEWER);
- expect(wrapper.vm.$emit).not.toHaveBeenCalled();
+ findSimpleViewerButton().vm.$emit('click');
+ await nextTick();
+
+ expect(findSimpleViewerButton().props('selected')).toBe(true);
+ expect(wrapper.emitted('input')).toBe(undefined);
});
it('emits an event when a Rich Viewer button is clicked', async () => {
- factory();
- expect(wrapper.vm.value).toBe(SIMPLE_BLOB_VIEWER);
-
- richBtn.vm.$emit('click');
+ createComponent();
+ expect(findSimpleViewerButton().props('selected')).toBe(true);
+ findRichViewerButton().vm.$emit('click');
await nextTick();
- expect(wrapper.vm.$emit).toHaveBeenCalledWith('input', RICH_BLOB_VIEWER);
+
+ expect(wrapper.emitted('input')).toEqual([[RICH_BLOB_VIEWER]]);
});
it('emits an event when a Simple Viewer button is clicked', async () => {
- factory({
- value: RICH_BLOB_VIEWER,
- });
- simpleBtn.vm.$emit('click');
+ createComponent({ value: RICH_BLOB_VIEWER });
+ findSimpleViewerButton().vm.$emit('click');
await nextTick();
- expect(wrapper.vm.$emit).toHaveBeenCalledWith('input', SIMPLE_BLOB_VIEWER);
+
+ expect(wrapper.emitted('input')).toEqual([[SIMPLE_BLOB_VIEWER]]);
});
});
});
diff --git a/spec/frontend/ci/runner/admin_register_runner/admin_register_runner_app_spec.js b/spec/frontend/ci/runner/admin_register_runner/admin_register_runner_app_spec.js
index 42ca28eebc7..1ba3d9c317f 100644
--- a/spec/frontend/ci/runner/admin_register_runner/admin_register_runner_app_spec.js
+++ b/spec/frontend/ci/runner/admin_register_runner/admin_register_runner_app_spec.js
@@ -1,4 +1,4 @@
-import Vue from 'vue';
+import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import { GlButton } from '@gitlab/ui';
@@ -7,12 +7,15 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import setWindowLocation from 'helpers/set_window_location_helper';
+import { TEST_HOST } from 'helpers/test_constants';
import { s__ } from '~/locale';
+import { updateHistory } from '~/lib/utils/url_utility';
import runnerForRegistrationQuery from '~/ci/runner/graphql/register/runner_for_registration.query.graphql';
import { PARAM_KEY_PLATFORM, DEFAULT_PLATFORM, WINDOWS_PLATFORM } from '~/ci/runner/constants';
import AdminRegisterRunnerApp from '~/ci/runner/admin_register_runner/admin_register_runner_app.vue';
import RegistrationInstructions from '~/ci/runner/components/registration/registration_instructions.vue';
+import PlatformsDrawer from '~/ci/runner/components/registration/platforms_drawer.vue';
import { runnerForRegistration } from '../mock_data';
const mockRunner = runnerForRegistration.data.runner;
@@ -22,11 +25,17 @@ const MOCK_TOKEN = 'MOCK_TOKEN';
Vue.use(VueApollo);
+jest.mock('~/lib/utils/url_utility', () => ({
+ ...jest.requireActual('~/lib/utils/url_utility'),
+ updateHistory: jest.fn(),
+}));
+
describe('AdminRegisterRunnerApp', () => {
let wrapper;
let mockRunnerQuery;
const findRegistrationInstructions = () => wrapper.findComponent(RegistrationInstructions);
+ const findPlatformsDrawer = () => wrapper.findComponent(PlatformsDrawer);
const findBtn = () => wrapper.findComponent(GlButton);
const createComponent = () => {
@@ -69,6 +78,13 @@ describe('AdminRegisterRunnerApp', () => {
});
});
+ it('configures platform drawer', () => {
+ expect(findPlatformsDrawer().props()).toEqual({
+ open: false,
+ platform: DEFAULT_PLATFORM,
+ });
+ });
+
it('shows runner list button', () => {
expect(findBtn().attributes('href')).toEqual(mockRunnersPath);
expect(findBtn().props('variant')).toEqual('confirm');
@@ -88,6 +104,52 @@ describe('AdminRegisterRunnerApp', () => {
});
});
+ describe('When opening install instructions', () => {
+ beforeEach(async () => {
+ createComponent();
+ await waitForPromises();
+
+ findRegistrationInstructions().vm.$emit('toggleDrawer');
+ await nextTick();
+ });
+
+ it('opens platform drawer', () => {
+ expect(findPlatformsDrawer().props('open')).toEqual(true);
+ });
+
+ it('closes platform drawer', async () => {
+ findRegistrationInstructions().vm.$emit('toggleDrawer');
+ await nextTick();
+
+ expect(findPlatformsDrawer().props('open')).toEqual(false);
+ });
+
+ it('closes platform drawer from drawer', async () => {
+ findPlatformsDrawer().vm.$emit('close');
+ await nextTick();
+
+ expect(findPlatformsDrawer().props('open')).toEqual(false);
+ });
+
+ describe('when selecting a platform', () => {
+ beforeEach(async () => {
+ findPlatformsDrawer().vm.$emit('selectPlatform', WINDOWS_PLATFORM);
+ await nextTick();
+ });
+
+ it('updates the url', () => {
+ expect(updateHistory).toHaveBeenCalledTimes(1);
+ expect(updateHistory).toHaveBeenCalledWith({
+ url: `${TEST_HOST}/?${PARAM_KEY_PLATFORM}=${WINDOWS_PLATFORM}`,
+ });
+ });
+
+ it('updates the registration instructions', () => {
+ expect(findRegistrationInstructions().props('platform')).toBe(WINDOWS_PLATFORM);
+ });
+ });
+ });
+
describe('When runner is loading', () => {
beforeEach(async () => {
createComponent();
diff --git a/spec/frontend/ci/runner/components/registration/__snapshots__/utils_spec.js.snap b/spec/frontend/ci/runner/components/registration/__snapshots__/utils_spec.js.snap
index c1cc6aa07a2..94b87d31ed0 100644
--- a/spec/frontend/ci/runner/components/registration/__snapshots__/utils_spec.js.snap
+++ b/spec/frontend/ci/runner/components/registration/__snapshots__/utils_spec.js.snap
@@ -1,10 +1,77 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`registration utils for "linux" platform commandPrompt matches snapshot 1`] = `"$"`;
+exports[`registration utils for "linux" platform commandPrompt is correct 1`] = `"$"`;
-exports[`registration utils for "linux" platform commandPrompt matches snapshot 2`] = `"$"`;
+exports[`registration utils for "linux" platform installScript is correct for "386" architecture 1`] = `
+"# Download the binary for your system
+sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-386
-exports[`registration utils for "linux" platform registerCommand matches snapshot 1`] = `
+# Give it permission to execute
+sudo chmod +x /usr/local/bin/gitlab-runner
+
+# Create a GitLab Runner user
+sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
+
+# Install and run as a service
+sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
+sudo gitlab-runner start"
+`;
+
+exports[`registration utils for "linux" platform installScript is correct for "amd64" architecture 1`] = `
+"# Download the binary for your system
+sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
+
+# Give it permission to execute
+sudo chmod +x /usr/local/bin/gitlab-runner
+
+# Create a GitLab Runner user
+sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
+
+# Install and run as a service
+sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
+sudo gitlab-runner start"
+`;
+
+exports[`registration utils for "linux" platform installScript is correct for "arm" architecture 1`] = `
+"# Download the binary for your system
+sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm
+
+# Give it permission to execute
+sudo chmod +x /usr/local/bin/gitlab-runner
+
+# Create a GitLab Runner user
+sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
+
+# Install and run as a service
+sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
+sudo gitlab-runner start"
+`;
+
+exports[`registration utils for "linux" platform installScript is correct for "arm64" architecture 1`] = `
+"# Download the binary for your system
+sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm64
+
+# Give it permission to execute
+sudo chmod +x /usr/local/bin/gitlab-runner
+
+# Create a GitLab Runner user
+sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
+
+# Install and run as a service
+sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
+sudo gitlab-runner start"
+`;
+
+exports[`registration utils for "linux" platform platformArchitectures returns correct list of architectures 1`] = `
+Array [
+ "amd64",
+ "386",
+ "arm",
+ "arm64",
+]
+`;
+
+exports[`registration utils for "linux" platform registerCommand is correct 1`] = `
Array [
"gitlab-runner register",
" --url http://test.host",
@@ -12,21 +79,53 @@ Array [
]
`;
-exports[`registration utils for "linux" platform registerCommand matches snapshot 2`] = `
+exports[`registration utils for "linux" platform registerCommand is correct 2`] = `
Array [
"gitlab-runner register",
" --url http://test.host",
- " --registration-token REGISTRATION_TOKEN",
]
`;
-exports[`registration utils for "linux" platform runCommand matches snapshot 1`] = `"gitlab-runner run"`;
+exports[`registration utils for "linux" platform runCommand is correct 1`] = `"gitlab-runner run"`;
+
+exports[`registration utils for "osx" platform commandPrompt is correct 1`] = `"$"`;
+
+exports[`registration utils for "osx" platform installScript is correct for "amd64" architecture 1`] = `
+"# Download the binary for your system
+sudo curl --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-amd64
+
+# Give it permission to execute
+sudo chmod +x /usr/local/bin/gitlab-runner
+
+# The rest of the commands execute as the user who will run the runner
+# Register the runner (steps below), then run
+cd ~
+gitlab-runner install
+gitlab-runner start"
+`;
+
+exports[`registration utils for "osx" platform installScript is correct for "arm64" architecture 1`] = `
+"# Download the binary for your system
+sudo curl --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-arm64
+
+# Give it permission to execute
+sudo chmod +x /usr/local/bin/gitlab-runner
-exports[`registration utils for "linux" platform runCommand matches snapshot 2`] = `"gitlab-runner run"`;
+# The rest of the commands execute as the user who will run the runner
+# Register the runner (steps below), then run
+cd ~
+gitlab-runner install
+gitlab-runner start"
+`;
-exports[`registration utils for "null" platform commandPrompt matches snapshot 1`] = `"$"`;
+exports[`registration utils for "osx" platform platformArchitectures returns correct list of architectures 1`] = `
+Array [
+ "amd64",
+ "arm64",
+]
+`;
-exports[`registration utils for "null" platform registerCommand matches snapshot 1`] = `
+exports[`registration utils for "osx" platform registerCommand is correct 1`] = `
Array [
"gitlab-runner register",
" --url http://test.host",
@@ -34,23 +133,57 @@ Array [
]
`;
-exports[`registration utils for "null" platform runCommand matches snapshot 1`] = `"gitlab-runner run"`;
-
-exports[`registration utils for "osx" platform commandPrompt matches snapshot 1`] = `"$"`;
-
-exports[`registration utils for "osx" platform registerCommand matches snapshot 1`] = `
+exports[`registration utils for "osx" platform registerCommand is correct 2`] = `
Array [
"gitlab-runner register",
" --url http://test.host",
- " --registration-token REGISTRATION_TOKEN",
]
`;
-exports[`registration utils for "osx" platform runCommand matches snapshot 1`] = `"gitlab-runner run"`;
+exports[`registration utils for "osx" platform runCommand is correct 1`] = `"gitlab-runner run"`;
+
+exports[`registration utils for "windows" platform commandPrompt is correct 1`] = `">"`;
+
+exports[`registration utils for "windows" platform installScript is correct for "386" architecture 1`] = `
+"# Run PowerShell: https://docs.microsoft.com/en-us/powershell/scripting/windows-powershell/starting-windows-powershell?view=powershell-7#with-administrative-privileges-run-as-administrator
+# Create a folder somewhere on your system, for example: C:\\\\GitLab-Runner
+New-Item -Path 'C:\\\\GitLab-Runner' -ItemType Directory
+
+# Change to the folder
+cd 'C:\\\\GitLab-Runner'
+
+# Download binary
+Invoke-WebRequest -Uri \\"https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-windows-386.exe\\" -OutFile \\"gitlab-runner.exe\\"
+
+# Register the runner (steps below), then run
+.\\\\gitlab-runner.exe install
+.\\\\gitlab-runner.exe start"
+`;
-exports[`registration utils for "windows" platform commandPrompt matches snapshot 1`] = `">"`;
+exports[`registration utils for "windows" platform installScript is correct for "amd64" architecture 1`] = `
+"# Run PowerShell: https://docs.microsoft.com/en-us/powershell/scripting/windows-powershell/starting-windows-powershell?view=powershell-7#with-administrative-privileges-run-as-administrator
+# Create a folder somewhere on your system, for example: C:\\\\GitLab-Runner
+New-Item -Path 'C:\\\\GitLab-Runner' -ItemType Directory
-exports[`registration utils for "windows" platform registerCommand matches snapshot 1`] = `
+# Change to the folder
+cd 'C:\\\\GitLab-Runner'
+
+# Download binary
+Invoke-WebRequest -Uri \\"https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-windows-amd64.exe\\" -OutFile \\"gitlab-runner.exe\\"
+
+# Register the runner (steps below), then run
+.\\\\gitlab-runner.exe install
+.\\\\gitlab-runner.exe start"
+`;
+
+exports[`registration utils for "windows" platform platformArchitectures returns correct list of architectures 1`] = `
+Array [
+ "amd64",
+ "386",
+]
+`;
+
+exports[`registration utils for "windows" platform registerCommand is correct 1`] = `
Array [
".\\\\gitlab-runner.exe register",
" --url http://test.host",
@@ -58,4 +191,11 @@ Array [
]
`;
-exports[`registration utils for "windows" platform runCommand matches snapshot 1`] = `".\\\\gitlab-runner.exe run"`;
+exports[`registration utils for "windows" platform registerCommand is correct 2`] = `
+Array [
+ ".\\\\gitlab-runner.exe register",
+ " --url http://test.host",
+]
+`;
+
+exports[`registration utils for "windows" platform runCommand is correct 1`] = `".\\\\gitlab-runner.exe run"`;
diff --git a/spec/frontend/ci/runner/components/registration/platforms_drawer_spec.js b/spec/frontend/ci/runner/components/registration/platforms_drawer_spec.js
new file mode 100644
index 00000000000..c1879efd45a
--- /dev/null
+++ b/spec/frontend/ci/runner/components/registration/platforms_drawer_spec.js
@@ -0,0 +1,92 @@
+import { nextTick } from 'vue';
+import { GlDrawer } from '@gitlab/ui';
+import { s__ } from '~/locale';
+import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
+
+import PlatformsDrawer from '~/ci/runner/components/registration/platforms_drawer.vue';
+import CliCommand from '~/ci/runner/components/registration/cli_command.vue';
+import { LINUX_PLATFORM, MACOS_PLATFORM, WINDOWS_PLATFORM } from '~/ci/runner/constants';
+import { installScript, platformArchitectures } from '~/ci/runner/components/registration/utils';
+
+const MOCK_WRAPPER_HEIGHT = '99px';
+const LINUX_ARCHS = platformArchitectures({ platform: LINUX_PLATFORM });
+const MACOS_ARCHS = platformArchitectures({ platform: MACOS_PLATFORM });
+
+jest.mock('~/lib/utils/dom_utils', () => ({
+ getContentWrapperHeight: () => MOCK_WRAPPER_HEIGHT,
+}));
+
+describe('RegistrationInstructions', () => {
+ let wrapper;
+
+ const findDrawer = () => wrapper.findComponent(GlDrawer);
+ const findEnvironmentOptions = () =>
+ wrapper.findByLabelText(s__('Runners|Environment')).findAll('option');
+ const findArchitectureOptions = () =>
+ wrapper.findByLabelText(s__('Runners|Architecture')).findAll('option');
+ const findCliCommand = () => wrapper.findComponent(CliCommand);
+
+ const createComponent = ({ props = {}, mountFn = shallowMountExtended } = {}) => {
+ wrapper = mountFn(PlatformsDrawer, {
+ propsData: {
+ open: true,
+ ...props,
+ },
+ });
+ };
+
+ it('shows drawer', () => {
+ createComponent();
+
+ expect(findDrawer().props()).toMatchObject({
+ open: true,
+ headerHeight: MOCK_WRAPPER_HEIGHT,
+ });
+ });
+
+ it('closes drawer', () => {
+ createComponent();
+ findDrawer().vm.$emit('close');
+
+ expect(wrapper.emitted('close')).toHaveLength(1);
+ });
+
+ it('shows selection options', () => {
+ createComponent({ mountFn: mountExtended });
+
+ expect(findEnvironmentOptions().wrappers.map((w) => w.attributes('value'))).toEqual([
+ LINUX_PLATFORM,
+ MACOS_PLATFORM,
+ WINDOWS_PLATFORM,
+ ]);
+
+ expect(findArchitectureOptions().wrappers.map((w) => w.attributes('value'))).toEqual(
+ LINUX_ARCHS,
+ );
+ });
+
+ it('shows script', () => {
+ createComponent();
+
+ expect(findCliCommand().props('command')).toBe(
+ installScript({ platform: LINUX_PLATFORM, architecture: LINUX_ARCHS[0] }),
+ );
+ });
+
+ it('shows selection options for another platform', async () => {
+ createComponent({ mountFn: mountExtended });
+
+ findEnvironmentOptions().at(1).setSelected(); // macos
+ await nextTick();
+
+ expect(wrapper.emitted('selectPlatform')).toEqual([[MACOS_PLATFORM]]);
+
+ expect(findArchitectureOptions().wrappers.map((w) => w.attributes('value'))).toEqual(
+ MACOS_ARCHS,
+ );
+
+ expect(findCliCommand().props('command')).toBe(
+ installScript({ platform: MACOS_PLATFORM, architecture: MACOS_ARCHS[0] }),
+ );
+ });
+});
diff --git a/spec/frontend/ci/runner/components/registration/registration_instructions_spec.js b/spec/frontend/ci/runner/components/registration/registration_instructions_spec.js
index 858b0732782..f81f96ceffb 100644
--- a/spec/frontend/ci/runner/components/registration/registration_instructions_spec.js
+++ b/spec/frontend/ci/runner/components/registration/registration_instructions_spec.js
@@ -1,11 +1,15 @@
-import { GlSprintf, GlLink, GlSkeletonLoader } from '@gitlab/ui';
+import { GlSprintf, GlSkeletonLoader } from '@gitlab/ui';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { extendedWrapper, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { TEST_HOST } from 'helpers/test_constants';
import RegistrationInstructions from '~/ci/runner/components/registration/registration_instructions.vue';
import CliCommand from '~/ci/runner/components/registration/cli_command.vue';
-import { DEFAULT_PLATFORM, INSTALL_HELP_URL, EXECUTORS_HELP_URL } from '~/ci/runner/constants';
+import {
+ DEFAULT_PLATFORM,
+ EXECUTORS_HELP_URL,
+ SERVICE_COMMANDS_HELP_URL,
+} from '~/ci/runner/constants';
const REGISTRATION_TOKEN = 'REGISTRATION_TOKEN';
const DUMMY_GON = {
@@ -16,9 +20,8 @@ describe('RegistrationInstructions', () => {
let wrapper;
let originalGon;
- const findStepAt = (i) => wrapper.findAll('section').at(i);
- const findLink = (href, container = wrapper) =>
- container.findAllComponents(GlLink).filter((w) => w.attributes('href') === href);
+ const findStepAt = (i) => extendedWrapper(wrapper.findAll('section').at(i));
+ const findByText = (text, container = wrapper) => container.findByText(text);
const createComponent = (props) => {
wrapper = shallowMountExtended(RegistrationInstructions, {
@@ -47,7 +50,9 @@ describe('RegistrationInstructions', () => {
});
it('renders legacy instructions', () => {
- expect(findLink(INSTALL_HELP_URL).exists()).toBe(true);
+ findByText('How do I install GitLab Runner?').vm.$emit('click');
+
+ expect(wrapper.emitted('toggleDrawer')).toHaveLength(1);
});
it('renders step 1', () => {
@@ -80,7 +85,9 @@ describe('RegistrationInstructions', () => {
it('renders step 2', () => {
const step2 = findStepAt(1);
- expect(findLink(EXECUTORS_HELP_URL, step2).exists()).toBe(true);
+ expect(findByText('Not sure which one to select?', step2).attributes('href')).toBe(
+ EXECUTORS_HELP_URL,
+ );
});
it('renders step 3', () => {
@@ -90,5 +97,9 @@ describe('RegistrationInstructions', () => {
command: 'gitlab-runner run',
prompt: '$',
});
+
+ expect(findByText('system or user service', step3).attributes('href')).toBe(
+ SERVICE_COMMANDS_HELP_URL,
+ );
});
});
diff --git a/spec/frontend/ci/runner/components/registration/utils_spec.js b/spec/frontend/ci/runner/components/registration/utils_spec.js
index aeb489ca7de..6c4bf65eaa6 100644
--- a/spec/frontend/ci/runner/components/registration/utils_spec.js
+++ b/spec/frontend/ci/runner/components/registration/utils_spec.js
@@ -10,6 +10,8 @@ import {
commandPrompt,
registerCommand,
runCommand,
+ installScript,
+ platformArchitectures,
} from '~/ci/runner/components/registration/utils';
const REGISTRATION_TOKEN = 'REGISTRATION_TOKEN';
@@ -29,24 +31,69 @@ describe('registration utils', () => {
window.gon = originalGon;
});
- describe.each([DEFAULT_PLATFORM, LINUX_PLATFORM, MACOS_PLATFORM, WINDOWS_PLATFORM, null])(
+ describe.each([LINUX_PLATFORM, MACOS_PLATFORM, WINDOWS_PLATFORM])(
'for "%s" platform',
(platform) => {
- describe('commandPrompt', () => {
- it('matches snapshot', () => {
- expect(commandPrompt({ platform })).toMatchSnapshot();
- });
+ it('commandPrompt is correct', () => {
+ expect(commandPrompt({ platform })).toMatchSnapshot();
+ });
+ it('registerCommand is correct', () => {
+ expect(
+ registerCommand({ platform, registrationToken: REGISTRATION_TOKEN }),
+ ).toMatchSnapshot();
+
+ expect(registerCommand({ platform })).toMatchSnapshot();
+ });
+ it('runCommand is correct', () => {
+ expect(runCommand({ platform })).toMatchSnapshot();
+ });
+ },
+ );
+
+ describe('for missing platform', () => {
+ it('commandPrompt uses the default', () => {
+ const expected = commandPrompt({ platform: DEFAULT_PLATFORM });
+
+ expect(commandPrompt({ platform: null })).toEqual(expected);
+ expect(commandPrompt({ platform: undefined })).toEqual(expected);
+ });
+
+ it('registerCommand uses the default', () => {
+ const expected = registerCommand({
+ platform: DEFAULT_PLATFORM,
+ registrationToken: REGISTRATION_TOKEN,
});
- describe('registerCommand', () => {
- it('matches snapshot', () => {
- expect(
- registerCommand({ platform, registrationToken: REGISTRATION_TOKEN }),
- ).toMatchSnapshot();
+
+ expect(registerCommand({ platform: null, registrationToken: REGISTRATION_TOKEN })).toEqual(
+ expected,
+ );
+ expect(
+ registerCommand({ platform: undefined, registrationToken: REGISTRATION_TOKEN }),
+ ).toEqual(expected);
+ });
+
+ it('runCommand uses the default', () => {
+ const expected = runCommand({ platform: DEFAULT_PLATFORM });
+
+ expect(runCommand({ platform: null })).toEqual(expected);
+ expect(runCommand({ platform: undefined })).toEqual(expected);
+ });
+ });
+
+ describe.each([LINUX_PLATFORM, MACOS_PLATFORM, WINDOWS_PLATFORM])(
+ 'for "%s" platform',
+ (platform) => {
+ describe('platformArchitectures', () => {
+ it('returns correct list of architectures', () => {
+ expect(platformArchitectures({ platform })).toMatchSnapshot();
});
});
- describe('runCommand', () => {
- it('matches snapshot', () => {
- expect(runCommand({ platform })).toMatchSnapshot();
+
+ describe('installScript', () => {
+ const architectures = platformArchitectures({ platform });
+
+ it.each(architectures)('is correct for "%s" architecture', (architecture) => {
+ expect(installScript({ platform, architecture })).toMatchSnapshot();
});
});
},
diff --git a/spec/frontend/monitoring/components/variables/text_field_spec.js b/spec/frontend/monitoring/components/variables/text_field_spec.js
index 3073b3968aa..20e1937c5ac 100644
--- a/spec/frontend/monitoring/components/variables/text_field_spec.js
+++ b/spec/frontend/monitoring/components/variables/text_field_spec.js
@@ -33,25 +33,23 @@ describe('Text variable component', () => {
it('triggers keyup enter', async () => {
createShallowWrapper();
- jest.spyOn(wrapper.vm, '$emit');
findInput().element.value = 'prod-pod';
findInput().trigger('input');
findInput().trigger('keyup.enter');
await nextTick();
- expect(wrapper.vm.$emit).toHaveBeenCalledWith('input', 'prod-pod');
+ expect(wrapper.emitted('input')).toEqual([['prod-pod']]);
});
it('triggers blur enter', async () => {
createShallowWrapper();
- jest.spyOn(wrapper.vm, '$emit');
findInput().element.value = 'canary-pod';
findInput().trigger('input');
findInput().trigger('blur');
await nextTick();
- expect(wrapper.vm.$emit).toHaveBeenCalledWith('input', 'canary-pod');
+ expect(wrapper.emitted('input')).toEqual([['canary-pod']]);
});
});
diff --git a/spec/frontend/vue_shared/issuable/show/components/issuable_title_spec.js b/spec/frontend/vue_shared/issuable/show/components/issuable_title_spec.js
index 619d0cb5dd3..39316dfa249 100644
--- a/spec/frontend/vue_shared/issuable/show/components/issuable_title_spec.js
+++ b/spec/frontend/vue_shared/issuable/show/components/issuable_title_spec.js
@@ -29,28 +29,28 @@ const createComponent = (propsData = issuableTitleProps) =>
describe('IssuableTitle', () => {
let wrapper;
+ const findStickyHeader = () => wrapper.findComponent('[data-testid="header"]');
+
beforeEach(() => {
wrapper = createComponent();
});
- afterEach(() => {
- wrapper.destroy();
- });
-
describe('methods', () => {
describe('handleTitleAppear', () => {
- it('sets value of `stickyTitleVisible` prop to false', () => {
+ it('sets value of `stickyTitleVisible` prop to false', async () => {
wrapper.findComponent(GlIntersectionObserver).vm.$emit('appear');
+ await nextTick();
- expect(wrapper.vm.stickyTitleVisible).toBe(false);
+ expect(findStickyHeader().exists()).toBe(false);
});
});
describe('handleTitleDisappear', () => {
- it('sets value of `stickyTitleVisible` prop to true', () => {
+ it('sets value of `stickyTitleVisible` prop to true', async () => {
wrapper.findComponent(GlIntersectionObserver).vm.$emit('disappear');
+ await nextTick();
- expect(wrapper.vm.stickyTitleVisible).toBe(true);
+ expect(findStickyHeader().exists()).toBe(true);
});
});
});
@@ -87,14 +87,10 @@ describe('IssuableTitle', () => {
});
it('renders sticky header when `stickyTitleVisible` prop is true', async () => {
- // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
- // eslint-disable-next-line no-restricted-syntax
- wrapper.setData({
- stickyTitleVisible: true,
- });
-
+ wrapper.findComponent(GlIntersectionObserver).vm.$emit('disappear');
await nextTick();
- const stickyHeaderEl = wrapper.find('[data-testid="header"]');
+
+ const stickyHeaderEl = findStickyHeader();
expect(stickyHeaderEl.exists()).toBe(true);
expect(stickyHeaderEl.findComponent(GlBadge).props('variant')).toBe('success');
diff --git a/spec/graphql/types/work_items/widget_interface_spec.rb b/spec/graphql/types/work_items/widget_interface_spec.rb
index a2b12ed52dc..d1dcfb961cb 100644
--- a/spec/graphql/types/work_items/widget_interface_spec.rb
+++ b/spec/graphql/types/work_items/widget_interface_spec.rb
@@ -15,11 +15,12 @@ RSpec.describe Types::WorkItems::WidgetInterface do
using RSpec::Parameterized::TableSyntax
where(:widget_class, :widget_type_name) do
- WorkItems::Widgets::Description | Types::WorkItems::Widgets::DescriptionType
- WorkItems::Widgets::Hierarchy | Types::WorkItems::Widgets::HierarchyType
- WorkItems::Widgets::Assignees | Types::WorkItems::Widgets::AssigneesType
- WorkItems::Widgets::Labels | Types::WorkItems::Widgets::LabelsType
- WorkItems::Widgets::Notes | Types::WorkItems::Widgets::NotesType
+ WorkItems::Widgets::Description | Types::WorkItems::Widgets::DescriptionType
+ WorkItems::Widgets::Hierarchy | Types::WorkItems::Widgets::HierarchyType
+ WorkItems::Widgets::Assignees | Types::WorkItems::Widgets::AssigneesType
+ WorkItems::Widgets::Labels | Types::WorkItems::Widgets::LabelsType
+ WorkItems::Widgets::Notes | Types::WorkItems::Widgets::NotesType
+ WorkItems::Widgets::Notifications | Types::WorkItems::Widgets::NotificationsType
end
with_them do
diff --git a/spec/graphql/types/work_items/widgets/notifications_type_spec.rb b/spec/graphql/types/work_items/widgets/notifications_type_spec.rb
new file mode 100644
index 00000000000..4f457a24710
--- /dev/null
+++ b/spec/graphql/types/work_items/widgets/notifications_type_spec.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::WorkItems::Widgets::NotificationsType, feature_category: :team_planning do
+ it 'exposes the expected fields' do
+ expected_fields = %i[subscribed type]
+
+ expect(described_class.graphql_name).to eq('WorkItemWidgetNotifications')
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb b/spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb
index a5031ff6a82..dd04d63bcc0 100644
--- a/spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb
@@ -107,6 +107,22 @@ RSpec.describe Sidebars::Projects::Menus::InfrastructureMenu, feature_category:
let(:item_id) { :terraform }
it_behaves_like 'access rights checks'
+
+ context 'if terraform_state.enabled=true' do
+ before do
+ stub_config(terraform_state: { enabled: true })
+ end
+
+ it_behaves_like 'access rights checks'
+ end
+
+ context 'if terraform_state.enabled=false' do
+ before do
+ stub_config(terraform_state: { enabled: false })
+ end
+
+ it { is_expected.to be_nil }
+ end
end
describe 'Google Cloud' do
diff --git a/spec/migrations/20230228142350_add_notifications_work_item_widget_spec.rb b/spec/migrations/20230228142350_add_notifications_work_item_widget_spec.rb
new file mode 100644
index 00000000000..065b6d00ddb
--- /dev/null
+++ b/spec/migrations/20230228142350_add_notifications_work_item_widget_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe AddNotificationsWorkItemWidget, :migration, feature_category: :team_planning do
+ let(:migration) { described_class.new }
+ let(:work_item_definitions) { table(:work_item_widget_definitions) }
+
+ describe '#up' do
+ it 'creates notifications widget definition in all types' do
+ work_item_definitions.where(name: 'Notifications').delete_all
+
+ expect { migrate! }.to change { work_item_definitions.count }.by(7)
+ expect(work_item_definitions.all.pluck(:name)).to include('Notifications')
+ end
+ end
+
+ describe '#down' do
+ it 'removes definitions for notifications widget' do
+ migrate!
+
+ expect { migration.down }.to change { work_item_definitions.count }.by(-7)
+ expect(work_item_definitions.all.pluck(:name)).not_to include('Notifications')
+ end
+ end
+end
diff --git a/spec/models/concerns/subscribable_spec.rb b/spec/models/concerns/subscribable_spec.rb
index a60a0a5e26d..80060802de8 100644
--- a/spec/models/concerns/subscribable_spec.rb
+++ b/spec/models/concerns/subscribable_spec.rb
@@ -215,5 +215,31 @@ RSpec.describe Subscribable, 'Subscribable' do
expect(lazy_queries.count).to eq(0)
expect(preloaded_queries.count).to eq(1)
end
+
+ context 'with work items' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:work_item) { create(:work_item, :task, project: project) }
+ let_it_be(:issue) { create(:issue, project: project) }
+
+ before do
+ [issue, work_item].each do |item|
+ create(:subscription, user: user, subscribable: item, subscribed: true, project: project)
+ end
+ end
+
+ it 'loads correct subscribable type' do
+ expect(issue).to receive(:subscribable_type).and_return('Issue')
+ issue.lazy_subscription(user, project)
+
+ expect(work_item).to receive(:subscribable_type).and_return('Issue')
+ work_item.lazy_subscription(user, project)
+ end
+
+ it 'matches existing subscription type' do
+ expect(issue.subscribed?(user, project)).to eq(true)
+ expect(work_item.subscribed?(user, project)).to eq(true)
+ end
+ end
end
end
diff --git a/spec/models/work_items/widget_definition_spec.rb b/spec/models/work_items/widget_definition_spec.rb
index 08f8f4d9663..3a4670c996f 100644
--- a/spec/models/work_items/widget_definition_spec.rb
+++ b/spec/models/work_items/widget_definition_spec.rb
@@ -11,7 +11,8 @@ RSpec.describe WorkItems::WidgetDefinition, feature_category: :team_planning do
::WorkItems::Widgets::Assignees,
::WorkItems::Widgets::StartAndDueDate,
::WorkItems::Widgets::Milestone,
- ::WorkItems::Widgets::Notes
+ ::WorkItems::Widgets::Notes,
+ ::WorkItems::Widgets::Notifications
]
if Gitlab.ee?
diff --git a/spec/models/work_items/widgets/notifications_spec.rb b/spec/models/work_items/widgets/notifications_spec.rb
new file mode 100644
index 00000000000..2942c149660
--- /dev/null
+++ b/spec/models/work_items/widgets/notifications_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe WorkItems::Widgets::Notifications, feature_category: :team_planning do
+ let_it_be(:work_item) { create(:work_item) }
+
+ describe '.type' do
+ it { expect(described_class.type).to eq(:notifications) }
+ end
+
+ describe '#type' do
+ it { expect(described_class.new(work_item).type).to eq(:notifications) }
+ end
+
+ describe '#subscribed?' do
+ it { expect(described_class.new(work_item).subscribed?(work_item.author, work_item.project)).to eq(true) }
+ end
+end
diff --git a/spec/policies/metrics/dashboard/annotation_policy_spec.rb b/spec/policies/metrics/dashboard/annotation_policy_spec.rb
index 9ea9f843f2c..2d1ef0ee0cb 100644
--- a/spec/policies/metrics/dashboard/annotation_policy_spec.rb
+++ b/spec/policies/metrics/dashboard/annotation_policy_spec.rb
@@ -14,9 +14,7 @@ RSpec.describe Metrics::Dashboard::AnnotationPolicy, :models do
end
it { expect(policy).to be_disallowed :read_metrics_dashboard_annotation }
- it { expect(policy).to be_disallowed :create_metrics_dashboard_annotation }
- it { expect(policy).to be_disallowed :update_metrics_dashboard_annotation }
- it { expect(policy).to be_disallowed :delete_metrics_dashboard_annotation }
+ it { expect(policy).to be_disallowed :admin_metrics_dashboard_annotation }
end
context 'when reporter' do
@@ -25,9 +23,7 @@ RSpec.describe Metrics::Dashboard::AnnotationPolicy, :models do
end
it { expect(policy).to be_allowed :read_metrics_dashboard_annotation }
- it { expect(policy).to be_disallowed :create_metrics_dashboard_annotation }
- it { expect(policy).to be_disallowed :update_metrics_dashboard_annotation }
- it { expect(policy).to be_disallowed :delete_metrics_dashboard_annotation }
+ it { expect(policy).to be_disallowed :admin_metrics_dashboard_annotation }
end
context 'when developer' do
@@ -36,9 +32,7 @@ RSpec.describe Metrics::Dashboard::AnnotationPolicy, :models do
end
it { expect(policy).to be_allowed :read_metrics_dashboard_annotation }
- it { expect(policy).to be_allowed :create_metrics_dashboard_annotation }
- it { expect(policy).to be_allowed :update_metrics_dashboard_annotation }
- it { expect(policy).to be_allowed :delete_metrics_dashboard_annotation }
+ it { expect(policy).to be_allowed :admin_metrics_dashboard_annotation }
end
context 'when maintainer' do
@@ -47,9 +41,7 @@ RSpec.describe Metrics::Dashboard::AnnotationPolicy, :models do
end
it { expect(policy).to be_allowed :read_metrics_dashboard_annotation }
- it { expect(policy).to be_allowed :create_metrics_dashboard_annotation }
- it { expect(policy).to be_allowed :update_metrics_dashboard_annotation }
- it { expect(policy).to be_allowed :delete_metrics_dashboard_annotation }
+ it { expect(policy).to be_allowed :admin_metrics_dashboard_annotation }
end
end
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index e7b548b8f3b..38c487f3c36 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -2275,6 +2275,12 @@ RSpec.describe ProjectPolicy, feature_category: :system_access do
describe 'infrastructure feature' do
using RSpec::Parameterized::TableSyntax
+ before do
+ # assuming the default setting terraform_state.enabled=true
+ # the terraform_state permissions should follow the same logic as the other features
+ stub_config(terraform_state: { enabled: true })
+ end
+
let(:guest_permissions) { [] }
let(:developer_permissions) do
@@ -2338,6 +2344,31 @@ RSpec.describe ProjectPolicy, feature_category: :system_access do
end
end
end
+
+ context 'when terraform state management is disabled' do
+ before do
+ stub_config(terraform_state: { enabled: false })
+ end
+
+ with_them do
+ let(:current_user) { user_subject(role) }
+ let(:project) { project_subject(project_visibility) }
+
+ let(:developer_permissions) do
+ [:read_terraform_state]
+ end
+
+ let(:maintainer_permissions) do
+ developer_permissions + [:admin_terraform_state]
+ end
+
+ it 'always disallows the terraform_state feature' do
+ project.project_feature.update!(infrastructure_access_level: access_level)
+
+ expect_disallowed(*permissions_abilities(role))
+ end
+ end
+ end
end
describe 'access_security_and_compliance' do
diff --git a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb
index bce57b47aab..3c7f4a030f9 100644
--- a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe Mutations::Metrics::Dashboard::Annotations::Create, feature_categ
graphql_mutation_response(:create_annotation)
end
- specify { expect(described_class).to require_graphql_authorizations(:create_metrics_dashboard_annotation) }
+ specify { expect(described_class).to require_graphql_authorizations(:admin_metrics_dashboard_annotation) }
context 'when annotation source is environment' do
let(:mutation) do
diff --git a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb
index f505dc25dc0..c104138b725 100644
--- a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb
+++ b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe Mutations::Metrics::Dashboard::Annotations::Delete, feature_categ
graphql_mutation_response(:delete_annotation)
end
- specify { expect(described_class).to require_graphql_authorizations(:delete_metrics_dashboard_annotation) }
+ specify { expect(described_class).to require_graphql_authorizations(:admin_metrics_dashboard_annotation) }
context 'when the user has permission to delete the annotation' do
before do
diff --git a/spec/requests/api/graphql/project/work_items_spec.rb b/spec/requests/api/graphql/project/work_items_spec.rb
index f49165a88ea..d5dd12de63e 100644
--- a/spec/requests/api/graphql/project/work_items_spec.rb
+++ b/spec/requests/api/graphql/project/work_items_spec.rb
@@ -313,6 +313,34 @@ RSpec.describe 'getting a work item list for a project', feature_category: :team
end
end
+ context 'when fetching work item notifications widget' do
+ let(:fields) do
+ <<~GRAPHQL
+ nodes {
+ widgets {
+ type
+ ... on WorkItemWidgetNotifications {
+ subscribed
+ }
+ }
+ }
+ GRAPHQL
+ end
+
+ it 'executes limited number of N+1 queries', :use_sql_query_cache do
+ control = ActiveRecord::QueryRecorder.new(skip_cached: false) do
+ post_graphql(query, current_user: current_user)
+ end
+
+ create_list(:work_item, 3, project: project)
+
+ # Performs 1 extra query per item to fetch subscriptions
+ expect { post_graphql(query, current_user: current_user) }
+ .not_to exceed_all_query_limit(control).with_threshold(3)
+ expect_graphql_errors_to_be_empty
+ end
+ end
+
def item_ids
graphql_dig_at(items_data, :node, :id)
end
diff --git a/spec/requests/api/graphql/work_item_spec.rb b/spec/requests/api/graphql/work_item_spec.rb
index 0fad4f4ff3a..24c72a8bb00 100644
--- a/spec/requests/api/graphql/work_item_spec.rb
+++ b/spec/requests/api/graphql/work_item_spec.rb
@@ -373,6 +373,32 @@ RSpec.describe 'Query.work_item(id)', feature_category: :team_planning do
)
end
end
+
+ describe 'notifications widget' do
+ let(:work_item_fields) do
+ <<~GRAPHQL
+ id
+ widgets {
+ type
+ ... on WorkItemWidgetNotifications {
+ subscribed
+ }
+ }
+ GRAPHQL
+ end
+
+ it 'returns widget information' do
+ expect(work_item_data).to include(
+ 'id' => work_item.to_gid.to_s,
+ 'widgets' => include(
+ hash_including(
+ 'type' => 'NOTIFICATIONS',
+ 'subscribed' => work_item.subscribed?(current_user, project)
+ )
+ )
+ )
+ end
+ end
end
context 'when an Issue Global ID is provided' do
diff --git a/spec/requests/api/terraform/state_spec.rb b/spec/requests/api/terraform/state_spec.rb
index 7784985f7f4..c94643242c9 100644
--- a/spec/requests/api/terraform/state_spec.rb
+++ b/spec/requests/api/terraform/state_spec.rb
@@ -21,6 +21,7 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu
before do
stub_terraform_state_object_storage
+ stub_config(terraform_state: { enabled: true })
end
shared_examples 'endpoint with unique user tracking' do
@@ -81,6 +82,7 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu
subject(:request) { get api(state_path), headers: auth_header }
it_behaves_like 'endpoint with unique user tracking'
+ it_behaves_like 'it depends on value of the `terraform_state.enabled` config'
context 'without authentication' do
let(:auth_header) { basic_auth_header('bad', 'token') }
@@ -193,6 +195,7 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu
subject(:request) { post api(state_path), headers: auth_header, as: :json, params: params }
it_behaves_like 'endpoint with unique user tracking'
+ it_behaves_like 'it depends on value of the `terraform_state.enabled` config'
context 'when terraform state with a given name is already present' do
context 'with maintainer permissions' do
@@ -371,6 +374,7 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu
subject(:request) { delete api(state_path), headers: auth_header }
it_behaves_like 'endpoint with unique user tracking'
+ it_behaves_like 'it depends on value of the `terraform_state.enabled` config'
shared_examples 'schedules the state for deletion' do
it 'returns empty body' do
@@ -552,6 +556,10 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu
let(:lock_id) { 'irrelevant to this test, just needs to be present' }
end
+ it_behaves_like 'it depends on value of the `terraform_state.enabled` config' do
+ let(:lock_id) { '123.456' }
+ end
+
where(given_state_name: %w[test-state test.state test%2Ffoo])
with_them do
let(:state_name) { given_state_name }
diff --git a/spec/requests/api/terraform/state_version_spec.rb b/spec/requests/api/terraform/state_version_spec.rb
index 28abbb5749d..24b3ca94581 100644
--- a/spec/requests/api/terraform/state_version_spec.rb
+++ b/spec/requests/api/terraform/state_version_spec.rb
@@ -22,9 +22,15 @@ RSpec.describe API::Terraform::StateVersion, feature_category: :infrastructure_a
let(:version_serial) { version.version }
let(:state_version_path) { "/projects/#{project_id}/terraform/state/#{state_name}/versions/#{version_serial}" }
+ before do
+ stub_config(terraform_state: { enabled: true })
+ end
+
describe 'GET /projects/:id/terraform/state/:name/versions/:serial' do
subject(:request) { get api(state_version_path), headers: auth_header }
+ it_behaves_like 'it depends on value of the `terraform_state.enabled` config'
+
context 'with invalid authentication' do
let(:auth_header) { basic_auth_header('bad', 'token') }
@@ -147,6 +153,8 @@ RSpec.describe API::Terraform::StateVersion, feature_category: :infrastructure_a
describe 'DELETE /projects/:id/terraform/state/:name/versions/:serial' do
subject(:request) { delete api(state_version_path), headers: auth_header }
+ it_behaves_like 'it depends on value of the `terraform_state.enabled` config', { success_status: :no_content }
+
context 'with invalid authentication' do
let(:auth_header) { basic_auth_header('bad', 'token') }
diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
index ccfd403bebd..a9b12d47393 100644
--- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
@@ -42,9 +42,7 @@ RSpec.shared_context 'GroupPolicy context' do
let(:developer_permissions) do
%i[
- create_metrics_dashboard_annotation
- delete_metrics_dashboard_annotation
- update_metrics_dashboard_annotation
+ admin_metrics_dashboard_annotation
create_custom_emoji
create_package
read_cluster
diff --git a/spec/support/shared_contexts/policies/project_policy_shared_context.rb b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
index afc7fc8766f..71244933702 100644
--- a/spec/support/shared_contexts/policies/project_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
@@ -52,12 +52,11 @@ RSpec.shared_context 'ProjectPolicy context' do
admin_merge_request admin_tag create_build
create_commit_status create_container_image create_deployment
create_environment create_merge_request_from
- create_metrics_dashboard_annotation create_pipeline create_release
- create_wiki delete_metrics_dashboard_annotation
- destroy_container_image push_code read_pod_logs read_terraform_state
- resolve_note update_build update_commit_status update_container_image
- update_deployment update_environment update_merge_request
- update_metrics_dashboard_annotation update_pipeline update_release destroy_release
+ admin_metrics_dashboard_annotation create_pipeline create_release
+ create_wiki destroy_container_image push_code read_pod_logs
+ read_terraform_state resolve_note update_build update_commit_status
+ update_container_image update_deployment update_environment
+ update_merge_request update_pipeline update_release destroy_release
read_resource_group update_resource_group update_escalation_status
]
end
diff --git a/spec/support/shared_contexts/rack_attack_shared_context.rb b/spec/support/shared_contexts/rack_attack_shared_context.rb
index 12625ead72b..4e8c6e9dec6 100644
--- a/spec/support/shared_contexts/rack_attack_shared_context.rb
+++ b/spec/support/shared_contexts/rack_attack_shared_context.rb
@@ -5,8 +5,7 @@ RSpec.shared_context 'rack attack cache store' do
# Instead of test environment's :null_store so the throttles can increment
Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
- # Make time-dependent tests deterministic
- freeze_time { example.run }
+ example.run
Rack::Attack.cache.store = Rails.cache
end
diff --git a/spec/support/shared_examples/lib/api/terraform_state_enabled_shared_examples.rb b/spec/support/shared_examples/lib/api/terraform_state_enabled_shared_examples.rb
new file mode 100644
index 00000000000..b88eade7db2
--- /dev/null
+++ b/spec/support/shared_examples/lib/api/terraform_state_enabled_shared_examples.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'it depends on value of the `terraform_state.enabled` config' do |params = {}|
+ let(:expected_success_status) { params[:success_status] || :ok }
+
+ context 'when terraform_state.enabled=false' do
+ before do
+ stub_config(terraform_state: { enabled: false })
+ end
+
+ it 'returns `forbidden` response' do
+ request
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'when terraform_state.enabled=true' do
+ before do
+ stub_config(terraform_state: { enabled: true })
+ end
+
+ it 'returns a successful response' do
+ request
+
+ expect(response).to have_gitlab_http_status(expected_success_status)
+ end
+ end
+end