123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
- import { within } from '@testing-library/vue'
- import {
- checkSimpleTableContent,
- checkSimpleTableHeader,
- } from '#tests/support/components/checkSimpleTableContent.ts'
- import { visitView } from '#tests/support/components/visitView.ts'
- import { mockApplicationConfig } from '#tests/support/mock-applicationConfig.ts'
- import { mockPermissions } from '#tests/support/mock-permissions.ts'
- import { mockUserCurrent } from '#tests/support/mock-userCurrent.ts'
- import { waitForNextTick } from '#tests/support/utils.ts'
- import { mockFormUpdaterQuery } from '#shared/components/Form/graphql/queries/formUpdater.mocks.ts'
- import { mockUserCurrentAccessTokenAddMutation } from '#shared/entities/user/current/graphql/mutations/userCurrentAccessTokenAdd.mocks.ts'
- import { mockUserCurrentAccessTokenDeleteMutation } from '#shared/entities/user/current/graphql/mutations/userCurrentAccessTokenDelete.mocks.ts'
- import { mockUserCurrentAccessTokenListQuery } from '#shared/entities/user/current/graphql/queries/userCurrentAcessTokenList.mocks.ts'
- import { convertToGraphQLId } from '#shared/graphql/utils.ts'
- import { getUserCurrentAccessTokenUpdatesSubscriptionHandler } from '../graphql/subscriptions/userCurrentAccessTokenUpdates.mocks.ts'
- vi.hoisted(() => {
- vi.setSystemTime(new Date('2024-04-25T10:00:00Z'))
- })
- const userCurrentAccessTokenList = [
- {
- id: convertToGraphQLId('Token', 1),
- name: 'Example Token',
- preferences: {
- permission: ['user_preferences.access_token'],
- },
- createdAt: '2020-12-18T17:26:00Z',
- expiresAt: null,
- lastUsedAt: '2024-02-01T17:00:00Z',
- },
- {
- id: convertToGraphQLId('Token', 2),
- name: 'Ticket Handling',
- preferences: {
- permission: ['admin.user', 'admin.organization'],
- },
- createdAt: '2022-01-31T12:00:00Z',
- expiresAt: '2024-06-01T10:00:00Z',
- lastUsedAt: null,
- },
- ]
- const rowContents = [
- [
- 'Example Token',
- 'user_preferences.access_token',
- ['2020-12-18 17:26', '3 years ago'],
- '-',
- ['2024-02-01 17:00', '2 months ago'],
- ],
- [
- 'Ticket Handling',
- 'admin.user, admin.organization',
- ['2022-01-31 12:00', '2 years ago'],
- ['2024-06-01 10:00', 'in 1 month'],
- '-',
- ],
- ]
- describe('personal settings for token access', () => {
- beforeEach(() => {
- mockUserCurrent({
- firstname: 'John',
- lastname: 'Doe',
- })
- mockPermissions(['user_preferences.access_token'])
- mockApplicationConfig({
- api_token_access: true,
- })
- })
- afterAll(() => {
- vi.useRealTimers()
- })
- it('show initial message when no token exists yet', async () => {
- mockUserCurrentAccessTokenListQuery({ userCurrentAccessTokenList: [] })
- const view = await visitView('/personal-setting/token-access')
- expect(
- view.getByText(
- 'You can generate a personal access token for each application you use that needs access to the Zammad API.',
- ),
- ).toBeInTheDocument()
- expect(
- view.getByText(
- "Pick a name for the application, and we'll give you a unique token.",
- ),
- ).toBeInTheDocument()
- })
- it('redirects to the error page when api token access is disabled', async () => {
- mockApplicationConfig({
- api_token_access: false,
- })
- const view = await visitView('/personal-setting/token-access')
- await vi.waitFor(() => {
- expect(view, 'correctly redirects to error page').toHaveCurrentUrl(
- '/error',
- )
- })
- })
- it('show existing personal access token', async () => {
- mockUserCurrentAccessTokenListQuery({ userCurrentAccessTokenList })
- const view = await visitView('/personal-setting/token-access')
- const tableLabel = 'Personal Access Tokens'
- const tableHeaders = [
- 'Name',
- 'Permissions',
- 'Created',
- 'Expires',
- 'Last Used',
- 'Actions',
- ]
- checkSimpleTableHeader(view, tableHeaders, tableLabel)
- checkSimpleTableContent(view, rowContents, tableLabel)
- const table = within(view.getByRole('table', { name: tableLabel }))
- expect(
- table.getAllByRole('button', { name: 'Delete this access token' }),
- ).toHaveLength(2)
- })
- it('can delete an personal access token', async () => {
- mockUserCurrentAccessTokenListQuery({ userCurrentAccessTokenList })
- const view = await visitView('/personal-setting/token-access')
- const table = within(view.getByRole('table'))
- const deleteButton = within(table.getAllByRole('row')[0]).getByRole(
- 'button',
- {
- name: 'Delete this access token',
- },
- )
- mockUserCurrentAccessTokenDeleteMutation({
- userCurrentAccessTokenDelete: {
- success: true,
- },
- })
- await view.events.click(deleteButton)
- await waitForNextTick()
- expect(
- await view.findByRole('dialog', { name: 'Delete Object' }),
- ).toBeInTheDocument()
- await view.events.click(view.getByRole('button', { name: 'Delete Object' }))
- checkSimpleTableContent(view, [rowContents[1]])
- })
- it('updates the personal access token list when a new access token is added', async () => {
- mockUserCurrentAccessTokenListQuery({ userCurrentAccessTokenList })
- const view = await visitView('/personal-setting/token-access')
- const accessTokenUpdateSubscription =
- getUserCurrentAccessTokenUpdatesSubscriptionHandler()
- accessTokenUpdateSubscription.trigger({
- userCurrentAccessTokenUpdates: {
- tokens: [
- ...userCurrentAccessTokenList,
- {
- id: convertToGraphQLId('Token', 3),
- name: 'New Token',
- preferences: {
- permission: ['ticket.agent'],
- },
- createdAt: '2024-04-25T09:59:59Z',
- expiresAt: null,
- lastUsedAt: null,
- },
- ],
- },
- })
- await waitForNextTick()
- const newAccessTokenRowContents = [
- 'New Token',
- 'ticket.agent',
- ['2024-04-25 09:59', 'just now'],
- ]
- checkSimpleTableContent(view, [...rowContents, newAccessTokenRowContents])
- })
- it('create new personal access token', async () => {
- mockFormUpdaterQuery({
- formUpdater: {
- fields: {
- permissions: {
- options: [
- {
- value: 'report',
- label: 'Report (%s)',
- description: 'To access the report interface.',
- },
- {
- value: 'ticket',
- label: 'Ticket (%s)',
- description: 'To access the ticket interface.',
- disabled: true,
- children: [
- {
- value: 'ticket.agent',
- label: 'Agent Tickets (%s)',
- description:
- 'To access the agent tickets based on group access.',
- },
- ],
- },
- ],
- },
- },
- },
- })
- mockUserCurrentAccessTokenListQuery({ userCurrentAccessTokenList })
- const view = await visitView('/personal-setting/token-access')
- const newAccessTokenButton = view.getByRole('button', {
- name: 'New Personal Access Token',
- })
- await view.events.click(newAccessTokenButton)
- const flyout = await view.findByRole('complementary', {
- name: 'New Personal Access Token',
- })
- expect(flyout).toBeInTheDocument()
- const name = await view.findByLabelText('Name')
- await view.events.type(name, 'A new token')
- const input = view.getByLabelText('Expiration date')
- await view.events.type(input, '2024-12-31{Enter}')
- const permissionsField = within(view.getByLabelText('Permissions'))
- const permissions = permissionsField.getAllByRole('treeitem')
- const toggleSwitch = within(permissions[0]).getByRole('switch')
- await view.events.click(toggleSwitch)
- mockUserCurrentAccessTokenAddMutation({
- userCurrentAccessTokenAdd: {
- tokenValue: 'new-token-1234',
- token: {
- id: convertToGraphQLId('Token', 3),
- name: 'A new token',
- preferences: {
- permission: ['report'],
- },
- createdAt: '2024-04-25T09:59:59Z',
- expiresAt: '2024-12-31T00:00:00Z',
- lastUsedAt: null,
- user: {
- id: '123',
- },
- },
- errors: null,
- },
- })
- await view.events.click(view.getByRole('button', { name: 'Create' }))
- expect(
- await view.findByLabelText('Your Personal Access Token'),
- ).toHaveValue('new-token-1234')
- await view.events.click(
- view.getByRole('button', {
- name: 'OK, I have copied my token',
- }),
- )
- expect(flyout).not.toBeInTheDocument()
- checkSimpleTableContent(view, [
- [
- 'A new token',
- 'report',
- ['2024-04-25 09:59', 'just now'],
- ['2024-12-31 00:00', 'in 8 months'],
- '-',
- ],
- ...rowContents,
- ])
- })
- })
|