diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-01-26 21:07:51 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-01-26 21:07:51 +0300 |
commit | cea01cb81787f0d2c119b287a5833f2e267324bc (patch) | |
tree | ca37d57c1ec57ef2fa475e6489c4c107cce89dbc /spec/frontend | |
parent | ee24c7d68f57a67754a5d1e2ea99f688029d14bd (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
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); |