// Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ import { getAllByRole, getByRole, queryByRole } from '@testing-library/vue' import { flushPromises } from '@vue/test-utils' import { visitView } from '#tests/support/components/visitView.ts' import { mockApplicationConfig } from '#tests/support/mock-applicationConfig.ts' import { mockAuthentication } from '#tests/support/mock-authentication.ts' import { mockPermissions } from '#tests/support/mock-permissions.ts' import { mockFormUpdaterQuery } from '#shared/components/Form/graphql/queries/formUpdater.mocks.ts' import { mockUserAddMutation, waitForUserAddMutationCalls, } from '#shared/entities/user/graphql/mutations/add.mocks.ts' import { EnumSystemSetupInfoStatus } from '#shared/graphql/types.ts' import { mockSystemSetupInfoQuery } from '../graphql/queries/systemSetupInfo.mocks.ts' describe('guided setup manual invite', () => { describe('when system is not ready', () => { beforeEach(() => { mockApplicationConfig({ system_init_done: false, }) }) it('redirects to guided setup start', async () => { mockSystemSetupInfoQuery({ systemSetupInfo: { status: EnumSystemSetupInfoStatus.New, type: null, }, }) const view = await visitView('/guided-setup/manual/invite') await vi.waitFor(() => { expect( view, 'correctly redirects to guided setup start screen', ).toHaveCurrentUrl('/guided-setup') }) view.getByText('Set up a new system') }) }) describe('when system is ready for optional steps', () => { beforeEach(() => { mockApplicationConfig({ system_init_done: true, }) mockPermissions(['admin']) mockAuthentication(true) mockFormUpdaterQuery({ formUpdater: { fields: { role_ids: { initialValue: [2], options: [ { value: 1, label: 'Admin', description: 'To configure your system.', }, { value: 2, label: 'Agent', description: 'To work on Tickets.', }, { value: 3, label: 'Customer', description: 'People who create Tickets ask for help.', }, ], }, group_ids: { options: [ { value: 1, label: 'Users', }, { value: 2, label: 'some group1', }, ], }, }, }, }) }) it('can invite user and rerender the form', async () => { const view = await visitView('/guided-setup/manual/invite') await flushPromises() expect(view.getByText('Invite Colleagues')).toBeInTheDocument() expect(view.getByLabelText('First name')).toBeInTheDocument() expect(view.getByLabelText('Last name')).toBeInTheDocument() expect(view.getByLabelText('Email')).toBeInTheDocument() expect(view.getByLabelText('Roles')).toBeInTheDocument() expect(view.getByLabelText('Group permissions')).toBeInTheDocument() expect( view.getByRole('button', { name: 'Finish Setup' }), ).toBeInTheDocument() await view.events.type(view.getByLabelText('Email'), 'test@example.com') await view.events.click(view.getAllByRole('switch')[2]) const groupPermissions = view.getByLabelText('Group permissions') const combobox = getByRole(groupPermissions, 'combobox') await view.events.click(combobox) const listbox = view.getByRole('listbox') const options = getAllByRole(listbox, 'option') await view.events.click(options[0]) await view.events.click(view.getByLabelText('Full')) expect(view.getByLabelText('Email')).toHaveValue('test@example.com') expect(view.getAllByRole('switch')[2]).toHaveAttribute( 'aria-checked', 'true', ) expect(queryByRole(combobox, 'listitem')).toBeInTheDocument() expect(view.getByLabelText('Full')).toBeChecked() const inviteButton = view.getByRole('button', { name: 'Send Invitation', }) await view.events.click(inviteButton) const calls = await waitForUserAddMutationCalls() expect(calls.at(-1)?.variables).toEqual({ input: { firstname: '', lastname: '', email: 'test@example.com', roleIds: ['gid://zammad/Role/2', 'gid://zammad/Role/3'], groupIds: [ { groupInternalId: 1, accessType: ['full'], }, ], objectAttributeValues: [], }, sendInvite: true, }) expect(await view.findByText('Invitation sent!')).toBeInTheDocument() expect(view.getByLabelText('Email')).toHaveValue('') expect(view.getAllByRole('switch')[2]).not.toHaveAttribute( 'aria-checked', 'true', ) expect(queryByRole(combobox, 'listitem')).not.toBeInTheDocument() expect(view.getByLabelText('Full')).not.toBeChecked() expect( view, 'stays on the guided setup manual invite step', ).toHaveCurrentUrl('/guided-setup/manual/invite') }) it('can display form errors', async () => { const view = await visitView('/guided-setup/manual/invite') mockUserAddMutation({ userAdd: { user: null, errors: [ { message: 'At least one identifier (firstname, lastname, phone or email) for user is required.', field: null, }, ], }, }) const inviteButton = view.getByRole('button', { name: 'Send Invitation', }) await view.events.click(inviteButton) expect( view.getByText( 'At least one identifier (firstname, lastname, phone or email) for user is required.', ), ).toHaveRole('alert') }) it('redirects to guided setup finish screen when continued', async () => { const view = await visitView('/guided-setup/manual/invite') await view.events.click( view.getByRole('button', { name: 'Finish Setup' }), ) await vi.waitFor(() => { expect( view, 'correctly redirects to guided setup finish screen', ).toHaveCurrentUrl('/guided-setup/manual/finish') }) }) }) })