diff options
Diffstat (limited to 'spec/frontend/access_tokens/components')
4 files changed, 15 insertions, 408 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) }, - ]); - }); - }); -}); |