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-08-07 21:10:22 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-08-07 21:10:22 +0300
commit4d18bba787186aeb5bf8a0463fd145fae48b3234 (patch)
treedaa26f7daa8bf303ef0cfcc1bfe68d13eae74537 /spec
parent33c86930e0a657e1519082a9a00faae260a44882 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/frontend/boards/components/boards_selector_spec.js3
-rw-r--r--spec/frontend/boards/components/config_toggle_spec.js20
-rw-r--r--spec/frontend/boards/mock_data.js22
-rw-r--r--spec/frontend/environments/graphql/mock_data.js7
-rw-r--r--spec/frontend/environments/graphql/resolvers_spec.js76
-rw-r--r--spec/frontend/environments/kubernetes_overview_spec.js10
-rw-r--r--spec/frontend/environments/kubernetes_status_bar_spec.js142
-rw-r--r--spec/frontend/environments/new_environment_item_spec.js6
-rw-r--r--spec/frontend/group_settings/components/shared_runners_form_spec.js51
-rw-r--r--spec/frontend/token_access/inbound_token_access_spec.js27
-rw-r--r--spec/frontend/token_access/outbound_token_access_spec.js6
-rw-r--r--spec/helpers/ci/runners_helper_spec.rb34
-rw-r--r--spec/models/ci/pipeline_spec.rb23
-rw-r--r--spec/models/project_team_spec.rb4
-rw-r--r--spec/support/fast_quarantine.rb7
15 files changed, 379 insertions, 59 deletions
diff --git a/spec/frontend/boards/components/boards_selector_spec.js b/spec/frontend/boards/components/boards_selector_spec.js
index 74d91eeaa26..50860c6e45b 100644
--- a/spec/frontend/boards/components/boards_selector_spec.js
+++ b/spec/frontend/boards/components/boards_selector_spec.js
@@ -180,8 +180,7 @@ describe('BoardsSelector', () => {
it('shows only matching boards when filtering', async () => {
const filterTerm = 'board1';
- const expectedCount = boards.filter((board) => board.node.name.includes(filterTerm))
- .length;
+ const expectedCount = boards.filter((board) => board.name.includes(filterTerm)).length;
fillSearchBox(filterTerm);
diff --git a/spec/frontend/boards/components/config_toggle_spec.js b/spec/frontend/boards/components/config_toggle_spec.js
index 5330721451e..9f006ebf01d 100644
--- a/spec/frontend/boards/components/config_toggle_spec.js
+++ b/spec/frontend/boards/components/config_toggle_spec.js
@@ -2,6 +2,7 @@ import Vuex from 'vuex';
import Vue from 'vue';
import { shallowMount } from '@vue/test-utils';
import { GlButton } from '@gitlab/ui';
+import { __ } from '~/locale';
import ConfigToggle from '~/boards/components/config_toggle.vue';
import eventHub from '~/boards/eventhub';
import store from '~/boards/stores';
@@ -12,13 +13,14 @@ describe('ConfigToggle', () => {
Vue.use(Vuex);
- const createComponent = (provide = {}) =>
+ const createComponent = (provide = {}, props = {}) =>
shallowMount(ConfigToggle, {
store,
provide: {
canAdminList: true,
...provide,
},
+ propsData: props,
});
const findButton = () => wrapper.findComponent(GlButton);
@@ -52,4 +54,20 @@ describe('ConfigToggle', () => {
label: 'edit_board',
});
});
+
+ it.each`
+ boardHasScope
+ ${true}
+ ${false}
+ `('renders dot highlight and tooltip depending on boardHasScope prop', ({ boardHasScope }) => {
+ wrapper = createComponent({}, { boardHasScope });
+
+ expect(findButton().classes('dot-highlight')).toBe(boardHasScope);
+
+ if (boardHasScope) {
+ expect(findButton().attributes('title')).toBe(__("This board's scope is reduced"));
+ } else {
+ expect(findButton().attributes('title')).toBe('');
+ }
+ });
});
diff --git a/spec/frontend/boards/mock_data.js b/spec/frontend/boards/mock_data.js
index 8235c3e4194..8f57a6eb7da 100644
--- a/spec/frontend/boards/mock_data.js
+++ b/spec/frontend/boards/mock_data.js
@@ -110,12 +110,10 @@ function boardGenerator(n) {
const name = `board${id}`;
return {
- node: {
- id,
- name,
- weight: 0,
- __typename: 'Board',
- },
+ id,
+ name,
+ weight: 0,
+ __typename: 'Board',
};
});
}
@@ -127,7 +125,7 @@ export const mockSmallProjectAllBoardsResponse = {
data: {
project: {
id: 'gid://gitlab/Project/114',
- boards: { edges: boardGenerator(3) },
+ boards: { nodes: boardGenerator(3) },
__typename: 'Project',
},
},
@@ -137,7 +135,7 @@ export const mockEmptyProjectRecentBoardsResponse = {
data: {
project: {
id: 'gid://gitlab/Project/114',
- recentIssueBoards: { edges: [] },
+ recentIssueBoards: { nodes: [] },
__typename: 'Project',
},
},
@@ -147,7 +145,7 @@ export const mockGroupAllBoardsResponse = {
data: {
group: {
id: 'gid://gitlab/Group/114',
- boards: { edges: boards },
+ boards: { nodes: boards },
__typename: 'Group',
},
},
@@ -157,7 +155,7 @@ export const mockProjectAllBoardsResponse = {
data: {
project: {
id: 'gid://gitlab/Project/1',
- boards: { edges: boards },
+ boards: { nodes: boards },
__typename: 'Project',
},
},
@@ -167,7 +165,7 @@ export const mockGroupRecentBoardsResponse = {
data: {
group: {
id: 'gid://gitlab/Group/114',
- recentIssueBoards: { edges: recentIssueBoards },
+ recentIssueBoards: { nodes: recentIssueBoards },
__typename: 'Group',
},
},
@@ -177,7 +175,7 @@ export const mockProjectRecentBoardsResponse = {
data: {
project: {
id: 'gid://gitlab/Project/1',
- recentIssueBoards: { edges: recentIssueBoards },
+ recentIssueBoards: { nodes: recentIssueBoards },
__typename: 'Project',
},
},
diff --git a/spec/frontend/environments/graphql/mock_data.js b/spec/frontend/environments/graphql/mock_data.js
index c2eafa5f51e..0a0a82c12c1 100644
--- a/spec/frontend/environments/graphql/mock_data.js
+++ b/spec/frontend/environments/graphql/mock_data.js
@@ -914,3 +914,10 @@ export const k8sNamespacesMock = [
{ metadata: { name: 'default' } },
{ metadata: { name: 'agent' } },
];
+
+export const fluxKustomizationsMock = [
+ {
+ status: 'True',
+ type: 'Ready',
+ },
+];
diff --git a/spec/frontend/environments/graphql/resolvers_spec.js b/spec/frontend/environments/graphql/resolvers_spec.js
index be210ed619e..20e96d771f3 100644
--- a/spec/frontend/environments/graphql/resolvers_spec.js
+++ b/spec/frontend/environments/graphql/resolvers_spec.js
@@ -2,7 +2,11 @@ import MockAdapter from 'axios-mock-adapter';
import { CoreV1Api, AppsV1Api, BatchV1Api } from '@gitlab/cluster-client';
import { s__ } from '~/locale';
import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
+import {
+ HTTP_STATUS_INTERNAL_SERVER_ERROR,
+ HTTP_STATUS_OK,
+ HTTP_STATUS_UNAUTHORIZED,
+} from '~/lib/utils/http_status';
import { resolvers } from '~/environments/graphql/resolvers';
import environmentToRollback from '~/environments/graphql/queries/environment_to_rollback.query.graphql';
import environmentToDelete from '~/environments/graphql/queries/environment_to_delete.query.graphql';
@@ -22,6 +26,7 @@ import {
k8sPodsMock,
k8sServicesMock,
k8sNamespacesMock,
+ fluxKustomizationsMock,
} from './mock_data';
const ENDPOINT = `${TEST_HOST}/environments`;
@@ -39,6 +44,7 @@ describe('~/frontend/environments/graphql/resolvers', () => {
},
};
const namespace = 'default';
+ const environmentName = 'my-environment';
beforeEach(() => {
mockResolvers = resolvers(ENDPOINT);
@@ -365,6 +371,74 @@ describe('~/frontend/environments/graphql/resolvers', () => {
},
);
});
+ describe('fluxKustomizationStatus', () => {
+ const endpoint = `${configuration.basePath}/apis/kustomize.toolkit.fluxcd.io/v1beta1/namespaces/${namespace}/kustomizations/${environmentName}`;
+
+ it('should request Flux Kustomizations via the Kubernetes API', async () => {
+ mock
+ .onGet(endpoint, { withCredentials: true, headers: configuration.baseOptions.headers })
+ .reply(HTTP_STATUS_OK, {
+ status: { conditions: fluxKustomizationsMock },
+ });
+
+ const fluxKustomizationStatus = await mockResolvers.Query.fluxKustomizationStatus(null, {
+ configuration,
+ namespace,
+ environmentName,
+ });
+
+ expect(fluxKustomizationStatus).toEqual(fluxKustomizationsMock);
+ });
+ it('should throw an error if the API call fails', async () => {
+ const apiError = 'Invalid credentials';
+ mock
+ .onGet(endpoint, { withCredentials: true, headers: configuration.base })
+ .reply(HTTP_STATUS_UNAUTHORIZED, { message: apiError });
+
+ const fluxKustomizationsError = mockResolvers.Query.fluxKustomizationStatus(null, {
+ configuration,
+ namespace,
+ environmentName,
+ });
+
+ await expect(fluxKustomizationsError).rejects.toThrow(apiError);
+ });
+ });
+
+ describe('fluxHelmReleaseStatus', () => {
+ const endpoint = `${configuration.basePath}/apis/helm.toolkit.fluxcd.io/v2beta1/namespaces/${namespace}/helmreleases/${environmentName}`;
+
+ it('should request Flux Helm Releases via the Kubernetes API', async () => {
+ mock
+ .onGet(endpoint, { withCredentials: true, headers: configuration.baseOptions.headers })
+ .reply(HTTP_STATUS_OK, {
+ status: { conditions: fluxKustomizationsMock },
+ });
+
+ const fluxHelmReleaseStatus = await mockResolvers.Query.fluxHelmReleaseStatus(null, {
+ configuration,
+ namespace,
+ environmentName,
+ });
+
+ expect(fluxHelmReleaseStatus).toEqual(fluxKustomizationsMock);
+ });
+ it('should throw an error if the API call fails', async () => {
+ const apiError = 'Invalid credentials';
+ mock
+ .onGet(endpoint, { withCredentials: true, headers: configuration.base })
+ .reply(HTTP_STATUS_UNAUTHORIZED, { message: apiError });
+
+ const fluxHelmReleasesError = mockResolvers.Query.fluxHelmReleaseStatus(null, {
+ configuration,
+ namespace,
+ environmentName,
+ });
+
+ await expect(fluxHelmReleasesError).rejects.toThrow(apiError);
+ });
+ });
+
describe('stopEnvironmentREST', () => {
it('should post to the stop environment path', async () => {
mock.onPost(ENDPOINT).reply(HTTP_STATUS_OK);
diff --git a/spec/frontend/environments/kubernetes_overview_spec.js b/spec/frontend/environments/kubernetes_overview_spec.js
index 1c7ace00f48..b088fce7e9e 100644
--- a/spec/frontend/environments/kubernetes_overview_spec.js
+++ b/spec/frontend/environments/kubernetes_overview_spec.js
@@ -6,12 +6,13 @@ import KubernetesAgentInfo from '~/environments/components/kubernetes_agent_info
import KubernetesPods from '~/environments/components/kubernetes_pods.vue';
import KubernetesTabs from '~/environments/components/kubernetes_tabs.vue';
import KubernetesStatusBar from '~/environments/components/kubernetes_status_bar.vue';
-import { agent, kubernetesNamespace } from './graphql/mock_data';
+import { agent, kubernetesNamespace, resolvedEnvironment } from './graphql/mock_data';
import { mockKasTunnelUrl } from './mock_data';
const propsData = {
clusterAgent: agent,
namespace: kubernetesNamespace,
+ environmentName: resolvedEnvironment.name,
};
const provide = {
@@ -110,7 +111,12 @@ describe('~/environments/components/kubernetes_overview.vue', () => {
});
it('renders kubernetes status bar', () => {
- expect(findKubernetesStatusBar().exists()).toBe(true);
+ expect(findKubernetesStatusBar().props()).toEqual({
+ clusterHealthStatus: 'success',
+ configuration,
+ namespace: kubernetesNamespace,
+ environmentName: resolvedEnvironment.name,
+ });
});
});
diff --git a/spec/frontend/environments/kubernetes_status_bar_spec.js b/spec/frontend/environments/kubernetes_status_bar_spec.js
index 2ebb30e2766..ad32818232a 100644
--- a/spec/frontend/environments/kubernetes_status_bar_spec.js
+++ b/spec/frontend/environments/kubernetes_status_bar_spec.js
@@ -1,20 +1,58 @@
-import { shallowMount } from '@vue/test-utils';
-import { GlLoadingIcon, GlBadge } from '@gitlab/ui';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import { GlLoadingIcon } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import KubernetesStatusBar from '~/environments/components/kubernetes_status_bar.vue';
import {
CLUSTER_STATUS_HEALTHY_TEXT,
CLUSTER_STATUS_UNHEALTHY_TEXT,
+ SYNC_STATUS_BADGES,
} from '~/environments/constants';
+import waitForPromises from 'helpers/wait_for_promises';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import { s__ } from '~/locale';
+import { mockKasTunnelUrl } from './mock_data';
+
+Vue.use(VueApollo);
+
+const configuration = {
+ basePath: mockKasTunnelUrl.replace(/\/$/, ''),
+ baseOptions: {
+ headers: { 'GitLab-Agent-Id': '1' },
+ withCredentials: true,
+ },
+};
+const environmentName = 'environment_name';
describe('~/environments/components/kubernetes_status_bar.vue', () => {
let wrapper;
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findHealthBadge = () => wrapper.findComponent(GlBadge);
+ const findHealthBadge = () => wrapper.findByTestId('health-badge');
+ const findSyncBadge = () => wrapper.findByTestId('sync-badge');
+
+ const fluxKustomizationStatusQuery = jest.fn().mockReturnValue([]);
+ const fluxHelmReleaseStatusQuery = jest.fn().mockReturnValue([]);
+
+ const createApolloProvider = () => {
+ const mockResolvers = {
+ Query: {
+ fluxKustomizationStatus: fluxKustomizationStatusQuery,
+ fluxHelmReleaseStatus: fluxHelmReleaseStatusQuery,
+ },
+ };
+
+ return createMockApollo([], mockResolvers);
+ };
- const createWrapper = ({ clusterHealthStatus = '' } = {}) => {
- wrapper = shallowMount(KubernetesStatusBar, {
- propsData: { clusterHealthStatus },
+ const createWrapper = ({
+ apolloProvider = createApolloProvider(),
+ clusterHealthStatus = '',
+ namespace = '',
+ } = {}) => {
+ wrapper = shallowMountExtended(KubernetesStatusBar, {
+ propsData: { clusterHealthStatus, configuration, environmentName, namespace },
+ apolloProvider,
});
};
@@ -39,4 +77,96 @@ describe('~/environments/components/kubernetes_status_bar.vue', () => {
},
);
});
+
+ describe('sync badge', () => {
+ describe('when no namespace is provided', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
+
+ it("doesn't request Kustomizations and HelmReleases", () => {
+ expect(fluxKustomizationStatusQuery).not.toHaveBeenCalled();
+ expect(fluxHelmReleaseStatusQuery).not.toHaveBeenCalled();
+ });
+
+ it('renders sync status as Unavailable', () => {
+ expect(findSyncBadge().text()).toBe(s__('Deployment|Unavailable'));
+ });
+ });
+
+ describe('when namespace is provided', () => {
+ describe('with no Flux resources found', () => {
+ beforeEach(() => {
+ createWrapper({ namespace: 'my-namespace' });
+ });
+
+ it('requests Kustomizations', () => {
+ expect(fluxKustomizationStatusQuery).toHaveBeenCalled();
+ });
+
+ it('requests HelmReleases when there were no Kustomizations found', async () => {
+ await waitForPromises();
+
+ expect(fluxHelmReleaseStatusQuery).toHaveBeenCalled();
+ });
+
+ it('renders sync status as Unavailable when no Kustomizations and HelmReleases found', async () => {
+ await waitForPromises();
+
+ expect(findSyncBadge().text()).toBe(s__('Deployment|Unavailable'));
+ });
+ });
+
+ describe('with Flux Kustomizations available', () => {
+ const createApolloProviderWithKustomizations = ({
+ result = { status: 'True', type: 'Ready' },
+ } = {}) => {
+ const mockResolvers = {
+ Query: {
+ fluxKustomizationStatus: jest.fn().mockReturnValue([result]),
+ fluxHelmReleaseStatus: fluxHelmReleaseStatusQuery,
+ },
+ };
+
+ return createMockApollo([], mockResolvers);
+ };
+
+ it("doesn't request HelmReleases when the Kustomizations were found", async () => {
+ createWrapper({
+ apolloProvider: createApolloProviderWithKustomizations(),
+ namespace: 'my-namespace',
+ });
+ await waitForPromises();
+
+ expect(fluxHelmReleaseStatusQuery).not.toHaveBeenCalled();
+ });
+
+ it.each`
+ status | type | badgeType
+ ${'True'} | ${'Stalled'} | ${'stalled'}
+ ${'True'} | ${'Reconciling'} | ${'reconciling'}
+ ${'True'} | ${'Ready'} | ${'reconciled'}
+ ${'False'} | ${'Ready'} | ${'failed'}
+ ${'True'} | ${'Unknown'} | ${'unknown'}
+ `(
+ 'renders $badgeType when status is $status and type is $type',
+ async ({ status, type, badgeType }) => {
+ createWrapper({
+ apolloProvider: createApolloProviderWithKustomizations({ result: { status, type } }),
+ namespace: 'my-namespace',
+ });
+ await waitForPromises();
+
+ const badge = SYNC_STATUS_BADGES[badgeType];
+
+ expect(findSyncBadge().text()).toBe(badge.text);
+ expect(findSyncBadge().props()).toMatchObject({
+ icon: badge.icon,
+ variant: badge.variant,
+ });
+ },
+ );
+ });
+ });
+ });
});
diff --git a/spec/frontend/environments/new_environment_item_spec.js b/spec/frontend/environments/new_environment_item_spec.js
index 387bc31c9aa..2f319855bca 100644
--- a/spec/frontend/environments/new_environment_item_spec.js
+++ b/spec/frontend/environments/new_environment_item_spec.js
@@ -534,7 +534,7 @@ describe('~/environments/components/new_environment_item.vue', () => {
});
describe('kubernetes overview', () => {
- it('should request agent data when the environment is visible if the feature flag is enabled', async () => {
+ it('should request agent data when the environment is visible', async () => {
wrapper = createWrapper({
propsData: { environment: resolvedEnvironment },
apolloProvider: createApolloProvider(agent),
@@ -578,6 +578,7 @@ describe('~/environments/components/new_environment_item.vue', () => {
expect(findKubernetesOverview().props()).toMatchObject({
clusterAgent: agent,
+ environmentName: resolvedEnvironment.name,
});
});
@@ -595,8 +596,9 @@ describe('~/environments/components/new_environment_item.vue', () => {
await expandCollapsedSection();
await waitForPromises();
- expect(findKubernetesOverview().props()).toMatchObject({
+ expect(findKubernetesOverview().props()).toEqual({
clusterAgent: agent,
+ environmentName: resolvedEnvironment.name,
namespace: 'default',
});
});
diff --git a/spec/frontend/group_settings/components/shared_runners_form_spec.js b/spec/frontend/group_settings/components/shared_runners_form_spec.js
index 5daa21fd618..b39b9a62661 100644
--- a/spec/frontend/group_settings/components/shared_runners_form_spec.js
+++ b/spec/frontend/group_settings/components/shared_runners_form_spec.js
@@ -1,5 +1,6 @@
-import { GlAlert } from '@gitlab/ui';
+import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
import { nextTick } from 'vue';
+import { s__, sprintf } from '~/locale';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
@@ -17,6 +18,9 @@ const RUNNER_ENABLED_VALUE = 'enabled';
const RUNNER_DISABLED_VALUE = 'disabled_and_unoverridable';
const RUNNER_ALLOW_OVERRIDE_VALUE = 'disabled_and_overridable';
+const mockParentName = 'My group';
+const mockParentSettingsPath = '/groups/my-group/-/settings/ci_cd';
+
describe('group_settings/components/shared_runners_form', () => {
let wrapper;
@@ -27,20 +31,19 @@ describe('group_settings/components/shared_runners_form', () => {
groupName: GROUP_NAME,
groupIsEmpty: false,
sharedRunnersSetting: RUNNER_ENABLED_VALUE,
- parentSharedRunnersSetting: null,
+
runnerEnabledValue: RUNNER_ENABLED_VALUE,
runnerDisabledValue: RUNNER_DISABLED_VALUE,
runnerAllowOverrideValue: RUNNER_ALLOW_OVERRIDE_VALUE,
...provide,
},
+ stubs: {
+ GlSprintf,
+ },
});
};
- const findAlert = (variant) =>
- wrapper
- .findAllComponents(GlAlert)
- .filter((w) => w.props('variant') === variant)
- .at(0);
+ const findAlert = () => wrapper.findComponent(GlAlert);
const findSharedRunnersToggle = () => wrapper.findByTestId('shared-runners-toggle');
const findOverrideToggle = () => wrapper.findByTestId('override-runners-toggle');
const getSharedRunnersSetting = () => {
@@ -86,17 +89,37 @@ describe('group_settings/components/shared_runners_form', () => {
});
});
- describe('When parent group disabled shared runners', () => {
- it('toggles are disabled', () => {
+ describe.each`
+ provide | case | isParentLinkExpected
+ ${{ parentName: mockParentName, parentSettingsPath: mockParentSettingsPath }} | ${'can configure parent'} | ${true}
+ ${{}} | ${'cannot configure parent'} | ${false}
+ `('When parent group disabled shared runners and $case', ({ provide, isParentLinkExpected }) => {
+ beforeEach(() => {
createComponent({
sharedRunnersSetting: RUNNER_DISABLED_VALUE,
parentSharedRunnersSetting: RUNNER_DISABLED_VALUE,
+ ...provide,
});
-
- expect(findSharedRunnersToggle().props('disabled')).toBe(true);
- expect(findOverrideToggle().props('disabled')).toBe(true);
- expect(findAlert('warning').exists()).toBe(true);
});
+
+ it.each([findSharedRunnersToggle, findOverrideToggle])(
+ 'toggle %# is disabled',
+ (findToggle) => {
+ expect(findToggle().props('disabled')).toBe(true);
+ expect(findToggle().text()).toContain(s__('Runners|Shared runners are disabled.'));
+
+ if (isParentLinkExpected) {
+ expect(findToggle().text()).toContain(
+ sprintf(s__('Runners|Go to %{groupLink} to enable them.'), {
+ groupLink: mockParentName,
+ }),
+ );
+ const link = findToggle().findComponent(GlLink);
+ expect(link.text()).toBe(mockParentName);
+ expect(link.attributes('href')).toBe(mockParentSettingsPath);
+ }
+ },
+ );
});
describe('loading state', () => {
@@ -240,7 +263,7 @@ describe('group_settings/components/shared_runners_form', () => {
});
it('error should be shown', () => {
- expect(findAlert('danger').text()).toBe(message);
+ expect(findAlert().text()).toBe(message);
});
});
});
diff --git a/spec/frontend/token_access/inbound_token_access_spec.js b/spec/frontend/token_access/inbound_token_access_spec.js
index 1ca58053e68..d82d65e3549 100644
--- a/spec/frontend/token_access/inbound_token_access_spec.js
+++ b/spec/frontend/token_access/inbound_token_access_spec.js
@@ -21,6 +21,7 @@ import {
} from './mock_data';
const projectPath = 'root/my-repo';
+const testProjectPath = 'root/test';
const message = 'An error occurred';
const error = new Error(message);
@@ -53,10 +54,11 @@ describe('TokenAccess component', () => {
const findToggle = () => wrapper.findComponent(GlToggle);
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findAddProjectBtn = () => wrapper.findByRole('button', { name: 'Add project' });
+ const findAddProjectBtn = () => wrapper.findByTestId('add-project-btn');
const findCancelBtn = () => wrapper.findByRole('button', { name: 'Cancel' });
const findProjectInput = () => wrapper.findComponent(GlFormInput);
const findRemoveProjectBtn = () => wrapper.findByRole('button', { name: 'Remove access' });
+ const findToggleFormBtn = () => wrapper.findByTestId('toggle-form-btn');
const findTokenDisabledAlert = () => wrapper.findComponent(GlAlert);
const createMockApolloProvider = (requestHandlers) => {
@@ -69,11 +71,6 @@ describe('TokenAccess component', () => {
fullPath: projectPath,
},
apolloProvider: createMockApolloProvider(requestHandlers),
- data() {
- return {
- targetProjectPath: 'root/test',
- };
- },
});
};
@@ -222,11 +219,13 @@ describe('TokenAccess component', () => {
await waitForPromises();
+ await findToggleFormBtn().trigger('click');
+ await findProjectInput().vm.$emit('input', testProjectPath);
findAddProjectBtn().trigger('click');
expect(inboundAddProjectSuccessResponseHandler).toHaveBeenCalledWith({
projectPath,
- targetProjectPath: 'root/test',
+ targetProjectPath: testProjectPath,
});
});
@@ -242,6 +241,8 @@ describe('TokenAccess component', () => {
await waitForPromises();
+ await findToggleFormBtn().trigger('click');
+ await findProjectInput().vm.$emit('input', testProjectPath);
findAddProjectBtn().trigger('click');
await waitForPromises();
@@ -249,7 +250,7 @@ describe('TokenAccess component', () => {
expect(createAlert).toHaveBeenCalledWith({ message });
});
- it('clicking cancel clears target path', async () => {
+ it('clicking cancel hides the form and clears the target path', async () => {
createComponent(
[
[inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler],
@@ -260,10 +261,18 @@ describe('TokenAccess component', () => {
await waitForPromises();
- expect(findProjectInput().element.value).toBe('root/test');
+ await findToggleFormBtn().trigger('click');
+
+ expect(findProjectInput().exists()).toBe(true);
+
+ await findProjectInput().vm.$emit('input', testProjectPath);
await findCancelBtn().trigger('click');
+ expect(findProjectInput().exists()).toBe(false);
+
+ await findToggleFormBtn().trigger('click');
+
expect(findProjectInput().element.value).toBe('');
});
});
diff --git a/spec/frontend/token_access/outbound_token_access_spec.js b/spec/frontend/token_access/outbound_token_access_spec.js
index f9eb201eb5c..c5224d5d942 100644
--- a/spec/frontend/token_access/outbound_token_access_spec.js
+++ b/spec/frontend/token_access/outbound_token_access_spec.js
@@ -38,9 +38,9 @@ describe('TokenAccess component', () => {
const findToggle = () => wrapper.findComponent(GlToggle);
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+ const findAddProjectBtn = () => wrapper.findByRole('button', { name: 'Add project' });
const findRemoveProjectBtn = () => wrapper.findByRole('button', { name: 'Remove access' });
const findDeprecationAlert = () => wrapper.findByTestId('deprecation-alert');
- const findProjectPathInput = () => wrapper.findByTestId('project-path-input');
const createMockApolloProvider = (requestHandlers) => {
return createMockApollo(requestHandlers);
@@ -247,7 +247,7 @@ describe('TokenAccess component', () => {
});
describe('adding a new project', () => {
- it('disables the input to add new projects', async () => {
+ it('disables the button for adding new projects', async () => {
createComponent(
[
[getCIJobTokenScopeQuery, disabledJobTokenScopeHandler],
@@ -260,7 +260,7 @@ describe('TokenAccess component', () => {
await waitForPromises();
- expect(findProjectPathInput().attributes('disabled')).toBe('disabled');
+ expect(findAddProjectBtn().attributes('disabled')).toBe('disabled');
});
});
});
diff --git a/spec/helpers/ci/runners_helper_spec.rb b/spec/helpers/ci/runners_helper_spec.rb
index dc593228388..4f47d7c936f 100644
--- a/spec/helpers/ci/runners_helper_spec.rb
+++ b/spec/helpers/ci/runners_helper_spec.rb
@@ -115,12 +115,19 @@ RSpec.describe Ci::RunnersHelper, feature_category: :runner_fleet do
}
end
+ before do
+ allow(helper).to receive(:can?).with(user, :admin_group, parent).and_return(true)
+ end
+
it 'returns group data for top level group' do
result = {
group_id: parent.id,
group_name: parent.name,
group_is_empty: 'false',
shared_runners_setting: Namespace::SR_ENABLED,
+
+ parent_name: nil,
+ parent_settings_path: nil,
parent_shared_runners_setting: nil
}.merge(runner_constants)
@@ -133,7 +140,27 @@ RSpec.describe Ci::RunnersHelper, feature_category: :runner_fleet do
group_name: group.name,
group_is_empty: 'true',
shared_runners_setting: Namespace::SR_DISABLED_AND_UNOVERRIDABLE,
- parent_shared_runners_setting: Namespace::SR_ENABLED
+
+ parent_shared_runners_setting: Namespace::SR_ENABLED,
+ parent_name: parent.name,
+ parent_settings_path: group_settings_ci_cd_path(group.parent, anchor: 'js-runner-settings')
+ }.merge(runner_constants)
+
+ expect(helper.group_shared_runners_settings_data(group)).to eq result
+ end
+
+ it 'returns groups data for child group with no access to parent' do
+ allow(helper).to receive(:can?).with(user, :admin_group, parent).and_return(false)
+
+ result = {
+ group_id: group.id,
+ group_name: group.name,
+ group_is_empty: 'true',
+ shared_runners_setting: Namespace::SR_DISABLED_AND_UNOVERRIDABLE,
+
+ parent_shared_runners_setting: Namespace::SR_ENABLED,
+ parent_name: nil,
+ parent_settings_path: nil
}.merge(runner_constants)
expect(helper.group_shared_runners_settings_data(group)).to eq result
@@ -145,7 +172,10 @@ RSpec.describe Ci::RunnersHelper, feature_category: :runner_fleet do
group_name: group_with_project.name,
group_is_empty: 'false',
shared_runners_setting: Namespace::SR_ENABLED,
- parent_shared_runners_setting: Namespace::SR_ENABLED
+
+ parent_shared_runners_setting: Namespace::SR_ENABLED,
+ parent_name: parent.name,
+ parent_settings_path: group_settings_ci_cd_path(group.parent, anchor: 'js-runner-settings')
}.merge(runner_constants)
expect(helper.group_shared_runners_settings_data(group_with_project)).to eq result
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 1de19faf063..c4b8f4f0145 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -279,6 +279,29 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep, feature_category:
end
end
+ describe '.for_status' do
+ subject { described_class.for_status(status) }
+
+ let_it_be(:pipeline1) { create(:ci_pipeline, name: 'Build pipeline', status: :created) }
+ let_it_be(:pipeline2) { create(:ci_pipeline, name: 'Chatops pipeline', status: :failed) }
+
+ context 'when status exists' do
+ let(:status) { :created }
+
+ it 'performs exact compare' do
+ is_expected.to contain_exactly(pipeline1)
+ end
+ end
+
+ context 'when status does not exist' do
+ let(:status) { :pending }
+
+ it 'returns empty' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
describe '.created_after' do
let_it_be(:old_pipeline) { create(:ci_pipeline, created_at: 1.week.ago) }
let_it_be(:pipeline) { create(:ci_pipeline) }
diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb
index a6766035c1e..e557990c7e9 100644
--- a/spec/models/project_team_spec.rb
+++ b/spec/models/project_team_spec.rb
@@ -180,8 +180,8 @@ RSpec.describe ProjectTeam, feature_category: :groups_and_projects do
subject(:import) { target_project.team.import(source_project, current_user) }
- it 'matches the imported members', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/419394' do
- is_expected.to match(imported_members)
+ it 'matches the imported members' do
+ is_expected.to match_array(imported_members)
end
it 'target project includes source member with the same access' do
diff --git a/spec/support/fast_quarantine.rb b/spec/support/fast_quarantine.rb
index b5ed1a2aa96..9732a287cb2 100644
--- a/spec/support/fast_quarantine.rb
+++ b/spec/support/fast_quarantine.rb
@@ -7,10 +7,9 @@ return if ENV['CI_MERGE_REQUEST_LABELS'].to_s.include?('pipeline:run-flaky-tests
require_relative '../../tooling/lib/tooling/fast_quarantine'
RSpec.configure do |config|
- fast_quarantine_local_path = ENV.fetch('RSPEC_FAST_QUARANTINE_LOCAL_PATH', 'rspec/fast_quarantine-gitlab.txt')
fast_quarantine_path = ENV.fetch(
'RSPEC_FAST_QUARANTINE_PATH',
- File.expand_path("../../#{fast_quarantine_local_path}", __dir__)
+ File.expand_path("../../rspec/fast_quarantine-gitlab.txt", __dir__)
)
fast_quarantine = Tooling::FastQuarantine.new(fast_quarantine_path: fast_quarantine_path)
skipped_examples = []
@@ -28,10 +27,12 @@ RSpec.configure do |config|
next if skipped_examples.empty?
skipped_tests_report_path = ENV.fetch(
- 'SKIPPED_TESTS_REPORT_PATH',
+ 'RSPEC_SKIPPED_TESTS_REPORT_PATH',
File.expand_path("../../rspec/flaky/skipped_tests.txt", __dir__)
)
+ next warn("#{skipped_tests_report_path} doesn't exist!") unless File.exist?(skipped_tests_report_path.to_s)
+
File.write(skipped_tests_report_path, "#{ENV.fetch('CI_JOB_URL', 'local-run')}\n#{skipped_examples.join("\n")}\n\n")
end
end