123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
- import { getNode } from '@formkit/core'
- import { within } from '@testing-library/vue'
- import { expect } from 'vitest'
- 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 { mockFormUpdaterQuery } from '#shared/components/Form/graphql/queries/formUpdater.mocks.ts'
- import {
- mockTicketUpdateMutation,
- waitForTicketUpdateMutationCalls,
- } from '#shared/entities/ticket/graphql/mutations/update.mocks.ts'
- import { mockTicketQuery } from '#shared/entities/ticket/graphql/queries/ticket.mocks.ts'
- import { createDummyTicket } from '#shared/entities/ticket-article/__tests__/mocks/ticket.ts'
- import { EnumUserErrorException } from '#shared/graphql/types.ts'
- import { convertToGraphQLId } from '#shared/graphql/utils.ts'
- import { mockLinkListQuery } from '../graphql/queries/linkList.mocks.ts'
- describe('Ticket detail view', () => {
- beforeEach(() => {
- mockPermissions(['ticket.agent'])
- mockLinkListQuery({
- linkList: [],
- })
- })
- describe('Time accounting', () => {
- it('shows accounted time information in sidebar', async () => {
- await mockApplicationConfig({
- time_accounting_types: true,
- })
- const ticket = createDummyTicket({
- state: {
- id: convertToGraphQLId('Ticket::State', 2),
- name: 'open',
- stateType: {
- id: convertToGraphQLId('TicketStateType', 2),
- name: 'open',
- },
- },
- articleType: 'email',
- defaultPolicy: {
- update: true,
- agentReadAccess: true,
- },
- timeUnit: 15,
- timeUnitsPerType: [
- {
- name: 'None',
- timeUnit: 6,
- },
- {
- name: 'Finance',
- timeUnit: 5,
- },
- {
- name: 'Business',
- timeUnit: 4,
- },
- ],
- })
- mockTicketQuery({
- ticket,
- })
- const view = await visitView('/tickets/1')
- const sidebar = view.getByLabelText('Content sidebar')
- expect(
- within(sidebar).getByRole('heading', {
- level: 3,
- name: 'Accounted Time',
- }),
- ).toBeInTheDocument()
- expect(within(sidebar).getByText('Total')).toBeInTheDocument()
- expect(within(sidebar).getByText('15')).toBeInTheDocument()
- })
- it('opens time accounting flyout when the condition is met', async () => {
- mockApplicationConfig({
- ui_ticket_zoom_article_note_new_internal: true,
- time_accounting: true,
- time_accounting_unit: '',
- time_accounting_types: false,
- })
- const ticket = createDummyTicket({
- state: {
- id: convertToGraphQLId('Ticket::State', 2),
- name: 'open',
- stateType: {
- id: convertToGraphQLId('TicketStateType', 2),
- name: 'open',
- },
- },
- articleType: 'email',
- defaultPolicy: {
- update: true,
- agentReadAccess: true,
- },
- })
- mockTicketQuery({
- ticket,
- })
- mockFormUpdaterQuery({
- formUpdater: {
- fields: {
- group_id: {
- options: [
- {
- value: 1,
- label: 'Users',
- },
- {
- value: 2,
- label: 'test group',
- },
- ],
- },
- owner_id: {
- options: [
- {
- value: 3,
- label: 'Test Admin Agent',
- },
- ],
- },
- state_id: {
- options: [
- {
- value: 4,
- label: 'closed',
- },
- {
- value: 2,
- label: 'open',
- },
- {
- value: 6,
- label: 'pending close',
- },
- {
- value: 3,
- label: 'pending reminder',
- },
- ],
- },
- pending_time: {
- show: false,
- },
- priority_id: {
- options: [
- {
- value: 1,
- label: '1 low',
- },
- {
- value: 2,
- label: '2 normal',
- },
- {
- value: 3,
- label: '3 high',
- },
- ],
- },
- },
- flags: {
- newArticlePresent: false,
- },
- },
- })
- const view = await visitView('/tickets/1')
- await view.events.click(
- await view.findByRole('button', { name: 'Add internal note' }),
- )
- await view.events.type(
- await view.findByRole('textbox', { name: 'Text' }),
- 'Foo note',
- )
- mockTicketUpdateMutation({
- ticketUpdate: {
- ticket: null,
- errors: [
- {
- message: 'The ticket time accounting condition is met.',
- exception:
- EnumUserErrorException.ServiceTicketUpdateValidatorTimeAccountingError,
- },
- ],
- },
- })
- mockFormUpdaterQuery({
- formUpdater: {
- fields: {},
- },
- })
- await view.events.click(
- await view.findByRole('button', { name: 'Update' }),
- )
- await waitForTicketUpdateMutationCalls()
- const flyout = await view.findByRole('complementary', {
- name: 'Time Accounting',
- })
- expect(
- within(flyout).getByRole('heading', {
- level: 2,
- }),
- ).toHaveTextContent('Time Accounting')
- await view.events.type(
- await within(flyout).findByLabelText('Accounted Time'),
- '1',
- )
- await getNode('form-ticket-time-accounting')?.settled
- mockTicketUpdateMutation({
- ticketUpdate: {
- ticket,
- errors: null,
- },
- })
- await view.events.click(
- within(flyout).getByRole('button', {
- name: 'Account Time',
- }),
- )
- const calls = await waitForTicketUpdateMutationCalls()
- expect(calls.at(-1)?.variables).toEqual(
- expect.objectContaining({
- input: expect.objectContaining({
- article: expect.objectContaining({
- timeUnit: 1,
- }),
- }),
- }),
- )
- expect(
- view.queryByRole('complementary', {
- name: 'Time Accounting',
- }),
- ).not.toBeInTheDocument()
- })
- })
- })
|