diff options
Diffstat (limited to 'spec/frontend/issuable')
3 files changed, 364 insertions, 0 deletions
diff --git a/spec/frontend/issuable/components/csv_export_modal_spec.js b/spec/frontend/issuable/components/csv_export_modal_spec.js new file mode 100644 index 00000000000..f46b6f72f05 --- /dev/null +++ b/spec/frontend/issuable/components/csv_export_modal_spec.js @@ -0,0 +1,91 @@ +import { GlModal, GlIcon, GlButton } from '@gitlab/ui'; +import { mount } from '@vue/test-utils'; +import { stubComponent } from 'helpers/stub_component'; +import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import CsvExportModal from '~/issuable/components/csv_export_modal.vue'; + +describe('CsvExportModal', () => { + let wrapper; + + function createComponent(options = {}) { + const { injectedProperties = {}, props = {} } = options; + return extendedWrapper( + mount(CsvExportModal, { + propsData: { + modalId: 'csv-export-modal', + ...props, + }, + provide: { + issuableType: 'issues', + ...injectedProperties, + }, + stubs: { + GlModal: stubComponent(GlModal, { + template: + '<div><slot name="modal-title"></slot><slot></slot><slot name="modal-footer"></slot></div>', + }), + }, + }), + ); + } + + afterEach(() => { + wrapper.destroy(); + }); + + const findModal = () => wrapper.findComponent(GlModal); + const findIcon = () => wrapper.findComponent(GlIcon); + const findButton = () => wrapper.findComponent(GlButton); + + describe('template', () => { + describe.each` + issuableType | modalTitle + ${'issues'} | ${'Export issues'} + ${'merge-requests'} | ${'Export merge requests'} + `('with the issuableType "$issuableType"', ({ issuableType, modalTitle }) => { + beforeEach(() => { + wrapper = createComponent({ injectedProperties: { issuableType } }); + }); + + it('displays the modal title "$modalTitle"', () => { + expect(findModal().text()).toContain(modalTitle); + }); + + it('displays the button with title "$modalTitle"', () => { + expect(findButton().text()).toBe(modalTitle); + }); + }); + + describe('issuable count info text', () => { + it('displays the info text when issuableCount is > -1', () => { + wrapper = createComponent({ injectedProperties: { issuableCount: 10 } }); + expect(wrapper.findByTestId('issuable-count-note').exists()).toBe(true); + expect(wrapper.findByTestId('issuable-count-note').text()).toContain('10 issues selected'); + expect(findIcon().exists()).toBe(true); + }); + + it("doesn't display the info text when issuableCount is -1", () => { + wrapper = createComponent({ injectedProperties: { issuableCount: -1 } }); + expect(wrapper.findByTestId('issuable-count-note').exists()).toBe(false); + }); + }); + + describe('email info text', () => { + it('displays the proper email', () => { + const email = 'admin@example.com'; + wrapper = createComponent({ injectedProperties: { email } }); + expect(findModal().text()).toContain( + `The CSV export will be created in the background. Once finished, it will be sent to ${email} in an attachment.`, + ); + }); + }); + + describe('primary button', () => { + it('passes the exportCsvPath to the button', () => { + const exportCsvPath = '/gitlab-org/gitlab-test/-/issues/export_csv'; + wrapper = createComponent({ injectedProperties: { exportCsvPath } }); + expect(findButton().attributes('href')).toBe(exportCsvPath); + }); + }); + }); +}); diff --git a/spec/frontend/issuable/components/csv_import_export_buttons_spec.js b/spec/frontend/issuable/components/csv_import_export_buttons_spec.js new file mode 100644 index 00000000000..e32bf35b13a --- /dev/null +++ b/spec/frontend/issuable/components/csv_import_export_buttons_spec.js @@ -0,0 +1,187 @@ +import { shallowMount } from '@vue/test-utils'; +import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; +import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import CsvExportModal from '~/issuable/components/csv_export_modal.vue'; +import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue'; +import CsvImportModal from '~/issuable/components/csv_import_modal.vue'; + +describe('CsvImportExportButtons', () => { + let wrapper; + let glModalDirective; + + function createComponent(injectedProperties = {}) { + glModalDirective = jest.fn(); + return extendedWrapper( + shallowMount(CsvImportExportButtons, { + directives: { + GlTooltip: createMockDirective(), + glModal: { + bind(_, { value }) { + glModalDirective(value); + }, + }, + }, + provide: { + ...injectedProperties, + }, + }), + ); + } + + afterEach(() => { + wrapper.destroy(); + }); + + const findExportCsvButton = () => wrapper.findByTestId('export-csv-button'); + const findImportDropdown = () => wrapper.findByTestId('import-csv-dropdown'); + const findImportCsvButton = () => wrapper.findByTestId('import-csv-dropdown'); + const findImportFromJiraLink = () => wrapper.findByTestId('import-from-jira-link'); + const findExportCsvModal = () => wrapper.findComponent(CsvExportModal); + const findImportCsvModal = () => wrapper.findComponent(CsvImportModal); + + describe('template', () => { + describe('when the showExportButton=true', () => { + beforeEach(() => { + wrapper = createComponent({ showExportButton: true }); + }); + + it('displays the export button', () => { + expect(findExportCsvButton().exists()).toBe(true); + }); + + it('export button has a tooltip', () => { + const tooltip = getBinding(findExportCsvButton().element, 'gl-tooltip'); + + expect(tooltip).toBeDefined(); + expect(tooltip.value).toBe('Export as CSV'); + }); + + it('renders the export modal', () => { + expect(findExportCsvModal().exists()).toBe(true); + }); + + it('opens the export modal', () => { + findExportCsvButton().trigger('click'); + + expect(glModalDirective).toHaveBeenCalledWith(wrapper.vm.exportModalId); + }); + }); + + describe('when the showExportButton=false', () => { + beforeEach(() => { + wrapper = createComponent({ showExportButton: false }); + }); + + it('does not display the export button', () => { + expect(findExportCsvButton().exists()).toBe(false); + }); + + it('does not render the export modal', () => { + expect(findExportCsvModal().exists()).toBe(false); + }); + }); + + describe('when the showImportButton=true', () => { + beforeEach(() => { + wrapper = createComponent({ showImportButton: true }); + }); + + it('displays the import dropdown', () => { + expect(findImportDropdown().exists()).toBe(true); + }); + + it('renders the import button', () => { + expect(findImportCsvButton().exists()).toBe(true); + }); + + describe('when showLabel=false', () => { + beforeEach(() => { + wrapper = createComponent({ showImportButton: true, showLabel: false }); + }); + + it('does not have a button text', () => { + expect(findImportCsvButton().props('text')).toBe(null); + }); + + it('import button has a tooltip', () => { + const tooltip = getBinding(findImportDropdown().element, 'gl-tooltip'); + + expect(tooltip).toBeDefined(); + expect(tooltip.value).toBe('Import issues'); + }); + }); + + describe('when showLabel=true', () => { + beforeEach(() => { + wrapper = createComponent({ showImportButton: true, showLabel: true }); + }); + + it('displays a button text', () => { + expect(findImportCsvButton().props('text')).toBe('Import issues'); + }); + + it('import button has no tooltip', () => { + const tooltip = getBinding(findImportDropdown().element, 'gl-tooltip'); + + expect(tooltip.value).toBe(null); + }); + }); + + it('renders the import modal', () => { + expect(findImportCsvModal().exists()).toBe(true); + }); + + it('opens the import modal', () => { + findImportCsvButton().trigger('click'); + + expect(glModalDirective).toHaveBeenCalledWith(wrapper.vm.importModalId); + }); + + describe('import from jira link', () => { + const projectImportJiraPath = 'gitlab-org/gitlab-test/-/import/jira'; + + beforeEach(() => { + wrapper = createComponent({ + showImportButton: true, + canEdit: true, + projectImportJiraPath, + }); + }); + + describe('when canEdit=true', () => { + it('renders the import dropdown item', () => { + expect(findImportFromJiraLink().exists()).toBe(true); + }); + + it('passes the proper path to the link', () => { + expect(findImportFromJiraLink().attributes('href')).toBe(projectImportJiraPath); + }); + }); + + describe('when canEdit=false', () => { + beforeEach(() => { + wrapper = createComponent({ showImportButton: true, canEdit: false }); + }); + + it('does not render the import dropdown item', () => { + expect(findImportFromJiraLink().exists()).toBe(false); + }); + }); + }); + }); + + describe('when the showImportButton=false', () => { + beforeEach(() => { + wrapper = createComponent({ showImportButton: false }); + }); + + it('does not display the import dropdown', () => { + expect(findImportDropdown().exists()).toBe(false); + }); + + it('does not render the import modal', () => { + expect(findImportCsvModal().exists()).toBe(false); + }); + }); + }); +}); diff --git a/spec/frontend/issuable/components/csv_import_modal_spec.js b/spec/frontend/issuable/components/csv_import_modal_spec.js new file mode 100644 index 00000000000..ce9d738f77b --- /dev/null +++ b/spec/frontend/issuable/components/csv_import_modal_spec.js @@ -0,0 +1,86 @@ +import { GlModal } from '@gitlab/ui'; +import { getByRole, getByLabelText } from '@testing-library/dom'; +import { mount } from '@vue/test-utils'; +import { stubComponent } from 'helpers/stub_component'; +import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import CsvImportModal from '~/issuable/components/csv_import_modal.vue'; + +jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' })); + +describe('CsvImportModal', () => { + let wrapper; + let formSubmitSpy; + + function createComponent(options = {}) { + const { injectedProperties = {}, props = {} } = options; + return extendedWrapper( + mount(CsvImportModal, { + propsData: { + modalId: 'csv-import-modal', + ...props, + }, + provide: { + issuableType: 'issues', + ...injectedProperties, + }, + stubs: { + GlModal: stubComponent(GlModal, { + template: '<div><slot></slot><slot name="modal-footer"></slot></div>', + }), + }, + }), + ); + } + + beforeEach(() => { + formSubmitSpy = jest.spyOn(HTMLFormElement.prototype, 'submit').mockImplementation(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + const findModal = () => wrapper.findComponent(GlModal); + const findPrimaryButton = () => getByRole(wrapper.element, 'button', { name: 'Import issues' }); + const findForm = () => wrapper.findByTestId('import-csv-form'); + const findFileInput = () => getByLabelText(wrapper.element, 'Upload CSV file'); + const findAuthenticityToken = () => new FormData(findForm().element).get('authenticity_token'); + + describe('template', () => { + it('displays modal title', () => { + wrapper = createComponent(); + expect(findModal().text()).toContain('Import issues'); + }); + + it('displays a note about the maximum allowed file size', () => { + const maxAttachmentSize = 500; + wrapper = createComponent({ injectedProperties: { maxAttachmentSize } }); + expect(findModal().text()).toContain(`The maximum file size allowed is ${maxAttachmentSize}`); + }); + + describe('form', () => { + const importCsvIssuesPath = 'gitlab-org/gitlab-test/-/issues/import_csv'; + + beforeEach(() => { + wrapper = createComponent({ injectedProperties: { importCsvIssuesPath } }); + }); + + it('displays the form with the correct action and inputs', () => { + expect(findForm().exists()).toBe(true); + expect(findForm().attributes('action')).toBe(importCsvIssuesPath); + expect(findAuthenticityToken()).toBe('mock-csrf-token'); + expect(findFileInput()).toExist(); + }); + + it('displays the correct primary button action text', () => { + expect(findPrimaryButton()).toExist(); + }); + + it('submits the form when the primary action is clicked', async () => { + findPrimaryButton().click(); + + expect(formSubmitSpy).toHaveBeenCalled(); + }); + }); + }); +}); |