diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-08-18 08:17:02 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-08-18 08:17:02 +0000 |
commit | b39512ed755239198a9c294b6a45e65c05900235 (patch) | |
tree | d234a3efade1de67c46b9e5a38ce813627726aa7 /spec/frontend/access_tokens | |
parent | d31474cf3b17ece37939d20082b07f6657cc79a9 (diff) | |
download | gitlab-ce-b39512ed755239198a9c294b6a45e65c05900235.tar.gz |
Add latest changes from gitlab-org/gitlab@15-3-stable-eev15.3.0-rc42
Diffstat (limited to 'spec/frontend/access_tokens')
5 files changed, 26 insertions, 442 deletions
diff --git a/spec/frontend/access_tokens/components/__snapshots__/expires_at_field_spec.js.snap b/spec/frontend/access_tokens/components/__snapshots__/expires_at_field_spec.js.snap index 36003154b58..2bd2b17a12d 100644 --- a/spec/frontend/access_tokens/components/__snapshots__/expires_at_field_spec.js.snap +++ b/spec/frontend/access_tokens/components/__snapshots__/expires_at_field_spec.js.snap @@ -11,22 +11,17 @@ exports[`~/access_tokens/components/expires_at_field should render datepicker wi arialabel="" autocomplete="" container="" + data-qa-selector="expiry_date_field" + defaultdate="Wed Aug 05 2020 00:00:00 GMT+0000 (Greenwich Mean Time)" displayfield="true" firstday="0" + inputid="personal_access_token_expires_at" inputlabel="Enter date" + inputname="personal_access_token[expires_at]" mindate="Mon Jul 06 2020 00:00:00 GMT+0000 (Greenwich Mean Time)" placeholder="YYYY-MM-DD" + showclearbutton="true" theme="" - > - <gl-form-input-stub - autocomplete="off" - class="datepicker gl-datepicker-input" - data-qa-selector="expiry_date_field" - id="personal_access_token_expires_at" - inputmode="none" - name="personal_access_token[expires_at]" - placeholder="YYYY-MM-DD" - /> - </gl-datepicker-stub> + /> </gl-form-group-stub> `; diff --git a/spec/frontend/access_tokens/components/expires_at_field_spec.js b/spec/frontend/access_tokens/components/expires_at_field_spec.js index cb899d10ba7..646dc0d703f 100644 --- a/spec/frontend/access_tokens/components/expires_at_field_spec.js +++ b/spec/frontend/access_tokens/components/expires_at_field_spec.js @@ -1,6 +1,7 @@ import { shallowMount } from '@vue/test-utils'; import { GlDatepicker } from '@gitlab/ui'; import ExpiresAtField from '~/access_tokens/components/expires_at_field.vue'; +import { getDateInFuture } from '~/lib/utils/datetime_utility'; describe('~/access_tokens/components/expires_at_field', () => { let wrapper; @@ -49,4 +50,12 @@ describe('~/access_tokens/components/expires_at_field', () => { expect(findDatepicker().props('maxDate')).toStrictEqual(maxDate); }); + + it('should set the default expiration date to be 30 days', () => { + const today = new Date(); + const future = getDateInFuture(today, 30); + createComponent(); + + expect(findDatepicker().props('defaultDate')).toStrictEqual(future); + }); }); diff --git a/spec/frontend/access_tokens/components/projects_field_spec.js b/spec/frontend/access_tokens/components/projects_field_spec.js deleted file mode 100644 index 1c4fe7bb168..00000000000 --- a/spec/frontend/access_tokens/components/projects_field_spec.js +++ /dev/null @@ -1,131 +0,0 @@ -import { nextTick } from 'vue'; -import { within, fireEvent } from '@testing-library/dom'; -import { mount } from '@vue/test-utils'; -import ProjectsField from '~/access_tokens/components/projects_field.vue'; -import ProjectsTokenSelector from '~/access_tokens/components/projects_token_selector.vue'; - -describe('ProjectsField', () => { - let wrapper; - - const createComponent = ({ inputAttrsValue = '' } = {}) => { - wrapper = mount(ProjectsField, { - propsData: { - inputAttrs: { - id: 'projects', - name: 'projects', - value: inputAttrsValue, - }, - }, - }); - }; - - const queryByLabelText = (text) => within(wrapper.element).queryByLabelText(text); - const queryByText = (text) => within(wrapper.element).queryByText(text); - const findAllProjectsRadio = () => queryByLabelText('All projects'); - const findSelectedProjectsRadio = () => queryByLabelText('Selected projects'); - const findProjectsTokenSelector = () => wrapper.findComponent(ProjectsTokenSelector); - const findHiddenInput = () => wrapper.find('input[type="hidden"]'); - - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); - - it('renders label and sub-label', () => { - createComponent(); - - expect(queryByText('Projects')).not.toBe(null); - expect(queryByText('Set access permissions for this token.')).not.toBe(null); - }); - - describe('when `inputAttrs.value` is empty', () => { - beforeEach(() => { - createComponent(); - }); - - it('renders "All projects" radio as checked', () => { - expect(findAllProjectsRadio().checked).toBe(true); - }); - - it('renders "Selected projects" radio as unchecked', () => { - expect(findSelectedProjectsRadio().checked).toBe(false); - }); - - it('sets `projects-token-selector` `initialProjectIds` prop to an empty array', () => { - expect(findProjectsTokenSelector().props('initialProjectIds')).toEqual([]); - }); - }); - - describe('when `inputAttrs.value` is a comma separated list of project IDs', () => { - beforeEach(() => { - createComponent({ inputAttrsValue: '1,2' }); - }); - - it('renders "All projects" radio as unchecked', () => { - expect(findAllProjectsRadio().checked).toBe(false); - }); - - it('renders "Selected projects" radio as checked', () => { - expect(findSelectedProjectsRadio().checked).toBe(true); - }); - - it('sets `projects-token-selector` `initialProjectIds` prop to an array of project IDs', () => { - expect(findProjectsTokenSelector().props('initialProjectIds')).toEqual(['1', '2']); - }); - }); - - it('renders `projects-token-selector` component', () => { - createComponent(); - - expect(findProjectsTokenSelector().exists()).toBe(true); - }); - - it('renders hidden input with correct `name` and `id` attributes', () => { - createComponent(); - - expect(findHiddenInput().attributes()).toEqual( - expect.objectContaining({ - id: 'projects', - name: 'projects', - }), - ); - }); - - describe('when `projects-token-selector` is focused', () => { - beforeEach(() => { - createComponent(); - - findProjectsTokenSelector().vm.$emit('focus'); - }); - - it('auto selects the "Selected projects" radio', () => { - expect(findSelectedProjectsRadio().checked).toBe(true); - }); - - describe('when `projects-token-selector` is changed', () => { - beforeEach(() => { - findProjectsTokenSelector().vm.$emit('input', [ - { - id: 1, - }, - { - id: 2, - }, - ]); - }); - - it('updates the hidden input value to a comma separated list of project IDs', () => { - expect(findHiddenInput().attributes('value')).toBe('1,2'); - }); - - describe('when radio is changed back to "All projects"', () => { - it('removes the hidden input value', async () => { - fireEvent.change(findAllProjectsRadio()); - await nextTick(); - - expect(findHiddenInput().attributes('value')).toBe(''); - }); - }); - }); - }); -}); diff --git a/spec/frontend/access_tokens/components/projects_token_selector_spec.js b/spec/frontend/access_tokens/components/projects_token_selector_spec.js deleted file mode 100644 index 40aaf16d41f..00000000000 --- a/spec/frontend/access_tokens/components/projects_token_selector_spec.js +++ /dev/null @@ -1,266 +0,0 @@ -import { - GlAvatar, - GlAvatarLabeled, - GlIntersectionObserver, - GlToken, - GlTokenSelector, - GlLoadingIcon, -} from '@gitlab/ui'; -import { mount } from '@vue/test-utils'; -import produce from 'immer'; -import Vue from 'vue'; -import VueApollo from 'vue-apollo'; - -import getProjectsQueryResponse from 'test_fixtures/graphql/projects/access_tokens/get_projects.query.graphql.json'; -import createMockApollo from 'helpers/mock_apollo_helper'; -import { extendedWrapper } from 'helpers/vue_test_utils_helper'; -import waitForPromises from 'helpers/wait_for_promises'; -import ProjectsTokenSelector from '~/access_tokens/components/projects_token_selector.vue'; -import getProjectsQuery from '~/access_tokens/graphql/queries/get_projects.query.graphql'; -import { getIdFromGraphQLId } from '~/graphql_shared/utils'; - -describe('ProjectsTokenSelector', () => { - const getProjectsQueryResponsePage2 = produce( - getProjectsQueryResponse, - (getProjectsQueryResponseDraft) => { - /* eslint-disable no-param-reassign */ - getProjectsQueryResponseDraft.data.projects.pageInfo.hasNextPage = false; - getProjectsQueryResponseDraft.data.projects.pageInfo.endCursor = null; - getProjectsQueryResponseDraft.data.projects.nodes.splice(1, 1); - getProjectsQueryResponseDraft.data.projects.nodes[0].id = 'gid://gitlab/Project/100'; - /* eslint-enable no-param-reassign */ - }, - ); - - const runDebounce = () => jest.runAllTimers(); - - const { pageInfo, nodes: projects } = getProjectsQueryResponse.data.projects; - const project1 = projects[0]; - const project2 = projects[1]; - - let wrapper; - - let resolveGetProjectsQuery; - let resolveGetInitialProjectsQuery; - const getProjectsQueryRequestHandler = jest.fn( - ({ ids }) => - new Promise((resolve) => { - if (ids) { - resolveGetInitialProjectsQuery = resolve; - } else { - resolveGetProjectsQuery = resolve; - } - }), - ); - - const createComponent = ({ - propsData = {}, - apolloProvider = createMockApollo([[getProjectsQuery, getProjectsQueryRequestHandler]]), - resolveQueries = true, - } = {}) => { - Vue.use(VueApollo); - - wrapper = extendedWrapper( - mount(ProjectsTokenSelector, { - apolloProvider, - propsData: { - selectedProjects: [], - initialProjectIds: [], - ...propsData, - }, - stubs: ['gl-intersection-observer'], - }), - ); - - runDebounce(); - - if (resolveQueries) { - resolveGetProjectsQuery(getProjectsQueryResponse); - - return waitForPromises(); - } - - return Promise.resolve(); - }; - - const findTokenSelector = () => wrapper.findComponent(GlTokenSelector); - const findTokenSelectorInput = () => findTokenSelector().find('input[type="text"]'); - const findIntersectionObserver = () => wrapper.findComponent(GlIntersectionObserver); - - it('renders dropdown items with project avatars', async () => { - await createComponent(); - - wrapper.findAllComponents(GlAvatarLabeled).wrappers.forEach((avatarLabeledWrapper, index) => { - const project = projects[index]; - - expect(avatarLabeledWrapper.attributes()).toEqual( - expect.objectContaining({ - 'entity-id': `${getIdFromGraphQLId(project.id)}`, - 'entity-name': project.name, - ...(project.avatarUrl && { src: project.avatarUrl }), - }), - ); - - expect(avatarLabeledWrapper.props()).toEqual( - expect.objectContaining({ - label: project.name, - subLabel: project.nameWithNamespace, - }), - ); - }); - }); - - it('renders tokens with project avatars', () => { - createComponent({ - propsData: { - selectedProjects: [{ ...project2, id: getIdFromGraphQLId(project2.id) }], - }, - }); - - const token = wrapper.findComponent(GlToken); - const avatar = token.findComponent(GlAvatar); - - expect(token.text()).toContain(project2.nameWithNamespace); - expect(avatar.attributes('src')).toBe(project2.avatarUrl); - expect(avatar.props()).toEqual( - expect.objectContaining({ - entityId: getIdFromGraphQLId(project2.id), - entityName: project2.name, - }), - ); - }); - - describe('when `enter` key is pressed', () => { - it('calls `preventDefault` so form is not submitted when user selects a project from the dropdown', () => { - createComponent(); - - const event = { - preventDefault: jest.fn(), - }; - - findTokenSelectorInput().trigger('keydown.enter', event); - - expect(event.preventDefault).toHaveBeenCalled(); - }); - }); - - describe('when text input is typed in', () => { - const searchTerm = 'foo bar'; - - beforeEach(async () => { - await createComponent(); - - await findTokenSelectorInput().setValue(searchTerm); - runDebounce(); - }); - - it('makes GraphQL request with `search` variable set', async () => { - expect(getProjectsQueryRequestHandler).toHaveBeenLastCalledWith({ - search: searchTerm, - after: null, - first: 20, - ids: null, - }); - }); - - it('sets loading state while waiting for GraphQL request to resolve', async () => { - expect(findTokenSelector().props('loading')).toBe(true); - - resolveGetProjectsQuery(getProjectsQueryResponse); - await waitForPromises(); - - expect(findTokenSelector().props('loading')).toBe(false); - }); - }); - - describe('when there is a next page of projects and user scrolls to the bottom of the dropdown', () => { - beforeEach(async () => { - await createComponent(); - - findIntersectionObserver().vm.$emit('appear'); - }); - - it('makes GraphQL request with `after` variable set', async () => { - expect(getProjectsQueryRequestHandler).toHaveBeenLastCalledWith({ - after: pageInfo.endCursor, - first: 20, - search: '', - ids: null, - }); - }); - - it('displays loading icon while waiting for GraphQL request to resolve', async () => { - expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true); - - resolveGetProjectsQuery(getProjectsQueryResponsePage2); - await waitForPromises(); - - expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false); - }); - }); - - describe('when there is not a next page of projects', () => { - it('does not render `GlIntersectionObserver`', async () => { - createComponent({ resolveQueries: false }); - - resolveGetProjectsQuery(getProjectsQueryResponsePage2); - await waitForPromises(); - - expect(findIntersectionObserver().exists()).toBe(false); - }); - }); - - describe('when `GlTokenSelector` emits `input` event', () => { - it('emits `input` event used by `v-model`', () => { - findTokenSelector().vm.$emit('input', project1); - - expect(wrapper.emitted('input')[0]).toEqual([project1]); - }); - }); - - describe('when `GlTokenSelector` emits `focus` event', () => { - it('emits `focus` event', () => { - const event = { fakeEvent: 'foo' }; - findTokenSelector().vm.$emit('focus', event); - - expect(wrapper.emitted('focus')[0]).toEqual([event]); - }); - }); - - describe('when `initialProjectIds` is an empty array', () => { - it('does not request initial projects', async () => { - await createComponent(); - - expect(getProjectsQueryRequestHandler).toHaveBeenCalledTimes(1); - expect(getProjectsQueryRequestHandler).toHaveBeenCalledWith( - expect.objectContaining({ - ids: null, - }), - ); - }); - }); - - describe('when `initialProjectIds` is an array of project IDs', () => { - it('requests those projects and emits `input` event with result', async () => { - await createComponent({ - propsData: { - initialProjectIds: [getIdFromGraphQLId(project1.id), getIdFromGraphQLId(project2.id)], - }, - }); - - resolveGetInitialProjectsQuery(getProjectsQueryResponse); - await waitForPromises(); - - expect(getProjectsQueryRequestHandler).toHaveBeenCalledWith({ - after: '', - first: null, - search: '', - ids: [project1.id, project2.id], - }); - expect(wrapper.emitted('input')[0][0]).toEqual([ - { ...project1, id: getIdFromGraphQLId(project1.id) }, - { ...project2, id: getIdFromGraphQLId(project2.id) }, - ]); - }); - }); -}); diff --git a/spec/frontend/access_tokens/index_spec.js b/spec/frontend/access_tokens/index_spec.js index b6119f1d167..0c611a4a512 100644 --- a/spec/frontend/access_tokens/index_spec.js +++ b/spec/frontend/access_tokens/index_spec.js @@ -8,13 +8,11 @@ import { initAccessTokenTableApp, initExpiresAtField, initNewAccessTokenApp, - initProjectsField, initTokensApp, } from '~/access_tokens'; import * as AccessTokenTableApp from '~/access_tokens/components/access_token_table_app.vue'; -import * as ExpiresAtField from '~/access_tokens/components/expires_at_field.vue'; +import ExpiresAtField from '~/access_tokens/components/expires_at_field.vue'; import * as NewAccessTokenApp from '~/access_tokens/components/new_access_token_app.vue'; -import * as ProjectsField from '~/access_tokens/components/projects_field.vue'; import * as TokensApp from '~/access_tokens/components/tokens_app.vue'; import { FEED_TOKEN, INCOMING_EMAIL_TOKEN, STATIC_OBJECT_TOKEN } from '~/access_tokens/constants'; import { __, sprintf } from '~/locale'; @@ -115,49 +113,28 @@ describe('access tokens', () => { }); }); - describe.each` - initFunction | mountSelector | fieldName | expectedComponent - ${initExpiresAtField} | ${'js-access-tokens-expires-at'} | ${'expiresAt'} | ${ExpiresAtField} - ${initProjectsField} | ${'js-access-tokens-projects'} | ${'projects'} | ${ProjectsField} - `('$initFunction', ({ initFunction, mountSelector, fieldName, expectedComponent }) => { + describe('initExpiresAtField', () => { describe('when mount element exists', () => { - const FakeComponent = Vue.component('FakeComponent', { - props: ['inputAttrs'], - render: () => null, - }); - - const nameAttribute = `access_tokens[${fieldName}]`; - const idAttribute = `access_tokens_${fieldName}`; + const nameAttribute = 'access_tokens[expires_at]'; + const idAttribute = 'access_tokens_expires_at'; beforeEach(() => { - window.gon = { features: { personalAccessTokensScopedToProjects: true } }; - setHTMLFixture( - `<div class="${mountSelector}"> + `<div class="js-access-tokens-expires-at"> <input - name="${nameAttribute}" - data-js-name="${fieldName}" - id="${idAttribute}" + name="access_tokens[expires_at]" + data-js-name="expiresAt" + id="access_tokens_expires_at" placeholder="Foo bar" value="1,2" /> </div>`, ); - - // Mock component so we don't have to deal with mocking Apollo - // eslint-disable-next-line no-param-reassign - expectedComponent.default = FakeComponent; - }); - - afterEach(() => { - delete window.gon; }); it('mounts component and sets `inputAttrs` prop', async () => { - const vueInstance = await initFunction(); - - wrapper = createWrapper(vueInstance); - const component = wrapper.findComponent(FakeComponent); + wrapper = createWrapper(initExpiresAtField()); + const component = wrapper.findComponent(ExpiresAtField); expect(component.exists()).toBe(true); expect(component.props('inputAttrs')).toEqual({ @@ -171,7 +148,7 @@ describe('access tokens', () => { describe('when mount element does not exist', () => { it('returns `null`', () => { - expect(initFunction()).toBe(null); + expect(initExpiresAtField()).toBe(null); }); }); }); |