diff options
Diffstat (limited to 'spec/frontend/ci')
21 files changed, 398 insertions, 331 deletions
diff --git a/spec/frontend/ci/ci_variable_list/components/ci_group_variables_spec.js b/spec/frontend/ci/ci_variable_list/components/ci_group_variables_spec.js index b364f098a3a..567a49d663c 100644 --- a/spec/frontend/ci/ci_variable_list/components/ci_group_variables_spec.js +++ b/spec/frontend/ci/ci_variable_list/components/ci_group_variables_spec.js @@ -30,7 +30,6 @@ describe('Ci Group Variable wrapper', () => { provide: { ...mockProvide, glFeatures: { - ciGroupEnvScopeGraphql: false, groupScopedCiVariables: false, ...featureFlags, }, @@ -61,6 +60,10 @@ describe('Ci Group Variable wrapper', () => { lookup: expect.any(Function), query: getGroupVariables, }, + environments: { + lookup: expect.any(Function), + query: getGroupEnvironments, + }, }, refetchAfterMutation: false, }); @@ -88,26 +91,4 @@ describe('Ci Group Variable wrapper', () => { }); }); }); - - describe('ciGroupEnvScopeGraphql feature flag', () => { - describe('When enabled', () => { - beforeEach(() => { - createComponent({ featureFlags: { ciGroupEnvScopeGraphql: true } }); - }); - - it('Passes down environments query to variable shared component', () => { - expect(findCiShared().props('queryData').environments.query).toBe(getGroupEnvironments); - }); - }); - - describe('When disabled', () => { - beforeEach(() => { - createComponent(); - }); - - it('Does not pass down environments query to variable shared component', () => { - expect(findCiShared().props('queryData').environments).toBe(undefined); - }); - }); - }); }); diff --git a/spec/frontend/ci/ci_variable_list/components/ci_variable_drawer_spec.js b/spec/frontend/ci/ci_variable_list/components/ci_variable_drawer_spec.js new file mode 100644 index 00000000000..762c9611dac --- /dev/null +++ b/spec/frontend/ci/ci_variable_list/components/ci_variable_drawer_spec.js @@ -0,0 +1,69 @@ +import { GlDrawer, GlFormSelect } from '@gitlab/ui'; +import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import CiVariableDrawer from '~/ci/ci_variable_list/components/ci_variable_drawer.vue'; +import { + ADD_VARIABLE_ACTION, + variableOptions, + variableTypes, +} from '~/ci/ci_variable_list/constants'; + +describe('CI Variable Drawer', () => { + let wrapper; + + const defaultProps = { + areEnvironmentsLoading: false, + hasEnvScopeQuery: true, + mode: ADD_VARIABLE_ACTION, + }; + + const createComponent = ({ mountFn = shallowMountExtended, props = {} } = {}) => { + wrapper = mountFn(CiVariableDrawer, { + propsData: { + ...defaultProps, + ...props, + }, + provide: { + environmentScopeLink: '/help/environments', + }, + }); + }; + + const findDrawer = () => wrapper.findComponent(GlDrawer); + const findTypeDropdown = () => wrapper.findComponent(GlFormSelect); + + describe('validations', () => { + beforeEach(() => { + createComponent({ mountFn: mountExtended }); + }); + + describe('type dropdown', () => { + it('adds each type option as a dropdown item', () => { + expect(findTypeDropdown().findAll('option')).toHaveLength(variableOptions.length); + + variableOptions.forEach((v) => { + expect(findTypeDropdown().text()).toContain(v.text); + }); + }); + + it('is set to environment variable by default', () => { + expect(findTypeDropdown().findAll('option').at(0).attributes('value')).toBe( + variableTypes.envType, + ); + }); + }); + }); + + describe('drawer events', () => { + beforeEach(() => { + createComponent(); + }); + + it('emits `close-form` when closing the drawer', async () => { + expect(wrapper.emitted('close-form')).toBeUndefined(); + + await findDrawer().vm.$emit('close'); + + expect(wrapper.emitted('close-form')).toHaveLength(1); + }); + }); +}); diff --git a/spec/frontend/ci/ci_variable_list/components/ci_variable_modal_spec.js b/spec/frontend/ci/ci_variable_list/components/ci_variable_modal_spec.js index d843646df16..7dce23f72c0 100644 --- a/spec/frontend/ci/ci_variable_list/components/ci_variable_modal_spec.js +++ b/spec/frontend/ci/ci_variable_list/components/ci_variable_modal_spec.js @@ -1,4 +1,4 @@ -import { GlButton, GlFormInput } from '@gitlab/ui'; +import { GlButton, GlFormInput, GlSprintf } from '@gitlab/ui'; import { mockTracking } from 'helpers/tracking_helper'; import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper'; import CiEnvironmentsDropdown from '~/ci/ci_variable_list/components/ci_environments_dropdown.vue'; @@ -10,6 +10,8 @@ import { EVENT_LABEL, EVENT_ACTION, ENVIRONMENT_SCOPE_LINK_TITLE, + AWS_TIP_TITLE, + AWS_TIP_MESSAGE, groupString, instanceString, projectString, @@ -28,10 +30,6 @@ describe('Ci variable modal', () => { const mockVariables = mockVariablesWithScopes(instanceString); const defaultProvide = { - awsLogoSvgPath: '/logo', - awsTipCommandsLink: '/tips', - awsTipDeployLink: '/deploy', - awsTipLearnLink: '/learn-link', containsVariableReferenceLink: '/reference', environmentScopeLink: '/help/environments', glFeatures: { @@ -122,9 +120,9 @@ describe('Ci variable modal', () => { expect(wrapper.emitted('add-variable')).toEqual([[currentVariable]]); }); - it('Dispatches the `hideModal` event when dismissing', () => { + it('Dispatches the `close-form` event when dismissing', () => { findModal().vm.$emit('hidden'); - expect(wrapper.emitted('hideModal')).toEqual([[]]); + expect(wrapper.emitted('close-form')).toEqual([[]]); }); }); }); @@ -171,7 +169,7 @@ describe('Ci variable modal', () => { it('does not show AWS guidance tip', () => { const tip = findAWSTip(); - expect(tip.exists()).toBe(true); + expect(tip.isVisible()).toBe(false); }); }); @@ -184,13 +182,18 @@ describe('Ci variable modal', () => { key: AWS_ACCESS_KEY_ID, value: 'AKIAIOSFODNN7EXAMPLEjdhy', }; - createComponent({ mountFn: mountExtended, props: { selectedVariable: AWSKeyVariable } }); + createComponent({ + mountFn: shallowMountExtended, + props: { selectedVariable: AWSKeyVariable }, + }); }); it('shows AWS guidance tip', () => { const tip = findAWSTip(); - expect(tip.exists()).toBe(true); + expect(tip.isVisible()).toBe(true); + expect(tip.props('title')).toBe(AWS_TIP_TITLE); + expect(tip.findComponent(GlSprintf).attributes('message')).toBe(AWS_TIP_MESSAGE); }); }); @@ -313,9 +316,9 @@ describe('Ci variable modal', () => { expect(wrapper.emitted('update-variable')).toEqual([[variable]]); }); - it('Propagates the `hideModal` event', () => { + it('Propagates the `close-form` event', () => { findModal().vm.$emit('hidden'); - expect(wrapper.emitted('hideModal')).toEqual([[]]); + expect(wrapper.emitted('close-form')).toEqual([[]]); }); it('dispatches `delete-variable` with correct variable to delete', () => { diff --git a/spec/frontend/ci/ci_variable_list/components/ci_variable_settings_spec.js b/spec/frontend/ci/ci_variable_list/components/ci_variable_settings_spec.js index d72cfc5fc14..f5737c61eea 100644 --- a/spec/frontend/ci/ci_variable_list/components/ci_variable_settings_spec.js +++ b/spec/frontend/ci/ci_variable_list/components/ci_variable_settings_spec.js @@ -1,7 +1,9 @@ import { shallowMount } from '@vue/test-utils'; import CiVariableSettings from '~/ci/ci_variable_list/components/ci_variable_settings.vue'; -import ciVariableModal from '~/ci/ci_variable_list/components/ci_variable_modal.vue'; -import ciVariableTable from '~/ci/ci_variable_list/components/ci_variable_table.vue'; +import CiVariableModal from '~/ci/ci_variable_list/components/ci_variable_modal.vue'; +import CiVariableTable from '~/ci/ci_variable_list/components/ci_variable_table.vue'; +import CiVariableDrawer from '~/ci/ci_variable_list/components/ci_variable_drawer.vue'; + import { ADD_VARIABLE_ACTION, EDIT_VARIABLE_ACTION, @@ -27,15 +29,22 @@ describe('Ci variable table', () => { variables: mockVariablesWithScopes(projectString), }; - const findCiVariableTable = () => wrapper.findComponent(ciVariableTable); - const findCiVariableModal = () => wrapper.findComponent(ciVariableModal); + const findCiVariableDrawer = () => wrapper.findComponent(CiVariableDrawer); + const findCiVariableTable = () => wrapper.findComponent(CiVariableTable); + const findCiVariableModal = () => wrapper.findComponent(CiVariableModal); - const createComponent = ({ props = {} } = {}) => { + const createComponent = ({ props = {}, featureFlags = {} } = {}) => { wrapper = shallowMount(CiVariableSettings, { propsData: { ...defaultProps, ...props, }, + provide: { + glFeatures: { + ciVariableDrawer: false, + ...featureFlags, + }, + }, }); }; @@ -70,51 +79,51 @@ describe('Ci variable table', () => { }); }); - describe('modal mode', () => { + describe.each` + bool | flagStatus | elementName | findElement + ${false} | ${'disabled'} | ${'modal'} | ${findCiVariableModal} + ${true} | ${'enabled'} | ${'drawer'} | ${findCiVariableDrawer} + `('when ciVariableDrawer feature flag is $flagStatus', ({ bool, elementName, findElement }) => { beforeEach(() => { - createComponent(); + createComponent({ featureFlags: { ciVariableDrawer: bool } }); }); - it('passes down ADD mode when receiving an empty variable', async () => { - await findCiVariableTable().vm.$emit('set-selected-variable'); - - expect(findCiVariableModal().props('mode')).toBe(ADD_VARIABLE_ACTION); + it(`${elementName} is hidden by default`, () => { + expect(findElement().exists()).toBe(false); }); - it('passes down EDIT mode when receiving a variable', async () => { - await findCiVariableTable().vm.$emit('set-selected-variable', newVariable); + it(`shows ${elementName} when adding a new variable`, async () => { + await findCiVariableTable().vm.$emit('set-selected-variable'); - expect(findCiVariableModal().props('mode')).toBe(EDIT_VARIABLE_ACTION); + expect(findElement().exists()).toBe(true); }); - }); - describe('variable modal', () => { - beforeEach(() => { - createComponent(); - }); + it(`shows ${elementName} when updating a variable`, async () => { + await findCiVariableTable().vm.$emit('set-selected-variable', newVariable); - it('is hidden by default', () => { - expect(findCiVariableModal().exists()).toBe(false); + expect(findElement().exists()).toBe(true); }); - it('shows modal when adding a new variable', async () => { + it(`hides ${elementName} when closing the form`, async () => { await findCiVariableTable().vm.$emit('set-selected-variable'); - expect(findCiVariableModal().exists()).toBe(true); - }); + expect(findElement().isVisible()).toBe(true); - it('shows modal when updating a variable', async () => { - await findCiVariableTable().vm.$emit('set-selected-variable', newVariable); + await findElement().vm.$emit('close-form'); - expect(findCiVariableModal().exists()).toBe(true); + expect(findElement().exists()).toBe(false); }); - it('hides modal when receiving the event from the modal', async () => { + it(`passes down ADD mode to ${elementName} when receiving an empty variable`, async () => { await findCiVariableTable().vm.$emit('set-selected-variable'); - await findCiVariableModal().vm.$emit('hideModal'); + expect(findElement().props('mode')).toBe(ADD_VARIABLE_ACTION); + }); + + it(`passes down EDIT mode to ${elementName} when receiving a variable`, async () => { + await findCiVariableTable().vm.$emit('set-selected-variable', newVariable); - expect(findCiVariableModal().exists()).toBe(false); + expect(findElement().props('mode')).toBe(EDIT_VARIABLE_ACTION); }); }); diff --git a/spec/frontend/ci/ci_variable_list/components/ci_variable_table_spec.js b/spec/frontend/ci/ci_variable_list/components/ci_variable_table_spec.js index f3f1c5bd2c5..39c03a41660 100644 --- a/spec/frontend/ci/ci_variable_list/components/ci_variable_table_spec.js +++ b/spec/frontend/ci/ci_variable_list/components/ci_variable_table_spec.js @@ -1,4 +1,4 @@ -import { GlAlert, GlBadge, GlKeysetPagination } from '@gitlab/ui'; +import { GlAlert, GlBadge, GlKeysetPagination, GlCard, GlIcon } from '@gitlab/ui'; import { sprintf } from '~/locale'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import CiVariableTable from '~/ci/ci_variable_list/components/ci_variable_table.vue'; @@ -36,7 +36,7 @@ describe('Ci variable table', () => { }; const findRevealButton = () => wrapper.findByText('Reveal values'); - const findAddButton = () => wrapper.findByLabelText('Add'); + const findAddButton = () => wrapper.findByTestId('add-ci-variable-button'); const findEditButton = () => wrapper.findByLabelText('Edit'); const findEmptyVariablesPlaceholder = () => wrapper.findByText('There are no variables yet.'); const findHiddenValues = () => wrapper.findAllByTestId('hiddenValue'); @@ -50,11 +50,30 @@ describe('Ci variable table', () => { const findGroupCiCdSettingsLink = (rowIndex) => wrapper.findAllByTestId('ci-variable-table-row-cicd-path').at(rowIndex).attributes('href'); const findKeysetPagination = () => wrapper.findComponent(GlKeysetPagination); + const findCard = () => wrapper.findComponent(GlCard); const generateExceedsVariableLimitText = (entity, currentVariableCount, maxVariableLimit) => { return sprintf(EXCEEDS_VARIABLE_LIMIT_TEXT, { entity, currentVariableCount, maxVariableLimit }); }; + describe('card', () => { + it('displays the correct title', () => { + createComponent(); + expect(findCard().text()).toContain('CI/CD Variables'); + }); + + it('displays the correct icon', () => { + createComponent(); + expect(findCard().findComponent(GlIcon).props('name')).toBe('code'); + }); + + it('displays the number of added CI/CD Variables', () => { + const variables = [1, 2, 3]; + createComponent({ props: { variables } }); + expect(findCard().text()).toContain(String(variables.length)); + }); + }); + describe.each` isVariablePagesEnabled | text ${true} | ${'enabled'} @@ -88,7 +107,7 @@ describe('Ci variable table', () => { ${1} | ${'Value'} ${2} | ${'Attributes'} ${3} | ${'Environments'} - ${4} | ${''} + ${4} | ${'Actions'} `('renders the $text column', ({ index, text }) => { expect(findTableColumnText(index)).toEqual(text); }); diff --git a/spec/frontend/ci/pipeline_editor/components/file-nav/branch_switcher_spec.js b/spec/frontend/ci/pipeline_editor/components/file-nav/branch_switcher_spec.js index 3a99949413b..4057759b9b9 100644 --- a/spec/frontend/ci/pipeline_editor/components/file-nav/branch_switcher_spec.js +++ b/spec/frontend/ci/pipeline_editor/components/file-nav/branch_switcher_spec.js @@ -1,3 +1,5 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; import { GlDropdown, GlDropdownItem, @@ -5,8 +7,7 @@ import { GlLoadingIcon, GlSearchBoxByType, } from '@gitlab/ui'; -import { createLocalVue, mount, shallowMount } from '@vue/test-utils'; -import VueApollo from 'vue-apollo'; +import { shallowMount } from '@vue/test-utils'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; import BranchSwitcher from '~/ci/pipeline_editor/components/file_nav/branch_switcher.vue'; @@ -17,10 +18,10 @@ import getLastCommitBranch from '~/ci/pipeline_editor/graphql/queries/client/las import { resolvers } from '~/ci/pipeline_editor/graphql/resolvers'; import { + generateMockProjectBranches, mockBranchPaginationLimit, mockDefaultBranch, mockEmptySearchBranches, - mockProjectBranches, mockProjectFullPath, mockSearchBranches, mockTotalBranches, @@ -28,55 +29,14 @@ import { mockTotalSearchResults, } from '../../mock_data'; -const localVue = createLocalVue(); -localVue.use(VueApollo); - describe('Pipeline editor branch switcher', () => { let wrapper; let mockApollo; let mockAvailableBranchQuery; - const createComponent = ({ - currentBranch = mockDefaultBranch, - availableBranches = ['main'], - isQueryLoading = false, - mountFn = shallowMount, - options = {}, - props = {}, - } = {}) => { - wrapper = mountFn(BranchSwitcher, { - propsData: { - ...props, - paginationLimit: mockBranchPaginationLimit, - }, - provide: { - projectFullPath: mockProjectFullPath, - totalBranches: mockTotalBranches, - }, - mocks: { - $apollo: { - queries: { - availableBranches: { - loading: isQueryLoading, - }, - }, - }, - }, - data() { - return { - availableBranches, - currentBranch, - }; - }, - ...options, - }); - }; + Vue.use(VueApollo); - const createComponentWithApollo = ({ - mountFn = shallowMount, - props = {}, - availableBranches = ['main'], - } = {}) => { + const createComponent = ({ props = {} } = {}) => { const handlers = [[getAvailableBranchesQuery, mockAvailableBranchQuery]]; mockApollo = createMockApollo(handlers, resolvers); @@ -106,16 +66,19 @@ describe('Pipeline editor branch switcher', () => { }, }); - createComponent({ - mountFn, - props, - availableBranches, - options: { - localVue, - apolloProvider: mockApollo, - mocks: {}, + wrapper = shallowMount(BranchSwitcher, { + propsData: { + ...props, + paginationLimit: mockBranchPaginationLimit, + }, + provide: { + projectFullPath: mockProjectFullPath, + totalBranches: mockTotalBranches, }, + apolloProvider: mockApollo, }); + + return waitForPromises(); }; const findDropdown = () => wrapper.findComponent(GlDropdown); @@ -137,7 +100,7 @@ describe('Pipeline editor branch switcher', () => { expect(wrapper.emitted('showError')).toBeDefined(); expect(wrapper.emitted('showError')[0]).toEqual([ { - reasons: [wrapper.vm.$options.i18n.fetchError], + reasons: ['Unable to fetch branch list for this project.'], type: DEFAULT_FAILURE, }, ]); @@ -145,19 +108,26 @@ describe('Pipeline editor branch switcher', () => { describe('when querying for the first time', () => { beforeEach(() => { - createComponentWithApollo({ availableBranches: [] }); + createComponent(); }); it('disables the dropdown', () => { expect(findDropdown().props('disabled')).toBe(true); }); + + it('shows loading icon', () => { + expect(findLoadingIcon().exists()).toBe(true); + }); }); describe('after querying', () => { beforeEach(async () => { - setAvailableBranchesMock(mockProjectBranches); - createComponentWithApollo({ mountFn: mount }); - await waitForPromises(); + setAvailableBranchesMock(generateMockProjectBranches()); + await createComponent(); + }); + + it('does not render the loading icon', () => { + expect(findLoadingIcon().exists()).toBe(false); }); it('renders search box', () => { @@ -185,8 +155,7 @@ describe('Pipeline editor branch switcher', () => { describe('on fetch error', () => { beforeEach(async () => { setAvailableBranchesMock(new Error()); - createComponentWithApollo({ availableBranches: [] }); - await waitForPromises(); + await createComponent(); }); it('does not render dropdown', () => { @@ -201,9 +170,8 @@ describe('Pipeline editor branch switcher', () => { describe('when switching branches', () => { beforeEach(async () => { jest.spyOn(window.history, 'pushState').mockImplementation(() => {}); - setAvailableBranchesMock(mockProjectBranches); - createComponentWithApollo({ mountFn: mount }); - await waitForPromises(); + setAvailableBranchesMock(generateMockProjectBranches()); + await createComponent(); }); it('updates session history when selecting a different branch', async () => { @@ -251,7 +219,7 @@ describe('Pipeline editor branch switcher', () => { describe('with unsaved changes', () => { beforeEach(async () => { - createComponentWithApollo({ mountFn: mount, props: { hasUnsavedChanges: true } }); + createComponent({ props: { hasUnsavedChanges: true } }); await waitForPromises(); }); @@ -269,9 +237,8 @@ describe('Pipeline editor branch switcher', () => { describe('when searching', () => { beforeEach(async () => { - setAvailableBranchesMock(mockProjectBranches); - createComponentWithApollo({ mountFn: mount }); - await waitForPromises(); + setAvailableBranchesMock(generateMockProjectBranches()); + await createComponent(); }); afterEach(() => { @@ -329,7 +296,7 @@ describe('Pipeline editor branch switcher', () => { findSearchBox().vm.$emit('input', 'te'); await waitForPromises(); - mockAvailableBranchQuery.mockResolvedValue(mockProjectBranches); + mockAvailableBranchQuery.mockResolvedValue(generateMockProjectBranches()); }); it('calls query with correct variables', async () => { @@ -355,23 +322,10 @@ describe('Pipeline editor branch switcher', () => { }); }); - describe('loading icon', () => { - it.each` - isQueryLoading | isRendered - ${true} | ${true} - ${false} | ${false} - `('checks if query is loading before rendering', ({ isQueryLoading, isRendered }) => { - createComponent({ isQueryLoading, mountFn: mount }); - - expect(findLoadingIcon().exists()).toBe(isRendered); - }); - }); - describe('when scrolling to the bottom of the list', () => { beforeEach(async () => { - setAvailableBranchesMock(mockProjectBranches); - createComponentWithApollo(); - await waitForPromises(); + setAvailableBranchesMock(generateMockProjectBranches()); + await createComponent(); }); afterEach(() => { @@ -382,6 +336,7 @@ describe('Pipeline editor branch switcher', () => { it('fetches more branches', async () => { expect(mockAvailableBranchQuery).toHaveBeenCalledTimes(1); + setAvailableBranchesMock(generateMockProjectBranches('new-')); findInfiniteScroll().vm.$emit('bottomReached'); await waitForPromises(); @@ -389,6 +344,7 @@ describe('Pipeline editor branch switcher', () => { }); it('calls the query with the correct variables', async () => { + setAvailableBranchesMock(generateMockProjectBranches('new-')); findInfiniteScroll().vm.$emit('bottomReached'); await waitForPromises(); diff --git a/spec/frontend/ci/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js b/spec/frontend/ci/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js index 29759f828e4..f5e0b65d615 100644 --- a/spec/frontend/ci/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js +++ b/spec/frontend/ci/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js @@ -4,7 +4,7 @@ import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; import PipelineEditorMiniGraph from '~/ci/pipeline_editor/components/header/pipeline_editor_mini_graph.vue'; -import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue'; +import LegacyPipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/legacy_pipeline_mini_graph.vue'; import getLinkedPipelinesQuery from '~/pipelines/graphql/queries/get_linked_pipelines.query.graphql'; import { PIPELINE_FAILURE } from '~/ci/pipeline_editor/constants'; import { mockLinkedPipelines, mockProjectFullPath, mockProjectPipeline } from '../../mock_data'; @@ -41,7 +41,7 @@ describe('Pipeline Status', () => { }); }; - const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph); + const findLegacyPipelineMiniGraph = () => wrapper.findComponent(LegacyPipelineMiniGraph); beforeEach(() => { mockLinkedPipelinesQuery = jest.fn(); @@ -53,7 +53,7 @@ describe('Pipeline Status', () => { }); it('renders pipeline mini graph', () => { - expect(findPipelineMiniGraph().exists()).toBe(true); + expect(findLegacyPipelineMiniGraph().exists()).toBe(true); }); }); @@ -63,7 +63,7 @@ describe('Pipeline Status', () => { }); it('does not render pipeline mini graph', () => { - expect(findPipelineMiniGraph().exists()).toBe(false); + expect(findLegacyPipelineMiniGraph().exists()).toBe(false); }); }); @@ -85,7 +85,7 @@ describe('Pipeline Status', () => { }); it('renders only the latest downstream pipelines', () => { - expect(findPipelineMiniGraph().props('downstreamPipelines')).toHaveLength(1); + expect(findLegacyPipelineMiniGraph().props('downstreamPipelines')).toHaveLength(1); }); }); diff --git a/spec/frontend/ci/pipeline_editor/components/header/pipeline_status_spec.js b/spec/frontend/ci/pipeline_editor/components/header/pipeline_status_spec.js index 9d93ba332e9..3bbe14adb88 100644 --- a/spec/frontend/ci/pipeline_editor/components/header/pipeline_status_spec.js +++ b/spec/frontend/ci/pipeline_editor/components/header/pipeline_status_spec.js @@ -6,7 +6,7 @@ import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; import PipelineStatus, { i18n } from '~/ci/pipeline_editor/components/header/pipeline_status.vue'; import getPipelineQuery from '~/ci/pipeline_editor/graphql/queries/pipeline.query.graphql'; -import GraphqlPipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/graphql_pipeline_mini_graph.vue'; +import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue'; import PipelineEditorMiniGraph from '~/ci/pipeline_editor/components/header/pipeline_editor_mini_graph.vue'; import { mockCommitSha, mockProjectPipeline, mockProjectFullPath } from '../../mock_data'; @@ -38,8 +38,9 @@ describe('Pipeline Status', () => { const findIcon = () => wrapper.findComponent(GlIcon); const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); - const findGraphqlPipelineMiniGraph = () => wrapper.findComponent(GraphqlPipelineMiniGraph); const findPipelineEditorMiniGraph = () => wrapper.findComponent(PipelineEditorMiniGraph); + const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph); + const findPipelineId = () => wrapper.find('[data-testid="pipeline-id"]'); const findPipelineCommit = () => wrapper.find('[data-testid="pipeline-commit"]'); const findPipelineErrorMsg = () => wrapper.find('[data-testid="pipeline-error-msg"]'); @@ -142,18 +143,18 @@ describe('Pipeline Status', () => { }); it.each` - state | provide | showPipelineMiniGraph | showGraphqlPipelineMiniGraph - ${true} | ${{ ciGraphqlPipelineMiniGraph: true }} | ${false} | ${true} - ${false} | ${{}} | ${true} | ${false} + state | showLegacyPipelineMiniGraph | showPipelineMiniGraph + ${true} | ${false} | ${true} + ${false} | ${true} | ${false} `( 'renders the correct component when the feature flag is set to $state', - async ({ provide, showPipelineMiniGraph, showGraphqlPipelineMiniGraph }) => { - createComponentWithApollo(provide); + async ({ state, showLegacyPipelineMiniGraph, showPipelineMiniGraph }) => { + createComponentWithApollo({ ciGraphqlPipelineMiniGraph: state }); await waitForPromises(); - expect(findPipelineEditorMiniGraph().exists()).toBe(showPipelineMiniGraph); - expect(findGraphqlPipelineMiniGraph().exists()).toBe(showGraphqlPipelineMiniGraph); + expect(findPipelineEditorMiniGraph().exists()).toBe(showLegacyPipelineMiniGraph); + expect(findPipelineMiniGraph().exists()).toBe(showPipelineMiniGraph); }, ); }); diff --git a/spec/frontend/ci/pipeline_editor/mock_data.js b/spec/frontend/ci/pipeline_editor/mock_data.js index a3294cdc269..007abde939f 100644 --- a/spec/frontend/ci/pipeline_editor/mock_data.js +++ b/spec/frontend/ci/pipeline_editor/mock_data.js @@ -1,5 +1,6 @@ import { CI_CONFIG_STATUS_INVALID, CI_CONFIG_STATUS_VALID } from '~/ci/pipeline_editor/constants'; import { unwrapStagesWithNeeds } from '~/pipelines/components/unwrapping_utils'; +import { DOCS_URL_IN_EE_DIR } from 'jh_else_ce/lib/utils/url_utility'; export const commonOptions = { ciConfigPath: '/ci/config', @@ -295,7 +296,7 @@ export const mockEmptyCommitShaResults = { }, }; -export const mockProjectBranches = { +export const generateMockProjectBranches = (prefix = '') => ({ data: { project: { id: '1', @@ -311,14 +312,14 @@ export const mockProjectBranches = { 'mock-feature', 'test-merge-request', 'staging', - ], + ].map((branch) => `${prefix}${branch}`), }, }, }, -}; +}); -export const mockTotalBranchResults = - mockProjectBranches.data.project.repository.branchNames.length; +export const mockTotalBranchResults = generateMockProjectBranches().data.project.repository + .branchNames.length; export const mockSearchBranches = { data: { @@ -601,7 +602,7 @@ export const mockErrors = [ ]; export const mockWarnings = [ - '"jobs:multi_project_job may allow multiple pipelines to run for a single action due to `rules:when` clause with no `workflow:rules` - read more: https://docs.gitlab.com/ee/ci/troubleshooting.html#pipeline-warnings"', + `"jobs:multi_project_job may allow multiple pipelines to run for a single action due to \`rules:when\` clause with no \`workflow:rules\` - read more: ${DOCS_URL_IN_EE_DIR}/ci/troubleshooting.html#pipeline-warnings"`, ]; export const mockCommitCreateResponse = { diff --git a/spec/frontend/ci/pipeline_editor/pipeline_editor_home_spec.js b/spec/frontend/ci/pipeline_editor/pipeline_editor_home_spec.js index 576263d5418..ca5f80f331c 100644 --- a/spec/frontend/ci/pipeline_editor/pipeline_editor_home_spec.js +++ b/spec/frontend/ci/pipeline_editor/pipeline_editor_home_spec.js @@ -1,19 +1,19 @@ import { shallowMount } from '@vue/test-utils'; -import { nextTick } from 'vue'; -import { GlButton, GlDrawer, GlModal } from '@gitlab/ui'; +import { GlButton, GlModal } from '@gitlab/ui'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import setWindowLocation from 'helpers/set_window_location_helper'; -import CiEditorHeader from '~/ci/pipeline_editor/components/editor/ci_editor_header.vue'; import CommitSection from '~/ci/pipeline_editor/components/commit/commit_section.vue'; import PipelineEditorDrawer from '~/ci/pipeline_editor/components/drawer/pipeline_editor_drawer.vue'; import JobAssistantDrawer from '~/ci/pipeline_editor/components/job_assistant_drawer/job_assistant_drawer.vue'; import PipelineEditorFileNav from '~/ci/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue'; import PipelineEditorFileTree from '~/ci/pipeline_editor/components/file_tree/container.vue'; -import BranchSwitcher from '~/ci/pipeline_editor/components/file_nav/branch_switcher.vue'; import PipelineEditorHeader from '~/ci/pipeline_editor/components/header/pipeline_editor_header.vue'; import PipelineEditorTabs from '~/ci/pipeline_editor/components/pipeline_editor_tabs.vue'; import { CREATE_TAB, + EDITOR_APP_DRAWER_HELP, + EDITOR_APP_DRAWER_JOB_ASSISTANT, + EDITOR_APP_DRAWER_NONE, FILE_TREE_DISPLAY_KEY, VALIDATE_TAB, MERGED_TAB, @@ -29,10 +29,9 @@ jest.mock('~/lib/utils/common_utils'); describe('Pipeline editor home wrapper', () => { let wrapper; - const createComponent = ({ props = {}, glFeatures = {}, data = {}, stubs = {} } = {}) => { + const createComponent = ({ props = {}, glFeatures = {}, stubs = {} } = {}) => { wrapper = extendedWrapper( shallowMount(PipelineEditorHome, { - data: () => data, propsData: { ciConfigData: mockLintResponse, ciFileContent: mockCiYml, @@ -53,7 +52,6 @@ describe('Pipeline editor home wrapper', () => { ); }; - const findBranchSwitcher = () => wrapper.findComponent(BranchSwitcher); const findCommitSection = () => wrapper.findComponent(CommitSection); const findFileNav = () => wrapper.findComponent(PipelineEditorFileNav); const findModal = () => wrapper.findComponent(GlModal); @@ -63,8 +61,16 @@ describe('Pipeline editor home wrapper', () => { const findPipelineEditorHeader = () => wrapper.findComponent(PipelineEditorHeader); const findPipelineEditorTabs = () => wrapper.findComponent(PipelineEditorTabs); const findFileTreeBtn = () => wrapper.findByTestId('file-tree-toggle'); - const findHelpBtn = () => wrapper.findByTestId('drawer-toggle'); - const findJobAssistantBtn = () => wrapper.findByTestId('job-assistant-drawer-toggle'); + + const clickHelpBtn = async () => { + await findPipelineEditorDrawer().vm.$emit('switch-drawer', EDITOR_APP_DRAWER_HELP); + }; + const clickJobAssistantBtn = async () => { + await findJobAssistantDrawer().vm.$emit('switch-drawer', EDITOR_APP_DRAWER_JOB_ASSISTANT); + }; + const closeDrawer = async (finder) => { + await finder().vm.$emit('switch-drawer', EDITOR_APP_DRAWER_NONE); + }; afterEach(() => { localStorage.clear(); @@ -103,11 +109,9 @@ describe('Pipeline editor home wrapper', () => { }); }); describe('when `showSwitchBranchModal` value is true', () => { - beforeEach(() => { - createComponent({ - data: { showSwitchBranchModal: true }, - stubs: { PipelineEditorFileNav }, - }); + beforeEach(async () => { + createComponent(); + await findFileNav().vm.$emit('select-branch'); }); it('is visible', () => { @@ -115,11 +119,11 @@ describe('Pipeline editor home wrapper', () => { }); it('pass down `shouldLoadNewBranch` to the branch switcher when primary is selected', async () => { - expect(findBranchSwitcher().props('shouldLoadNewBranch')).toBe(false); + expect(findFileNav().props('shouldLoadNewBranch')).toBe(false); await findModal().vm.$emit('primary'); - expect(findBranchSwitcher().props('shouldLoadNewBranch')).toBe(true); + expect(findFileNav().props('shouldLoadNewBranch')).toBe(true); }); it('closes the modal when secondary action is selected', async () => { @@ -148,9 +152,7 @@ describe('Pipeline editor home wrapper', () => { async ({ tab, shouldShow }) => { expect(findCommitSection().exists()).toBe(true); - findPipelineEditorTabs().vm.$emit('set-current-tab', tab); - - await nextTick(); + await findPipelineEditorTabs().vm.$emit('set-current-tab', tab); expect(findCommitSection().isVisible()).toBe(shouldShow); }, @@ -159,12 +161,10 @@ describe('Pipeline editor home wrapper', () => { it('shows the commit form again when coming back to the create tab', async () => { expect(findCommitSection().isVisible()).toBe(true); - findPipelineEditorTabs().vm.$emit('set-current-tab', MERGED_TAB); - await nextTick(); + await findPipelineEditorTabs().vm.$emit('set-current-tab', MERGED_TAB); expect(findCommitSection().isVisible()).toBe(false); - findPipelineEditorTabs().vm.$emit('set-current-tab', CREATE_TAB); - await nextTick(); + await findPipelineEditorTabs().vm.$emit('set-current-tab', CREATE_TAB); expect(findCommitSection().isVisible()).toBe(true); }); @@ -195,7 +195,9 @@ describe('Pipeline editor home wrapper', () => { describe('when "walkthrough-popover-cta-clicked" is emitted from pipeline editor tabs', () => { it('passes down `scrollToCommitForm=true` to commit section', async () => { expect(findCommitSection().props('scrollToCommitForm')).toBe(false); + await findPipelineEditorTabs().vm.$emit('walkthrough-popover-cta-clicked'); + expect(findCommitSection().props('scrollToCommitForm')).toBe(true); }); }); @@ -204,6 +206,7 @@ describe('Pipeline editor home wrapper', () => { it('passes down `scrollToCommitForm=false` to commit section', async () => { await findPipelineEditorTabs().vm.$emit('walkthrough-popover-cta-clicked'); expect(findCommitSection().props('scrollToCommitForm')).toBe(true); + await findCommitSection().vm.$emit('scrolled-to-commit-form'); expect(findCommitSection().props('scrollToCommitForm')).toBe(false); }); @@ -211,133 +214,49 @@ describe('Pipeline editor home wrapper', () => { }); describe('help drawer', () => { - const clickHelpBtn = async () => { - findHelpBtn().vm.$emit('click'); - await nextTick(); - }; - - it('hides the drawer by default', () => { + beforeEach(() => { createComponent(); + }); + it('hides the drawer by default', () => { expect(findPipelineEditorDrawer().props('isVisible')).toBe(false); }); it('toggles the drawer on button click', async () => { - createComponent({ - stubs: { - CiEditorHeader, - GlButton, - GlDrawer, - PipelineEditorTabs, - PipelineEditorDrawer, - }, - }); - - await clickHelpBtn(); - - expect(findPipelineEditorDrawer().props('isVisible')).toBe(true); - - await clickHelpBtn(); - expect(findPipelineEditorDrawer().props('isVisible')).toBe(false); - }); - - it("closes the drawer through the drawer's close button", async () => { - createComponent({ - stubs: { - CiEditorHeader, - GlButton, - GlDrawer, - PipelineEditorTabs, - PipelineEditorDrawer, - }, - }); await clickHelpBtn(); - expect(findPipelineEditorDrawer().props('isVisible')).toBe(true); - findPipelineEditorDrawer().findComponent(GlDrawer).vm.$emit('close'); - await nextTick(); - + await closeDrawer(findPipelineEditorDrawer); expect(findPipelineEditorDrawer().props('isVisible')).toBe(false); }); }); describe('job assistant drawer', () => { - const clickHelpBtn = async () => { - findHelpBtn().vm.$emit('click'); - await nextTick(); - }; - const clickJobAssistantBtn = async () => { - findJobAssistantBtn().vm.$emit('click'); - await nextTick(); - }; - - const stubs = { - CiEditorHeader, - GlButton, - GlDrawer, - PipelineEditorTabs, - JobAssistantDrawer, - }; - - it('hides the job assistant drawer by default', () => { + beforeEach(() => { createComponent({ glFeatures: { ciJobAssistantDrawer: true, }, }); + }); + it('hides the job assistant drawer by default', () => { expect(findJobAssistantDrawer().props('isVisible')).toBe(false); }); it('toggles the job assistant drawer on button click', async () => { - createComponent({ - stubs, - glFeatures: { - ciJobAssistantDrawer: true, - }, - }); - - await clickJobAssistantBtn(); - - expect(findJobAssistantDrawer().props('isVisible')).toBe(true); - - await clickJobAssistantBtn(); - expect(findJobAssistantDrawer().props('isVisible')).toBe(false); - }); - - it("closes the job assistant drawer through the drawer's close button", async () => { - createComponent({ - stubs, - glFeatures: { - ciJobAssistantDrawer: true, - }, - }); await clickJobAssistantBtn(); - expect(findJobAssistantDrawer().props('isVisible')).toBe(true); - findJobAssistantDrawer().findComponent(GlDrawer).vm.$emit('close'); - await nextTick(); - + await closeDrawer(findJobAssistantDrawer); expect(findJobAssistantDrawer().props('isVisible')).toBe(false); }); it('covers helper drawer when opened last', async () => { - createComponent({ - stubs: { - ...stubs, - PipelineEditorDrawer, - }, - glFeatures: { - ciJobAssistantDrawer: true, - }, - }); - await clickHelpBtn(); await clickJobAssistantBtn(); @@ -348,16 +267,6 @@ describe('Pipeline editor home wrapper', () => { }); it('covered by helper drawer when opened first', async () => { - createComponent({ - stubs: { - ...stubs, - PipelineEditorDrawer, - }, - glFeatures: { - ciJobAssistantDrawer: true, - }, - }); - await clickJobAssistantBtn(); await clickHelpBtn(); @@ -370,8 +279,7 @@ describe('Pipeline editor home wrapper', () => { describe('file tree', () => { const toggleFileTree = async () => { - findFileTreeBtn().vm.$emit('click'); - await nextTick(); + await findFileTreeBtn().vm.$emit('click'); }; describe('button toggle', () => { @@ -412,9 +320,7 @@ describe('Pipeline editor home wrapper', () => { describe('when file tree display state is saved in local storage', () => { beforeEach(() => { localStorage.setItem(FILE_TREE_DISPLAY_KEY, 'true'); - createComponent({ - stubs: { PipelineEditorFileNav }, - }); + createComponent(); }); it('shows the file tree by default', () => { @@ -424,9 +330,7 @@ describe('Pipeline editor home wrapper', () => { describe('when file tree display state is not saved in local storage', () => { beforeEach(() => { - createComponent({ - stubs: { PipelineEditorFileNav }, - }); + createComponent(); }); it('hides the file tree by default', () => { diff --git a/spec/frontend/ci/pipeline_new/mock_data.js b/spec/frontend/ci/pipeline_new/mock_data.js index 76a88f63298..72a491bb946 100644 --- a/spec/frontend/ci/pipeline_new/mock_data.js +++ b/spec/frontend/ci/pipeline_new/mock_data.js @@ -1,3 +1,5 @@ +import { DOCS_URL_IN_EE_DIR } from 'jh_else_ce/lib/utils/url_utility'; + export const mockFilteredRefs = { Branches: ['branch-1'], Tags: ['1.0.0', '1.1.0'], @@ -28,9 +30,9 @@ export const mockError = { 'test job: chosen stage does not exist; available stages are .pre, build, test, deploy, .post', ], warnings: [ - 'jobs:build1 may allow multiple pipelines to run for a single action due to `rules:when` clause with no `workflow:rules` - read more: https://docs.gitlab.com/ee/ci/troubleshooting.html#pipeline-warnings', - 'jobs:build2 may allow multiple pipelines to run for a single action due to `rules:when` clause with no `workflow:rules` - read more: https://docs.gitlab.com/ee/ci/troubleshooting.html#pipeline-warnings', - 'jobs:build3 may allow multiple pipelines to run for a single action due to `rules:when` clause with no `workflow:rules` - read more: https://docs.gitlab.com/ee/ci/troubleshooting.html#pipeline-warnings', + `jobs:build1 may allow multiple pipelines to run for a single action due to \`rules:when\` clause with no \`workflow:rules\` - read more: ${DOCS_URL_IN_EE_DIR}/ci/troubleshooting.html#pipeline-warnings`, + `jobs:build2 may allow multiple pipelines to run for a single action due to \`rules:when\` clause with no \`workflow:rules\` - read more: ${DOCS_URL_IN_EE_DIR}/ci/troubleshooting.html#pipeline-warnings`, + `jobs:build3 may allow multiple pipelines to run for a single action due to \`rules:when\` clause with no \`workflow:rules\` - read more: ${DOCS_URL_IN_EE_DIR}/ci/troubleshooting.html#pipeline-warnings`, ], total_warnings: 7, }; diff --git a/spec/frontend/ci/pipeline_schedules/components/pipeline_schedules_form_spec.js b/spec/frontend/ci/pipeline_schedules/components/pipeline_schedules_form_spec.js index bb48d4dc38d..79a0cfa0dc9 100644 --- a/spec/frontend/ci/pipeline_schedules/components/pipeline_schedules_form_spec.js +++ b/spec/frontend/ci/pipeline_schedules/components/pipeline_schedules_form_spec.js @@ -21,6 +21,7 @@ import { createScheduleMutationResponse, updateScheduleMutationResponse, mockSinglePipelineScheduleNode, + mockSinglePipelineScheduleNodeNoVars, } from '../mock_data'; Vue.use(VueApollo); @@ -51,6 +52,9 @@ describe('Pipeline schedules form', () => { const dailyLimit = ''; const querySuccessHandler = jest.fn().mockResolvedValue(mockSinglePipelineScheduleNode); + const querySuccessEmptyVarsHandler = jest + .fn() + .mockResolvedValue(mockSinglePipelineScheduleNodeNoVars); const queryFailedHandler = jest.fn().mockRejectedValue(new Error('GraphQL error')); const createMutationHandlerSuccess = jest.fn().mockResolvedValue(createScheduleMutationResponse); @@ -95,6 +99,10 @@ describe('Pipeline schedules form', () => { const findVariableRows = () => wrapper.findAllByTestId('ci-variable-row'); const findKeyInputs = () => wrapper.findAllByTestId('pipeline-form-ci-variable-key'); const findValueInputs = () => wrapper.findAllByTestId('pipeline-form-ci-variable-value'); + const findHiddenValueInputs = () => + wrapper.findAllByTestId('pipeline-form-ci-variable-hidden-value'); + const findVariableSecurityBtn = () => wrapper.findByTestId('variable-security-btn'); + const findRemoveIcons = () => wrapper.findAllByTestId('remove-ci-variable-row'); const addVariableToForm = () => { @@ -241,6 +249,12 @@ describe('Pipeline schedules form', () => { expect(findLoadingIcon().exists()).toBe(false); }); + it('does not show variable security button', () => { + createComponent(); + + expect(findVariableSecurityBtn().exists()).toBe(false); + }); + describe('schedule creation success', () => { let mock; @@ -336,6 +350,26 @@ describe('Pipeline schedules form', () => { expect(findLoadingIcon().exists()).toBe(false); }); + it('shows variable security button', async () => { + createComponent(shallowMountExtended, true, [ + [getPipelineSchedulesQuery, querySuccessHandler], + ]); + + await waitForPromises(); + + expect(findVariableSecurityBtn().exists()).toBe(true); + }); + + it('does not show variable security button with no present variables', async () => { + createComponent(shallowMountExtended, true, [ + [getPipelineSchedulesQuery, querySuccessEmptyVarsHandler], + ]); + + await waitForPromises(); + + expect(findVariableSecurityBtn().exists()).toBe(false); + }); + describe('schedule fetch success', () => { it('fetches schedule and sets form data correctly', async () => { createComponent(mountExtended, true, [[getPipelineSchedulesQuery, querySuccessHandler]]); @@ -351,8 +385,13 @@ describe('Pipeline schedules form', () => { expect(findVariableRows()).toHaveLength(3); expect(findKeyInputs().at(0).element.value).toBe(variables[0].key); expect(findKeyInputs().at(1).element.value).toBe(variables[1].key); - expect(findValueInputs().at(0).element.value).toBe(variables[0].value); - expect(findValueInputs().at(1).element.value).toBe(variables[1].value); + // values are hidden on load when editing a schedule + expect(findHiddenValueInputs().at(0).element.value).toBe('*****************'); + expect(findHiddenValueInputs().at(1).element.value).toBe('*****************'); + expect(findHiddenValueInputs().at(0).attributes('disabled')).toBe('disabled'); + expect(findHiddenValueInputs().at(1).attributes('disabled')).toBe('disabled'); + // empty placeholder to create a new variable + expect(findValueInputs()).toHaveLength(1); }); }); @@ -432,5 +471,23 @@ describe('Pipeline schedules form', () => { message: 'An error occurred while updating the pipeline schedule.', }); }); + + it('hides/shows variable values', async () => { + createComponent(mountExtended, true, [[getPipelineSchedulesQuery, querySuccessHandler]]); + + await waitForPromises(); + + // shows two hidden values and one placeholder + expect(findHiddenValueInputs()).toHaveLength(2); + expect(findValueInputs()).toHaveLength(1); + + findVariableSecurityBtn().vm.$emit('click'); + + await nextTick(); + + // shows all variable values + expect(findHiddenValueInputs()).toHaveLength(0); + expect(findValueInputs()).toHaveLength(3); + }); }); }); diff --git a/spec/frontend/ci/pipeline_schedules/components/pipeline_schedules_spec.js b/spec/frontend/ci/pipeline_schedules/components/pipeline_schedules_spec.js index 01a19711264..eb76b0bfbb4 100644 --- a/spec/frontend/ci/pipeline_schedules/components/pipeline_schedules_spec.js +++ b/spec/frontend/ci/pipeline_schedules/components/pipeline_schedules_spec.js @@ -3,6 +3,7 @@ import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; import { trimText } from 'helpers/text_helper'; import createMockApollo from 'helpers/mock_apollo_helper'; +import setWindowLocation from 'helpers/set_window_location_helper'; import waitForPromises from 'helpers/wait_for_promises'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import PipelineSchedules from '~/ci/pipeline_schedules/components/pipeline_schedules.vue'; @@ -354,5 +355,19 @@ describe('Pipeline schedules app', () => { expect(findLink().exists()).toBe(true); expect(findLink().text()).toContain('scheduled pipelines documentation.'); }); + + describe('inactive tab', () => { + beforeEach(() => { + setWindowLocation('https://gitlab.com/flightjs/Flight/-/pipeline_schedules?scope=INACTIVE'); + }); + + it('should not show empty state', async () => { + createComponent([[getPipelineSchedulesQuery, successEmptyHandler]]); + + await waitForPromises(); + + expect(findEmptyState().exists()).toBe(false); + }); + }); }); }); diff --git a/spec/frontend/ci/pipeline_schedules/components/table/pipeline_schedules_table_spec.js b/spec/frontend/ci/pipeline_schedules/components/table/pipeline_schedules_table_spec.js index e488a36f3dc..8f0e9fca379 100644 --- a/spec/frontend/ci/pipeline_schedules/components/table/pipeline_schedules_table_spec.js +++ b/spec/frontend/ci/pipeline_schedules/components/table/pipeline_schedules_table_spec.js @@ -1,4 +1,4 @@ -import { GlTableLite } from '@gitlab/ui'; +import { GlTable } from '@gitlab/ui'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import PipelineSchedulesTable from '~/ci/pipeline_schedules/components/table/pipeline_schedules_table.vue'; import { mockPipelineScheduleNodes, mockPipelineScheduleCurrentUser } from '../../mock_data'; @@ -19,7 +19,7 @@ describe('Pipeline schedules table', () => { }); }; - const findTable = () => wrapper.findComponent(GlTableLite); + const findTable = () => wrapper.findComponent(GlTable); const findScheduleDescription = () => wrapper.findByTestId('pipeline-schedule-description'); beforeEach(() => { diff --git a/spec/frontend/ci/pipeline_schedules/mock_data.js b/spec/frontend/ci/pipeline_schedules/mock_data.js index 0a4f233f199..8d4e0f1bea6 100644 --- a/spec/frontend/ci/pipeline_schedules/mock_data.js +++ b/spec/frontend/ci/pipeline_schedules/mock_data.js @@ -35,6 +35,19 @@ export const mockPipelineScheduleAsGuestNodes = guestNodes; export const mockTakeOwnershipNodes = takeOwnershipNodes; export const mockSinglePipelineScheduleNode = mockGetSinglePipelineScheduleGraphQLResponse; +export const mockSinglePipelineScheduleNodeNoVars = { + data: { + currentUser: mockGetPipelineSchedulesGraphQLResponse.data.currentUser, + project: { + id: mockGetPipelineSchedulesGraphQLResponse.data.project.id, + pipelineSchedules: { + count: 1, + nodes: [mockGetPipelineSchedulesGraphQLResponse.data.project.pipelineSchedules.nodes[1]], + }, + }, + }, +}; + export const emptyPipelineSchedulesResponse = { data: { currentUser: { diff --git a/spec/frontend/ci/runner/admin_runners/provide_spec.js b/spec/frontend/ci/runner/admin_runners/provide_spec.js new file mode 100644 index 00000000000..b24ddabbb66 --- /dev/null +++ b/spec/frontend/ci/runner/admin_runners/provide_spec.js @@ -0,0 +1,34 @@ +import { provide } from '~/ci/runner/admin_runners/provide'; + +import { + onlineContactTimeoutSecs, + staleTimeoutSecs, + runnerInstallHelpPage, +} from 'jest/ci/runner/mock_data'; + +const mockDataset = { + runnerInstallHelpPage, + onlineContactTimeoutSecs, + staleTimeoutSecs, +}; + +describe('admin runners provide', () => { + it('returns provide values', () => { + expect(provide(mockDataset)).toMatchObject({ + runnerInstallHelpPage, + onlineContactTimeoutSecs, + staleTimeoutSecs, + }); + }); + + it('returns only provide values', () => { + const dataset = { + ...mockDataset, + extraEntry: 'ANOTHER_ENTRY', + }; + + expect(provide(dataset)).not.toMatchObject({ + extraEntry: 'ANOTHER_ENTRY', + }); + }); +}); 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 e4373d1c198..3fb845b186a 100644 --- a/spec/frontend/ci/runner/components/registration/registration_dropdown_spec.js +++ b/spec/frontend/ci/runner/components/registration/registration_dropdown_spec.js @@ -168,9 +168,8 @@ describe('RegistrationDropdown', () => { expect(findTokenDropdownItem().exists()).toBe(true); }); - it('Displays masked value by default', () => { + it('Displays masked value as password input by default', () => { const mockToken = '0123456789'; - const maskToken = '**********'; createComponent( { @@ -179,7 +178,7 @@ describe('RegistrationDropdown', () => { mountExtended, ); - expect(findRegistrationTokenInput().element.value).toBe(maskToken); + expect(findRegistrationTokenInput().element.type).toBe('password'); }); }); diff --git a/spec/frontend/ci/runner/components/registration/registration_token_spec.js b/spec/frontend/ci/runner/components/registration/registration_token_spec.js index fd3896d5500..eccfe43b47f 100644 --- a/spec/frontend/ci/runner/components/registration/registration_token_spec.js +++ b/spec/frontend/ci/runner/components/registration/registration_token_spec.js @@ -38,10 +38,15 @@ describe('RegistrationToken', () => { ); }); + it('Renders readonly input', () => { + createComponent(); + + expect(findInputCopyToggleVisibility().props('readonly')).toBe(true); + }); + // Component integration test to ensure secure masking - it('Displays masked value by default', () => { + it('Displays masked value as password input by default', () => { const mockToken = '0123456789'; - const maskToken = '**********'; createComponent({ props: { @@ -50,7 +55,7 @@ describe('RegistrationToken', () => { mountFn: mountExtended, }); - expect(wrapper.find('input').element.value).toBe(maskToken); + expect(wrapper.find('input').element.type).toBe('password'); }); describe('When the copy to clipboard button is clicked', () => { diff --git a/spec/frontend/ci/runner/components/search_tokens/tag_token_spec.js b/spec/frontend/ci/runner/components/search_tokens/tag_token_spec.js index e9f2e888b9a..91d2a20ec8a 100644 --- a/spec/frontend/ci/runner/components/search_tokens/tag_token_spec.js +++ b/spec/frontend/ci/runner/components/search_tokens/tag_token_spec.js @@ -6,7 +6,7 @@ import waitForPromises from 'helpers/wait_for_promises'; import { createAlert } from '~/alert'; import axios from '~/lib/utils/axios_utils'; import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status'; -import TagToken, { TAG_SUGGESTIONS_PATH } from '~/ci/runner/components/search_tokens/tag_token.vue'; +import TagToken from '~/ci/runner/components/search_tokens/tag_token.vue'; import { OPERATORS_IS } from '~/vue_shared/components/filtered_search_bar/constants'; import { getRecentlyUsedSuggestions } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils'; @@ -45,6 +45,8 @@ const mockTagTokenConfig = { operators: OPERATORS_IS, }; +const mockTagSuggestionsPath = '/path/runners/tag_list'; + describe('TagToken', () => { let mock; let wrapper; @@ -59,7 +61,8 @@ describe('TagToken', () => { }, provide: { portalName: 'fake target', - alignSuggestions: function fakeAlignSuggestions() {}, + tagSuggestionsPath: mockTagSuggestionsPath, + alignSuggestions: function fakeAligxnSuggestions() {}, filteredSearchSuggestionListInstance: { register: jest.fn(), unregister: jest.fn(), @@ -80,9 +83,9 @@ describe('TagToken', () => { beforeEach(() => { mock = new MockAdapter(axios); - mock.onGet(TAG_SUGGESTIONS_PATH, { params: { search: '' } }).reply(HTTP_STATUS_OK, mockTags); + mock.onGet(mockTagSuggestionsPath, { params: { search: '' } }).reply(HTTP_STATUS_OK, mockTags); mock - .onGet(TAG_SUGGESTIONS_PATH, { params: { search: mockSearchTerm } }) + .onGet(mockTagSuggestionsPath, { params: { search: mockSearchTerm } }) .reply(HTTP_STATUS_OK, mockTagsFiltered); getRecentlyUsedSuggestions.mockReturnValue([]); @@ -163,7 +166,7 @@ describe('TagToken', () => { describe('when suggestions cannot be loaded', () => { beforeEach(async () => { mock - .onGet(TAG_SUGGESTIONS_PATH, { params: { search: '' } }) + .onGet(mockTagSuggestionsPath, { params: { search: '' } }) .reply(HTTP_STATUS_INTERNAL_SERVER_ERROR); createComponent(); diff --git a/spec/frontend/ci/runner/mock_data.js b/spec/frontend/ci/runner/mock_data.js index d72f93ad574..b8eb9f0ba1b 100644 --- a/spec/frontend/ci/runner/mock_data.js +++ b/spec/frontend/ci/runner/mock_data.js @@ -104,7 +104,7 @@ export const mockSearchExamples = [ }, }, { - name: 'a two terms text search', + name: 'a two words text search', urlQuery: '?search=something+else', search: { runnerType: null, @@ -112,11 +112,7 @@ export const mockSearchExamples = [ filters: [ { type: FILTERED_SEARCH_TERM, - value: { data: 'something' }, - }, - { - type: FILTERED_SEARCH_TERM, - value: { data: 'else' }, + value: { data: 'something else' }, }, ], pagination: {}, @@ -323,6 +319,7 @@ export const mockRegistrationToken = 'MOCK_REGISTRATION_TOKEN'; export const mockAuthenticationToken = 'MOCK_AUTHENTICATION_TOKEN'; export const newRunnerPath = '/runners/new'; +export const runnerInstallHelpPage = 'https://docs.example.com/runner/install/'; export { allRunnersData, diff --git a/spec/frontend/ci/runner/runner_search_utils_spec.js b/spec/frontend/ci/runner/runner_search_utils_spec.js index 9a4a6139198..0623d2a3348 100644 --- a/spec/frontend/ci/runner/runner_search_utils_spec.js +++ b/spec/frontend/ci/runner/runner_search_utils_spec.js @@ -50,8 +50,7 @@ describe('search_params.js', () => { it('When search params appear as array, they are concatenated', () => { expect(fromUrlQueryToSearch('?search[]=my&search[]=text').filters).toEqual([ - { type: FILTERED_SEARCH_TERM, value: { data: 'my' } }, - { type: FILTERED_SEARCH_TERM, value: { data: 'text' } }, + { type: FILTERED_SEARCH_TERM, value: { data: 'my text' } }, ]); }); }); |