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
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-01-26 21:07:51 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-01-26 21:07:51 +0300
commitcea01cb81787f0d2c119b287a5833f2e267324bc (patch)
treeca37d57c1ec57ef2fa475e6489c4c107cce89dbc /spec/frontend
parentee24c7d68f57a67754a5d1e2ea99f688029d14bd (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
-rw-r--r--spec/frontend/ci/runner/admin_runners/admin_runners_app_spec.js10
-rw-r--r--spec/frontend/ci/runner/components/registration/registration_dropdown_spec.js9
-rw-r--r--spec/frontend/ci/runner/components/runner_list_empty_state_spec.js71
-rw-r--r--spec/frontend/ci/runner/mock_data.js1
-rw-r--r--spec/frontend/clusters/clusters_bundle_spec.js6
-rw-r--r--spec/frontend/issues/list/components/issues_list_app_spec.js46
-rw-r--r--spec/frontend/vue_shared/components/entity_select/entity_select_spec.js19
-rw-r--r--spec/frontend/vue_shared/components/entity_select/project_select_spec.js96
8 files changed, 191 insertions, 67 deletions
diff --git a/spec/frontend/ci/runner/admin_runners/admin_runners_app_spec.js b/spec/frontend/ci/runner/admin_runners/admin_runners_app_spec.js
index 9084ecdb4cc..a0ec2069ca8 100644
--- a/spec/frontend/ci/runner/admin_runners/admin_runners_app_spec.js
+++ b/spec/frontend/ci/runner/admin_runners/admin_runners_app_spec.js
@@ -56,6 +56,7 @@ import {
allRunnersDataPaginated,
onlineContactTimeoutSecs,
staleTimeoutSecs,
+ newRunnerPath,
emptyPageInfo,
emptyStateSvgPath,
emptyStateFilteredSvgPath,
@@ -113,6 +114,7 @@ describe('AdminRunnersApp', () => {
apolloProvider: createMockApollo(handlers, {}, cacheConfig),
propsData: {
registrationToken: mockRegistrationToken,
+ newRunnerPath,
...props,
},
provide: {
@@ -443,7 +445,13 @@ describe('AdminRunnersApp', () => {
});
it('shows an empty state', () => {
- expect(findRunnerListEmptyState().props('isSearchFiltered')).toBe(false);
+ expect(findRunnerListEmptyState().props()).toEqual({
+ newRunnerPath,
+ isSearchFiltered: false,
+ filteredSvgPath: emptyStateFilteredSvgPath,
+ registrationToken: mockRegistrationToken,
+ svgPath: emptyStateSvgPath,
+ });
});
describe('when a filter is selected by the user', () => {
diff --git a/spec/frontend/ci/runner/components/registration/registration_dropdown_spec.js b/spec/frontend/ci/runner/components/registration/registration_dropdown_spec.js
index 0ecafdd7d83..0daaca9c4ff 100644
--- a/spec/frontend/ci/runner/components/registration/registration_dropdown_spec.js
+++ b/spec/frontend/ci/runner/components/registration/registration_dropdown_spec.js
@@ -1,8 +1,9 @@
import { GlModal, GlDropdown, GlDropdownItem, GlDropdownForm } from '@gitlab/ui';
import { mount, shallowMount, createWrapper } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
-
import VueApollo from 'vue-apollo';
+
+import { s__ } from '~/locale';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
@@ -84,9 +85,9 @@ describe('RegistrationDropdown', () => {
it.each`
type | text
- ${INSTANCE_TYPE} | ${'Register an instance runner'}
- ${GROUP_TYPE} | ${'Register a group runner'}
- ${PROJECT_TYPE} | ${'Register a project runner'}
+ ${INSTANCE_TYPE} | ${s__('Runners|Register an instance runner')}
+ ${GROUP_TYPE} | ${s__('Runners|Register a group runner')}
+ ${PROJECT_TYPE} | ${s__('Runners|Register a project runner')}
`('Dropdown text for type $type is "$text"', () => {
createComponent({ props: { type: INSTANCE_TYPE } }, mount);
diff --git a/spec/frontend/ci/runner/components/runner_list_empty_state_spec.js b/spec/frontend/ci/runner/components/runner_list_empty_state_spec.js
index d351f7b6908..6aea3ddf58c 100644
--- a/spec/frontend/ci/runner/components/runner_list_empty_state_spec.js
+++ b/spec/frontend/ci/runner/components/runner_list_empty_state_spec.js
@@ -4,10 +4,14 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import RunnerInstructionsModal from '~/vue_shared/components/runner_instructions/runner_instructions_modal.vue';
+import {
+ newRunnerPath,
+ emptyStateSvgPath,
+ emptyStateFilteredSvgPath,
+} from 'jest/ci/runner/mock_data';
+
import RunnerListEmptyState from '~/ci/runner/components/runner_list_empty_state.vue';
-const mockSvgPath = 'mock-svg-path.svg';
-const mockFilteredSvgPath = 'mock-filtered-svg-path.svg';
const mockRegistrationToken = 'REGISTRATION_TOKEN';
describe('RunnerListEmptyState', () => {
@@ -17,12 +21,13 @@ describe('RunnerListEmptyState', () => {
const findLink = () => wrapper.findComponent(GlLink);
const findRunnerInstructionsModal = () => wrapper.findComponent(RunnerInstructionsModal);
- const createComponent = ({ props, mountFn = shallowMountExtended } = {}) => {
+ const createComponent = ({ props, mountFn = shallowMountExtended, ...options } = {}) => {
wrapper = mountFn(RunnerListEmptyState, {
propsData: {
- svgPath: mockSvgPath,
- filteredSvgPath: mockFilteredSvgPath,
+ svgPath: emptyStateSvgPath,
+ filteredSvgPath: emptyStateFilteredSvgPath,
registrationToken: mockRegistrationToken,
+ newRunnerPath,
...props,
},
directives: {
@@ -33,6 +38,7 @@ describe('RunnerListEmptyState', () => {
GlSprintf,
GlLink,
},
+ ...options,
});
};
@@ -45,7 +51,7 @@ describe('RunnerListEmptyState', () => {
});
it('renders an illustration', () => {
- expect(findEmptyState().props('svgPath')).toBe(mockSvgPath);
+ expect(findEmptyState().props('svgPath')).toBe(emptyStateSvgPath);
});
it('displays "no results" text with instructions', () => {
@@ -56,10 +62,53 @@ describe('RunnerListEmptyState', () => {
expect(findEmptyState().text()).toMatchInterpolatedText(`${title} ${desc}`);
});
- it('opens a runner registration instructions modal with a link', () => {
- const { value } = getBinding(findLink().element, 'gl-modal');
+ describe('when create_runner_workflow is enabled', () => {
+ beforeEach(() => {
+ createComponent({
+ provide: {
+ glFeatures: { createRunnerWorkflow: true },
+ },
+ });
+ });
+
+ it('shows a link to the new runner page', () => {
+ expect(findLink().attributes('href')).toBe(newRunnerPath);
+ });
+ });
+
+ describe('when create_runner_workflow is enabled and newRunnerPath not defined', () => {
+ beforeEach(() => {
+ createComponent({
+ props: {
+ newRunnerPath: null,
+ },
+ provide: {
+ glFeatures: { createRunnerWorkflow: true },
+ },
+ });
+ });
+
+ it('opens a runner registration instructions modal with a link', () => {
+ const { value } = getBinding(findLink().element, 'gl-modal');
+
+ expect(findRunnerInstructionsModal().props('modalId')).toEqual(value);
+ });
+ });
+
+ describe('when create_runner_workflow is disabled', () => {
+ beforeEach(() => {
+ createComponent({
+ provide: {
+ glFeatures: { createRunnerWorkflow: false },
+ },
+ });
+ });
+
+ it('opens a runner registration instructions modal with a link', () => {
+ const { value } = getBinding(findLink().element, 'gl-modal');
- expect(findRunnerInstructionsModal().props('modalId')).toEqual(value);
+ expect(findRunnerInstructionsModal().props('modalId')).toEqual(value);
+ });
});
});
@@ -69,7 +118,7 @@ describe('RunnerListEmptyState', () => {
});
it('renders an illustration', () => {
- expect(findEmptyState().props('svgPath')).toBe(mockSvgPath);
+ expect(findEmptyState().props('svgPath')).toBe(emptyStateSvgPath);
});
it('displays "no results" text', () => {
@@ -92,7 +141,7 @@ describe('RunnerListEmptyState', () => {
});
it('renders a "filtered search" illustration', () => {
- expect(findEmptyState().props('svgPath')).toBe(mockFilteredSvgPath);
+ expect(findEmptyState().props('svgPath')).toBe(emptyStateFilteredSvgPath);
});
it('displays "no filtered results" text', () => {
diff --git a/spec/frontend/ci/runner/mock_data.js b/spec/frontend/ci/runner/mock_data.js
index 525756ed513..5cdf0ea4e3b 100644
--- a/spec/frontend/ci/runner/mock_data.js
+++ b/spec/frontend/ci/runner/mock_data.js
@@ -304,6 +304,7 @@ export const mockSearchExamples = [
export const onlineContactTimeoutSecs = 2 * 60 * 60;
export const staleTimeoutSecs = 7889238; // Ruby's `3.months`
+export const newRunnerPath = '/runners/new';
export const emptyStateSvgPath = 'emptyStateSvgPath.svg';
export const emptyStateFilteredSvgPath = 'emptyStateFilteredSvgPath.svg';
diff --git a/spec/frontend/clusters/clusters_bundle_spec.js b/spec/frontend/clusters/clusters_bundle_spec.js
index 114cf5a9856..a2ec19c5b4a 100644
--- a/spec/frontend/clusters/clusters_bundle_spec.js
+++ b/spec/frontend/clusters/clusters_bundle_spec.js
@@ -4,10 +4,10 @@ import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
import Clusters from '~/clusters/clusters_bundle';
import axios from '~/lib/utils/axios_utils';
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
-import initProjectSelectDropdown from '~/project_select';
+import { initProjectSelects } from '~/vue_shared/components/entity_select/init_project_selects';
jest.mock('~/lib/utils/poll');
-jest.mock('~/project_select');
+jest.mock('~/vue_shared/components/entity_select/init_project_selects');
useMockLocationHelper();
@@ -49,7 +49,7 @@ describe('Clusters', () => {
});
it('should call initProjectSelectDropdown on construct', () => {
- expect(initProjectSelectDropdown).toHaveBeenCalled();
+ expect(initProjectSelects).toHaveBeenCalled();
});
});
diff --git a/spec/frontend/issues/list/components/issues_list_app_spec.js b/spec/frontend/issues/list/components/issues_list_app_spec.js
index dc01f37df27..1f09a8ad0e3 100644
--- a/spec/frontend/issues/list/components/issues_list_app_spec.js
+++ b/spec/frontend/issues/list/components/issues_list_app_spec.js
@@ -52,7 +52,6 @@ import {
WORK_ITEM_TYPE_ENUM_TEST_CASE,
} from '~/work_items/constants';
import {
- FILTERED_SEARCH_TERM,
TOKEN_TYPE_ASSIGNEE,
TOKEN_TYPE_AUTHOR,
TOKEN_TYPE_CONFIDENTIAL,
@@ -99,7 +98,6 @@ describe('CE IssuesListApp component', () => {
hasScopedLabelsFeature: true,
initialEmail: 'email@example.com',
initialSort: CREATED_DESC,
- isAnonymousSearchDisabled: false,
isIssueRepositioningDisabled: false,
isProject: true,
isPublicVisibilityRestricted: false,
@@ -432,27 +430,6 @@ describe('CE IssuesListApp component', () => {
expect(findIssuableList().props('initialFilterValue')).toEqual(filteredTokens);
});
-
- describe('when anonymous searching is performed', () => {
- beforeEach(() => {
- setWindowLocation(locationSearch);
- wrapper = mountComponent({
- provide: { isAnonymousSearchDisabled: true, isSignedIn: false },
- });
- });
-
- it('is set from url params and removes search terms', () => {
- const expected = filteredTokens.filter((token) => token.type !== FILTERED_SEARCH_TERM);
- expect(findIssuableList().props('initialFilterValue')).toEqual(expected);
- });
-
- it('shows an alert to tell the user they must be signed in to search', () => {
- expect(createAlert).toHaveBeenCalledWith({
- message: IssuesListApp.i18n.anonymousSearchingMessage,
- variant: VARIANT_INFO,
- });
- });
- });
});
});
@@ -911,29 +888,6 @@ describe('CE IssuesListApp component', () => {
query: expect.objectContaining(urlParams),
});
});
-
- describe('when anonymous searching is performed', () => {
- beforeEach(() => {
- wrapper = mountComponent({
- provide: { isAnonymousSearchDisabled: true, isSignedIn: false },
- });
- router.push = jest.fn();
-
- findIssuableList().vm.$emit('filter', filteredTokens);
- });
-
- it('removes search terms', () => {
- const expected = filteredTokens.filter((token) => token.type !== FILTERED_SEARCH_TERM);
- expect(findIssuableList().props('initialFilterValue')).toEqual(expected);
- });
-
- it('shows an alert to tell the user they must be signed in to search', () => {
- expect(createAlert).toHaveBeenCalledWith({
- message: IssuesListApp.i18n.anonymousSearchingMessage,
- variant: VARIANT_INFO,
- });
- });
- });
});
describe('when "page-size-change" event is emitted by IssuableList', () => {
diff --git a/spec/frontend/vue_shared/components/entity_select/entity_select_spec.js b/spec/frontend/vue_shared/components/entity_select/entity_select_spec.js
index b66ce3544c2..6b98f6c5e89 100644
--- a/spec/frontend/vue_shared/components/entity_select/entity_select_spec.js
+++ b/spec/frontend/vue_shared/components/entity_select/entity_select_spec.js
@@ -1,5 +1,5 @@
import { nextTick } from 'vue';
-import { GlCollapsibleListbox } from '@gitlab/ui';
+import { GlCollapsibleListbox, GlFormGroup } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import EntitySelect from '~/vue_shared/components/entity_select/entity_select.vue';
import { QUERY_TOO_SHORT_MESSAGE } from '~/vue_shared/components/entity_select/constants';
@@ -33,7 +33,7 @@ describe('EntitySelect', () => {
const findInput = () => wrapper.findByTestId('input');
// Helpers
- const createComponent = ({ props = {}, slots = {} } = {}) => {
+ const createComponent = ({ props = {}, slots = {}, stubs = {} } = {}) => {
wrapper = shallowMountExtended(EntitySelect, {
propsData: {
label,
@@ -47,6 +47,7 @@ describe('EntitySelect', () => {
stubs: {
GlAlert,
EntitySelect,
+ ...stubs,
},
slots,
});
@@ -99,6 +100,20 @@ describe('EntitySelect', () => {
expect(wrapper.find(`[${selector}]`).exists()).toBe(true);
});
+ it('renders the label slot if provided', () => {
+ const testid = 'label-slot';
+ createComponent({
+ slots: {
+ label: `<div data-testid="${testid}" />`,
+ },
+ stubs: {
+ GlFormGroup,
+ },
+ });
+
+ expect(wrapper.findByTestId(testid).exists()).toBe(true);
+ });
+
describe('selection', () => {
it('uses the default toggle text while no group is selected', () => {
createComponent();
diff --git a/spec/frontend/vue_shared/components/entity_select/project_select_spec.js b/spec/frontend/vue_shared/components/entity_select/project_select_spec.js
index ef5fa78f5cc..04a57fa475c 100644
--- a/spec/frontend/vue_shared/components/entity_select/project_select_spec.js
+++ b/spec/frontend/vue_shared/components/entity_select/project_select_spec.js
@@ -27,6 +27,7 @@ describe('ProjectSelect', () => {
const inputName = 'inputName';
const inputId = 'inputId';
const groupId = '22';
+ const userId = '1';
// Mocks
const apiVersion = 'v4';
@@ -34,7 +35,9 @@ describe('ProjectSelect', () => {
name_with_namespace: 'selectedProject',
id: '1',
};
+ const projectsEndpoint = `/api/${apiVersion}/projects.json`;
const groupProjectEndpoint = `/api/${apiVersion}/groups/${groupId}/projects.json`;
+ const userProjectEndpoint = `/api/${apiVersion}/users/${userId}/projects`;
const projectEndpoint = `/api/${apiVersion}/projects/${projectMock.id}`;
// Finders
@@ -72,6 +75,18 @@ describe('ProjectSelect', () => {
mock.restore();
});
+ it('renders HTML label when hasHtmlLabel is true', () => {
+ const testid = 'html-label';
+ createComponent({
+ props: {
+ label: `<div data-testid="${testid}" />`,
+ hasHtmlLabel: true,
+ },
+ });
+
+ expect(wrapper.findByTestId(testid).exists()).toBe(true);
+ });
+
describe('entity_select props', () => {
beforeEach(() => {
createComponent();
@@ -84,6 +99,7 @@ describe('ProjectSelect', () => {
${'inputId'} | ${inputId}
${'defaultToggleText'} | ${PROJECT_TOGGLE_TEXT}
${'headerText'} | ${PROJECT_HEADER_TEXT}
+ ${'clearable'} | ${true}
`('passes the $prop prop to entity-select', ({ prop, expectedValue }) => {
expect(findEntitySelect().props(prop)).toBe(expectedValue);
});
@@ -111,6 +127,86 @@ describe('ProjectSelect', () => {
});
});
+ it('includes projects from subgroups if includeSubgroups is true', async () => {
+ createComponent({
+ props: {
+ includeSubgroups: true,
+ },
+ });
+ openListbox();
+ await waitForPromises();
+
+ expect(mock.history.get[0].params.include_subgroups).toBe(true);
+ });
+
+ it('fetches projects globally if no group ID is provided', async () => {
+ createComponent({
+ props: {
+ groupId: null,
+ },
+ });
+ openListbox();
+ await waitForPromises();
+
+ expect(mock.history.get[0].url).toBe(projectsEndpoint);
+ expect(mock.history.get[0].params).toEqual({
+ membership: false,
+ order_by: 'similarity',
+ per_page: 20,
+ search: '',
+ simple: true,
+ });
+ });
+
+ it('restricts search to owned projects if membership is true', async () => {
+ createComponent({
+ props: {
+ groupId: null,
+ membership: true,
+ },
+ });
+ openListbox();
+ await waitForPromises();
+
+ expect(mock.history.get[0].params.membership).toBe(true);
+ });
+
+ it("fetches the user's projects if a user ID is provided", async () => {
+ createComponent({
+ props: {
+ groupId: null,
+ userId,
+ },
+ });
+ openListbox();
+ await waitForPromises();
+
+ expect(mock.history.get[0].url).toBe(userProjectEndpoint);
+ expect(mock.history.get[0].params).toEqual({
+ per_page: 20,
+ search: '',
+ with_shared: true,
+ include_subgroups: false,
+ });
+ });
+
+ it.each([null, groupId])(
+ 'fetches with the provided sort key when groupId is %s',
+ async (groupIdProp) => {
+ const orderBy = 'last_activity_at';
+ createComponent({
+ props: {
+ groupId: groupIdProp,
+ orderBy,
+ },
+ });
+ openListbox();
+ await waitForPromises();
+
+ expect(mock.history.get[0].params.order_by).toBe(orderBy);
+ },
+ );
+
describe('with an initial selection', () => {
it("fetches the initially selected value's name", async () => {
mock.onGet(projectEndpoint).reply(HTTP_STATUS_OK, projectMock);