diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-10-17 21:10:11 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-10-17 21:10:11 +0300 |
commit | 5cbf24858edb03505b16474e3b7b41a49b677ff6 (patch) | |
tree | 21999fbb911fe2410cf498b3ed668d202dc90098 /spec/frontend | |
parent | 673a1a02e97181a5f2d94cd0b116ebb4dba3d875 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
14 files changed, 340 insertions, 161 deletions
diff --git a/spec/frontend/crm/crm_form_spec.js b/spec/frontend/crm/crm_form_spec.js index fabf43ceb9d..083b49b7c30 100644 --- a/spec/frontend/crm/crm_form_spec.js +++ b/spec/frontend/crm/crm_form_spec.js @@ -10,7 +10,7 @@ import routes from '~/crm/contacts/routes'; import createContactMutation from '~/crm/contacts/components/graphql/create_contact.mutation.graphql'; import updateContactMutation from '~/crm/contacts/components/graphql/update_contact.mutation.graphql'; import getGroupContactsQuery from '~/crm/contacts/components/graphql/get_group_contacts.query.graphql'; -import createOrganizationMutation from '~/crm/organizations/components/graphql/create_organization.mutation.graphql'; +import createOrganizationMutation from '~/crm/organizations/components/graphql/create_customer_relations_organization.mutation.graphql'; import getGroupOrganizationsQuery from '~/crm/organizations/components/graphql/get_group_organizations.query.graphql'; import { createContactMutationErrorResponse, diff --git a/spec/frontend/crm/organization_form_wrapper_spec.js b/spec/frontend/crm/organization_form_wrapper_spec.js index 8408c1920a9..f15fcac71d5 100644 --- a/spec/frontend/crm/organization_form_wrapper_spec.js +++ b/spec/frontend/crm/organization_form_wrapper_spec.js @@ -2,7 +2,7 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import OrganizationFormWrapper from '~/crm/organizations/components/organization_form_wrapper.vue'; import CrmForm from '~/crm/components/crm_form.vue'; import getGroupOrganizationsQuery from '~/crm/organizations/components/graphql/get_group_organizations.query.graphql'; -import createOrganizationMutation from '~/crm/organizations/components/graphql/create_organization.mutation.graphql'; +import createOrganizationMutation from '~/crm/organizations/components/graphql/create_customer_relations_organization.mutation.graphql'; import updateOrganizationMutation from '~/crm/organizations/components/graphql/update_organization.mutation.graphql'; describe('Customer relations organization form wrapper', () => { diff --git a/spec/frontend/environments/graphql/resolvers/kubernetes_spec.js b/spec/frontend/environments/graphql/resolvers/kubernetes_spec.js index 9788b9062d6..ed15c66f4c6 100644 --- a/spec/frontend/environments/graphql/resolvers/kubernetes_spec.js +++ b/spec/frontend/environments/graphql/resolvers/kubernetes_spec.js @@ -97,7 +97,7 @@ describe('~/frontend/environments/graphql/resolvers', () => { it('should request namespaced services from the cluster_client library if namespace is specified', async () => { const services = await mockResolvers.Query.k8sServices(null, { configuration, namespace }); - expect(mockNamespacedServicesListFn).toHaveBeenCalledWith(namespace); + expect(mockNamespacedServicesListFn).toHaveBeenCalledWith({ namespace }); expect(mockAllServicesListFn).not.toHaveBeenCalled(); expect(services).toEqual(k8sServicesMock); diff --git a/spec/frontend/import/details/mock_data.js b/spec/frontend/import/details/mock_data.js index 67148173404..b61a7f36f85 100644 --- a/spec/frontend/import/details/mock_data.js +++ b/spec/frontend/import/details/mock_data.js @@ -7,7 +7,7 @@ export const mockImportFailures = [ exception_class: 'ActiveRecord::RecordInvalid', exception_message: 'Record invalid', source: 'Gitlab::GithubImport::Importer::PullRequestImporter', - github_identifiers: { + external_identifiers: { iid: 2, issuable_type: 'MergeRequest', object_type: 'pull_request', @@ -22,7 +22,7 @@ export const mockImportFailures = [ exception_class: 'ActiveRecord::RecordInvalid', exception_message: 'Record invalid', source: 'Gitlab::GithubImport::Importer::PullRequestImporter', - github_identifiers: { + external_identifiers: { iid: 3, issuable_type: 'MergeRequest', object_type: 'pull_request', @@ -37,7 +37,7 @@ export const mockImportFailures = [ exception_class: 'NameError', exception_message: 'some message', source: 'Gitlab::GithubImport::Importer::LfsObjectImporter', - github_identifiers: { + external_identifiers: { oid: '3a9257fae9e86faee27d7208cb55e086f18e6f29f48c430bfbc26d42eb', size: 2473979, }, diff --git a/spec/frontend/import_entities/components/group_dropdown_spec.js b/spec/frontend/import_entities/components/group_dropdown_spec.js deleted file mode 100644 index 14f39a35387..00000000000 --- a/spec/frontend/import_entities/components/group_dropdown_spec.js +++ /dev/null @@ -1,94 +0,0 @@ -import { GlSearchBoxByType, GlDropdown } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import Vue, { nextTick } from 'vue'; -import VueApollo from 'vue-apollo'; -import createMockApollo from 'helpers/mock_apollo_helper'; -import waitForPromises from 'helpers/wait_for_promises'; -import GroupDropdown from '~/import_entities/components/group_dropdown.vue'; -import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants'; -import searchNamespacesWhereUserCanImportProjectsQuery from '~/import_entities/import_projects/graphql/queries/search_namespaces_where_user_can_import_projects.query.graphql'; - -Vue.use(VueApollo); - -const makeGroupMock = (fullPath) => ({ - id: `gid://gitlab/Group/${fullPath}`, - fullPath, - name: fullPath, - visibility: 'public', - webUrl: `http://gdk.test:3000/groups/${fullPath}`, - __typename: 'Group', -}); - -const AVAILABLE_NAMESPACES = [ - makeGroupMock('match1'), - makeGroupMock('unrelated'), - makeGroupMock('match2'), -]; - -const SEARCH_NAMESPACES_MOCK = Promise.resolve({ - data: { - currentUser: { - id: 'gid://gitlab/User/1', - groups: { - nodes: AVAILABLE_NAMESPACES, - __typename: 'GroupConnection', - }, - namespace: { - id: 'gid://gitlab/Namespaces::UserNamespace/1', - fullPath: 'root', - __typename: 'Namespace', - }, - __typename: 'UserCore', - }, - }, -}); - -describe('Import entities group dropdown component', () => { - let wrapper; - let namespacesTracker; - - const createComponent = (propsData) => { - const apolloProvider = createMockApollo([ - [searchNamespacesWhereUserCanImportProjectsQuery, () => SEARCH_NAMESPACES_MOCK], - ]); - - namespacesTracker = jest.fn(); - - wrapper = shallowMount(GroupDropdown, { - apolloProvider, - scopedSlots: { - default: namespacesTracker, - }, - stubs: { GlDropdown }, - propsData, - }); - }; - - it('passes namespaces from graphql query to default slot', async () => { - createComponent(); - jest.advanceTimersByTime(DEBOUNCE_DELAY); - await nextTick(); - await waitForPromises(); - await nextTick(); - - expect(namespacesTracker).toHaveBeenCalledWith({ namespaces: AVAILABLE_NAMESPACES }); - }); - - it('filters namespaces based on user input', async () => { - createComponent(); - - namespacesTracker.mockReset(); - wrapper.findComponent(GlSearchBoxByType).vm.$emit('input', 'match'); - jest.advanceTimersByTime(DEBOUNCE_DELAY); - await nextTick(); - await waitForPromises(); - await nextTick(); - - expect(namespacesTracker).toHaveBeenCalledWith({ - namespaces: [ - expect.objectContaining({ fullPath: 'match1' }), - expect.objectContaining({ fullPath: 'match2' }), - ], - }); - }); -}); diff --git a/spec/frontend/import_entities/components/import_target_dropdown_spec.js b/spec/frontend/import_entities/components/import_target_dropdown_spec.js index c12baed2374..ba0bb0b0f74 100644 --- a/spec/frontend/import_entities/components/import_target_dropdown_spec.js +++ b/spec/frontend/import_entities/components/import_target_dropdown_spec.js @@ -18,7 +18,6 @@ describe('ImportTargetDropdown', () => { const defaultProps = { selected: mockUserNamespace, - userNamespace: mockUserNamespace, }; const createComponent = ({ props = {} } = {}) => { @@ -39,7 +38,7 @@ describe('ImportTargetDropdown', () => { }; const findListbox = () => wrapper.findComponent(GlCollapsibleListbox); - const findListboxUsersItems = () => findListbox().props('items')[0].options; + const findListboxFirstGroupItems = () => findListbox().props('items')[0].options; const findListboxGroupsItems = () => findListbox().props('items')[1].options; const waitForQuery = async () => { @@ -63,12 +62,54 @@ describe('ImportTargetDropdown', () => { expect(findListbox().props('toggleText')).toBe('a-group-path-that-is-lo…'); }); - it('passes userNamespace as "Users" group item', () => { - createComponent(); + describe('when used on group import', () => { + beforeEach(() => { + createComponent(); + }); - expect(findListboxUsersItems()).toEqual([ - { text: mockUserNamespace, value: mockUserNamespace }, - ]); + it('adds "No parent" in "Parent" group', () => { + expect(findListboxFirstGroupItems()).toEqual([{ text: 'No parent', value: '' }]); + }); + + it('emits "select" event with { fullPath: "", id: null } when "No parent" is selected', () => { + findListbox().vm.$emit('select', ''); + + expect(wrapper.emitted('select')[0]).toEqual([{ fullPath: '', id: null }]); + }); + + it('emits "select" event with { fullPath, id } when a group is selected', async () => { + await waitForQuery(); + + const mockGroupPath = 'match1'; + + findListbox().vm.$emit('select', mockGroupPath); + + expect(wrapper.emitted('select')[0]).toEqual([ + { fullPath: mockGroupPath, id: `gid://gitlab/Group/${mockGroupPath}` }, + ]); + }); + }); + + describe('when used on project import', () => { + beforeEach(() => { + createComponent({ + props: { userNamespace: mockUserNamespace }, + }); + }); + + it('passes userNamespace as "Users" group item', () => { + expect(findListboxFirstGroupItems()).toEqual([ + { text: mockUserNamespace, value: mockUserNamespace }, + ]); + }); + + it('emits "select" event with path as value', () => { + const mockProjectPath = 'mock-project'; + + findListbox().vm.$emit('select', mockProjectPath); + + expect(wrapper.emitted('select')[0]).toEqual([mockProjectPath]); + }); }); it('passes namespaces from GraphQL as "Groups" group item', async () => { diff --git a/spec/frontend/import_entities/import_groups/components/import_table_spec.js b/spec/frontend/import_entities/import_groups/components/import_table_spec.js index 68fbd8dae18..4fab22e316a 100644 --- a/spec/frontend/import_entities/import_groups/components/import_table_spec.js +++ b/spec/frontend/import_entities/import_groups/components/import_table_spec.js @@ -57,7 +57,7 @@ describe('import table', () => { ]; const findPaginationDropdown = () => wrapper.findByTestId('page-size'); const findTargetNamespaceDropdown = (rowWrapper) => - extendedWrapper(rowWrapper).findByTestId('target-namespace-selector'); + extendedWrapper(rowWrapper).findByTestId('target-namespace-dropdown'); const findTargetNamespaceInput = (rowWrapper) => extendedWrapper(rowWrapper).findByTestId('target-namespace-input'); const findPaginationDropdownText = () => findPaginationDropdown().find('button').text(); diff --git a/spec/frontend/import_entities/import_groups/components/import_target_cell_spec.js b/spec/frontend/import_entities/import_groups/components/import_target_cell_spec.js index 46884a42707..ac95026a9a4 100644 --- a/spec/frontend/import_entities/import_groups/components/import_target_cell_spec.js +++ b/spec/frontend/import_entities/import_groups/components/import_target_cell_spec.js @@ -1,10 +1,9 @@ -import { GlDropdownItem, GlFormInput } from '@gitlab/ui'; +import { GlFormInput } from '@gitlab/ui'; import Vue, { nextTick } from 'vue'; 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 ImportGroupDropdown from '~/import_entities/components/group_dropdown.vue'; +import ImportTargetDropdown from '~/import_entities/components/import_target_dropdown.vue'; import { STATUSES } from '~/import_entities/constants'; import ImportTargetCell from '~/import_entities/import_groups/components/import_target_cell.vue'; import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants'; @@ -37,7 +36,7 @@ describe('import target cell', () => { let group; const findNameInput = () => wrapper.findComponent(GlFormInput); - const findNamespaceDropdown = () => wrapper.findComponent(ImportGroupDropdown); + const findNamespaceDropdown = () => wrapper.findComponent(ImportTargetDropdown); const createComponent = (props) => { apolloProvider = createMockApollo([ @@ -49,7 +48,7 @@ describe('import target cell', () => { wrapper = shallowMount(ImportTargetCell, { apolloProvider, - stubs: { ImportGroupDropdown }, + stubs: { ImportTargetDropdown }, propsData: { groupPathRegex: /.*/, ...props, @@ -73,14 +72,14 @@ describe('import target cell', () => { }); it('emits update-target-namespace when dropdown option is clicked', () => { - const dropdownItem = findNamespaceDropdown().findAllComponents(GlDropdownItem).at(2); + const targetNamespace = { + fullPath: AVAILABLE_NAMESPACES[1].fullPath, + id: AVAILABLE_NAMESPACES[1].id, + }; - dropdownItem.vm.$emit('click'); + findNamespaceDropdown().vm.$emit('select', targetNamespace); - expect(wrapper.emitted('update-target-namespace')).toBeDefined(); - expect(wrapper.emitted('update-target-namespace')[0][0]).toStrictEqual( - AVAILABLE_NAMESPACES[1], - ); + expect(wrapper.emitted('update-target-namespace')[0]).toStrictEqual([targetNamespace]); }); }); @@ -101,36 +100,6 @@ describe('import target cell', () => { }); }); - it('renders only no parent option if available namespaces list is empty', () => { - createComponent({ - group: generateFakeTableEntry({ id: 1, status: STATUSES.NONE }), - availableNamespaces: [], - }); - - const items = findNamespaceDropdown() - .findAllComponents(GlDropdownItem) - .wrappers.map((w) => w.text()); - - expect(items[0]).toBe('No parent'); - expect(items).toHaveLength(1); - }); - - it('renders both no parent option and available namespaces list when available namespaces list is not empty', async () => { - createComponent({ - group: generateFakeTableEntry({ id: 1, status: STATUSES.NONE }), - }); - jest.advanceTimersByTime(DEBOUNCE_DELAY); - await waitForPromises(); - await nextTick(); - - const [firstItem, ...rest] = findNamespaceDropdown() - .findAllComponents(GlDropdownItem) - .wrappers.map((w) => w.text()); - - expect(firstItem).toBe('No parent'); - expect(rest).toHaveLength(AVAILABLE_NAMESPACES.length); - }); - describe('when entity is not available for import', () => { beforeEach(() => { group = generateFakeTableEntry({ @@ -147,6 +116,7 @@ describe('import target cell', () => { describe('when entity is available for import', () => { const FAKE_PROGRESS_MESSAGE = 'progress message'; + beforeEach(() => { group = generateFakeTableEntry({ id: 1, diff --git a/spec/frontend/ml/model_registry/routes/models/index/components/ml_models_index_spec.js b/spec/frontend/ml/model_registry/routes/models/index/components/ml_models_index_spec.js index b4aad5ca292..c1b9aef9634 100644 --- a/spec/frontend/ml/model_registry/routes/models/index/components/ml_models_index_spec.js +++ b/spec/frontend/ml/model_registry/routes/models/index/components/ml_models_index_spec.js @@ -1,6 +1,6 @@ -import { GlLink } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import MlModelsIndexApp from '~/ml/model_registry/routes/models/index'; +import ModelRow from '~/ml/model_registry/routes/models/index/components/model_row.vue'; import { TITLE_LABEL, NO_MODELS_LABEL } from '~/ml/model_registry/routes/models/index/translations'; import Pagination from '~/vue_shared/components/incubation/pagination.vue'; import { mockModels, startCursor, defaultPageInfo } from './mock_data'; @@ -10,10 +10,8 @@ const createWrapper = (propsData = { models: mockModels, pageInfo: defaultPageIn wrapper = shallowMountExtended(MlModelsIndexApp, { propsData }); }; -const findModelLink = (index) => wrapper.findAllComponents(GlLink).at(index); +const findModelRow = (index) => wrapper.findAllComponents(ModelRow).at(index); const findPagination = () => wrapper.findComponent(Pagination); -const modelLinkText = (index) => findModelLink(index).text(); -const modelLinkHref = (index) => findModelLink(index).attributes('href'); const findTitle = () => wrapper.findByText(TITLE_LABEL); const findEmptyLabel = () => wrapper.findByText(NO_MODELS_LABEL); @@ -47,11 +45,8 @@ describe('MlModelsIndex', () => { describe('model list', () => { it('displays the models', () => { - expect(modelLinkHref(0)).toBe(mockModels[0].path); - expect(modelLinkText(0)).toBe(`${mockModels[0].name} / ${mockModels[0].version}`); - - expect(modelLinkHref(1)).toBe(mockModels[1].path); - expect(modelLinkText(1)).toBe(`${mockModels[1].name} / ${mockModels[1].version}`); + expect(findModelRow(0).props('model')).toMatchObject(mockModels[0]); + expect(findModelRow(1).props('model')).toMatchObject(mockModels[1]); }); }); diff --git a/spec/frontend/ml/model_registry/routes/models/index/components/mock_data.js b/spec/frontend/ml/model_registry/routes/models/index/components/mock_data.js index e65552353c3..841a543606f 100644 --- a/spec/frontend/ml/model_registry/routes/models/index/components/mock_data.js +++ b/spec/frontend/ml/model_registry/routes/models/index/components/mock_data.js @@ -3,14 +3,22 @@ export const mockModels = [ name: 'model_1', version: '1.0', path: 'path/to/model_1', + versionCount: 3, }, { name: 'model_2', - version: '1.0', + version: '1.1', path: 'path/to/model_2', + versionCount: 1, }, ]; +export const modelWithoutVersion = { + name: 'model_without_version', + path: 'path/to/model_without_version', + versionCount: 0, +}; + export const startCursor = 'eyJpZCI6IjE2In0'; export const defaultPageInfo = Object.freeze({ diff --git a/spec/frontend/ml/model_registry/routes/models/index/components/model_row_spec.js b/spec/frontend/ml/model_registry/routes/models/index/components/model_row_spec.js new file mode 100644 index 00000000000..7600288f560 --- /dev/null +++ b/spec/frontend/ml/model_registry/routes/models/index/components/model_row_spec.js @@ -0,0 +1,42 @@ +import { GlLink } from '@gitlab/ui'; +import { + mockModels, + modelWithoutVersion, +} from 'jest/ml/model_registry/routes/models/index/components/mock_data'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import ModelRow from '~/ml/model_registry/routes/models/index/components/model_row.vue'; + +let wrapper; +const createWrapper = (model = mockModels[0]) => { + wrapper = shallowMountExtended(ModelRow, { propsData: { model } }); +}; + +const findLink = () => wrapper.findComponent(GlLink); +const findMessage = (message) => wrapper.findByText(message); + +describe('ModelRow', () => { + beforeEach(() => { + createWrapper(); + }); + + it('Has a link to the model', () => { + expect(findLink().text()).toBe(mockModels[0].name); + expect(findLink().attributes('href')).toBe(mockModels[0].path); + }); + + it('Shows the latest version and the version count', () => { + expect(findMessage('1.0 · 3 versions').exists()).toBe(true); + }); + + it('Shows the latest version and no version count if it has only 1 version', () => { + createWrapper(mockModels[1]); + + expect(findMessage('1.1 · No other versions').exists()).toBe(true); + }); + + it('Shows no version message if model has no versions', () => { + createWrapper(modelWithoutVersion); + + expect(findMessage('No registered versions').exists()).toBe(true); + }); +}); diff --git a/spec/frontend/notes/components/email_participants_warning_spec.js b/spec/frontend/notes/components/email_participants_warning_spec.js index 34b7524d8fb..620c753e3c5 100644 --- a/spec/frontend/notes/components/email_participants_warning_spec.js +++ b/spec/frontend/notes/components/email_participants_warning_spec.js @@ -1,10 +1,12 @@ import { mount } from '@vue/test-utils'; +import { GlButton } from '@gitlab/ui'; + import EmailParticipantsWarning from '~/notes/components/email_participants_warning.vue'; describe('Email Participants Warning Component', () => { let wrapper; - const findMoreButton = () => wrapper.find('button'); + const findMoreButton = () => wrapper.findComponent(GlButton); const createWrapper = (emails) => { wrapper = mount(EmailParticipantsWarning, { @@ -48,7 +50,7 @@ describe('Email Participants Warning Component', () => { describe('when more button clicked', () => { beforeEach(() => { - findMoreButton().trigger('click'); + findMoreButton().vm.$emit('click'); }); it('more button no longer exists', () => { diff --git a/spec/frontend/organizations/new/components/app_spec.js b/spec/frontend/organizations/new/components/app_spec.js new file mode 100644 index 00000000000..2206a6f0384 --- /dev/null +++ b/spec/frontend/organizations/new/components/app_spec.js @@ -0,0 +1,103 @@ +import VueApollo from 'vue-apollo'; +import Vue, { nextTick } from 'vue'; + +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import App from '~/organizations/new/components/app.vue'; +import resolvers from '~/organizations/shared/graphql/resolvers'; +import NewEditForm from '~/organizations/shared/components/new_edit_form.vue'; +import { visitUrl } from '~/lib/utils/url_utility'; +import { createOrganizationResponse } from '~/organizations/mock_data'; +import { createAlert } from '~/alert'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; + +Vue.use(VueApollo); +jest.useFakeTimers(); + +jest.mock('~/lib/utils/url_utility'); +jest.mock('~/alert'); + +describe('OrganizationNewApp', () => { + let wrapper; + let mockApollo; + + const createComponent = ({ mockResolvers = resolvers } = {}) => { + mockApollo = createMockApollo([], mockResolvers); + + wrapper = shallowMountExtended(App, { apolloProvider: mockApollo }); + }; + + const findForm = () => wrapper.findComponent(NewEditForm); + const submitForm = async () => { + findForm().vm.$emit('submit', { name: 'Foo bar', path: 'foo-bar' }); + await nextTick(); + }; + + afterEach(() => { + mockApollo = null; + }); + + it('renders form', () => { + createComponent(); + + expect(findForm().exists()).toBe(true); + }); + + describe('when form is submitted', () => { + describe('when API is loading', () => { + beforeEach(async () => { + const mockResolvers = { + Mutation: { + createOrganization: jest.fn().mockReturnValueOnce(new Promise(() => {})), + }, + }; + + createComponent({ mockResolvers }); + + await submitForm(); + }); + + it('sets `NewEditForm` `loading` prop to `true`', () => { + expect(findForm().props('loading')).toBe(true); + }); + }); + + describe('when API request is successful', () => { + beforeEach(async () => { + createComponent(); + await submitForm(); + jest.runAllTimers(); + await waitForPromises(); + }); + + it('redirects user to organization path', () => { + expect(visitUrl).toHaveBeenCalledWith(createOrganizationResponse.organization.path); + }); + }); + + describe('when API request is not successful', () => { + const error = new Error(); + + beforeEach(async () => { + const mockResolvers = { + Mutation: { + createOrganization: jest.fn().mockRejectedValueOnce(error), + }, + }; + + createComponent({ mockResolvers }); + await submitForm(); + jest.runAllTimers(); + await waitForPromises(); + }); + + it('displays error alert', () => { + expect(createAlert).toHaveBeenCalledWith({ + message: 'An error occurred creating an organization. Please try again.', + error, + captureError: true, + }); + }); + }); + }); +}); diff --git a/spec/frontend/organizations/shared/components/new_edit_form_spec.js b/spec/frontend/organizations/shared/components/new_edit_form_spec.js new file mode 100644 index 00000000000..43c099fbb1c --- /dev/null +++ b/spec/frontend/organizations/shared/components/new_edit_form_spec.js @@ -0,0 +1,112 @@ +import { GlButton, GlInputGroupText, GlTruncate } from '@gitlab/ui'; + +import NewEditForm from '~/organizations/shared/components/new_edit_form.vue'; +import { mountExtended } from 'helpers/vue_test_utils_helper'; + +describe('NewEditForm', () => { + let wrapper; + + const defaultProvide = { + organizationsPath: '/-/organizations', + rootUrl: 'http://127.0.0.1:3000/', + }; + + const defaultPropsData = { + loading: false, + }; + + const createComponent = ({ propsData = {} } = {}) => { + wrapper = mountExtended(NewEditForm, { + attachTo: document.body, + provide: defaultProvide, + propsData: { + ...defaultPropsData, + ...propsData, + }, + }); + }; + + const findNameField = () => wrapper.findByLabelText('Organization name'); + const findUrlField = () => wrapper.findByLabelText('Organization URL'); + const submitForm = async () => { + await wrapper.findByRole('button', { name: 'Create organization' }).trigger('click'); + }; + + it('renders `Organization name` field', () => { + createComponent(); + + expect(findNameField().exists()).toBe(true); + }); + + it('renders `Organization URL` field', () => { + createComponent(); + + expect(wrapper.findComponent(GlInputGroupText).findComponent(GlTruncate).props('text')).toBe( + 'http://127.0.0.1:3000/-/organizations/', + ); + expect(findUrlField().exists()).toBe(true); + }); + + describe('when form is submitted without filling in required fields', () => { + beforeEach(async () => { + createComponent(); + await submitForm(); + }); + + it('shows error messages', () => { + expect(wrapper.findByText('Organization name is required.').exists()).toBe(true); + expect(wrapper.findByText('Organization URL is required.').exists()).toBe(true); + }); + }); + + describe('when form is submitted successfully', () => { + beforeEach(async () => { + createComponent(); + + await findNameField().setValue('Foo bar'); + await findUrlField().setValue('foo-bar'); + await submitForm(); + }); + + it('emits `submit` event with form values', () => { + expect(wrapper.emitted('submit')).toEqual([[{ name: 'Foo bar', path: 'foo-bar' }]]); + }); + }); + + describe('when `Organization URL` has not been manually set', () => { + beforeEach(async () => { + createComponent(); + + await findNameField().setValue('Foo bar'); + await submitForm(); + }); + + it('sets `Organization URL` when typing in `Organization name`', () => { + expect(findUrlField().element.value).toBe('foo-bar'); + }); + }); + + describe('when `Organization URL` has been manually set', () => { + beforeEach(async () => { + createComponent(); + + await findUrlField().setValue('foo-bar-baz'); + await findNameField().setValue('Foo bar'); + await submitForm(); + }); + + it('does not modify `Organization URL` when typing in `Organization name`', () => { + expect(findUrlField().element.value).toBe('foo-bar-baz'); + }); + }); + + describe('when `loading` prop is `true`', () => { + beforeEach(() => { + createComponent({ propsData: { loading: true } }); + }); + + it('shows button with loading icon', () => { + expect(wrapper.findComponent(GlButton).props('loading')).toBe(true); + }); + }); +}); |