Maintenance: Update graphqlcodegenerator monorepo (major)
Approved-by: Benjamin Scharf <bs@zammad.com> Co-authored-by: Dominik Klein <dk@zammad.com>
This commit is contained in:
committed by
Dominik Klein
parent
885fa533aa
commit
4366446b9b
@@ -18,6 +18,18 @@ const mockerPreset = {
|
||||
},
|
||||
}
|
||||
|
||||
const documents = ['app/frontend/shared/**/*.graphql', 'app/frontend/apps/**/*.graphql']
|
||||
|
||||
const scalars = {
|
||||
BinaryString: 'string',
|
||||
NonEmptyString: 'string',
|
||||
FormId: 'string',
|
||||
ISO8601Date: 'string',
|
||||
ISO8601DateTime: 'string',
|
||||
JSON: 'any',
|
||||
UriHttpString: 'string',
|
||||
}
|
||||
|
||||
/** @type {import('@graphql-codegen/cli').CodegenConfig} */
|
||||
const config = {
|
||||
overwrite: true,
|
||||
@@ -27,28 +39,34 @@ const config = {
|
||||
addDocBlocks: false,
|
||||
},
|
||||
generates: {
|
||||
'./app/frontend/shared/graphql/types.ts': {
|
||||
documents: [
|
||||
'app/frontend/shared/**/*.graphql',
|
||||
'app/frontend/apps/**/*.graphql',
|
||||
],
|
||||
'./app/frontend/shared/graphql/schema-types.ts': {
|
||||
config: {
|
||||
scalars: {
|
||||
BinaryString: 'string',
|
||||
NonEmptyString: 'string',
|
||||
FormId: 'string',
|
||||
ISO8601Date: 'string',
|
||||
ISO8601DateTime: 'string',
|
||||
UriHttpString: 'string',
|
||||
},
|
||||
scalars,
|
||||
},
|
||||
plugins: ['typescript', 'typescript-operations'],
|
||||
plugins: ['typescript'],
|
||||
},
|
||||
'./app/frontend/shared/graphql/types.ts': {
|
||||
documents,
|
||||
config: {
|
||||
scalars,
|
||||
importSchemaTypesFrom: './app/frontend/shared/graphql/schema-types.ts',
|
||||
namespacedImportName: 'Types',
|
||||
// eslint-disable-next-line zammad/zammad-detect-translatable-string
|
||||
maybeValue: 'T | null | undefined',
|
||||
nonOptionalTypename: true,
|
||||
skipTypeNameForRoot: true,
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
add: {
|
||||
content: ["export * from './schema-types'", 'export type { Exact }'],
|
||||
},
|
||||
},
|
||||
'typescript-operations',
|
||||
],
|
||||
},
|
||||
'./app/frontend/': {
|
||||
documents: [
|
||||
'app/frontend/shared/**/*.graphql',
|
||||
'app/frontend/apps/**/*.graphql',
|
||||
],
|
||||
documents,
|
||||
preset: 'near-operation-file',
|
||||
presetConfig: {
|
||||
baseTypesPath: '~#shared/graphql/types.ts',
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
"ignorePatterns": [
|
||||
"app/frontend/**/graphql/**/*.ts",
|
||||
"!app/frontend/tests/graphql/**/*.ts",
|
||||
"app/frontend/shared/graphql/schema-types.ts",
|
||||
"app/frontend/shared/graphql/types.ts",
|
||||
"app/frontend/shared/types/config.ts"
|
||||
]
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { useTicketNumberAndTitle } from '#shared/entities/ticket/composables/useTicketNumberAndTitle.ts'
|
||||
import type { TicketById } from '#shared/entities/ticket/types.ts'
|
||||
import type { TicketLabel } from '#shared/entities/ticket/types.ts'
|
||||
import { EnumTicketStateColorCode } from '#shared/graphql/types.ts'
|
||||
|
||||
import CommonTicketStateIndicatorIcon from '#desktop/components/CommonTicketStateIndicator/CommonTicketStateIndicatorIcon.vue'
|
||||
|
||||
interface Props {
|
||||
ticket?: Partial<TicketById> | null
|
||||
ticket?: TicketLabel | null
|
||||
unauthorized?: boolean
|
||||
noLink?: boolean
|
||||
noWrap?: boolean
|
||||
|
||||
@@ -2,9 +2,15 @@
|
||||
|
||||
import { nullableMock } from '#tests/support/utils.ts'
|
||||
|
||||
import type { AutocompleteSearchGenericEntry } from '#shared/graphql/types.ts'
|
||||
import type { AutocompleteSearchGenericQuery } from '#shared/graphql/types.ts'
|
||||
import { convertToGraphQLId } from '#shared/graphql/utils.ts'
|
||||
|
||||
type AutocompleteSearchGenericEntry =
|
||||
AutocompleteSearchGenericQuery['autocompleteSearchGeneric'][number]
|
||||
type GenericEntryObject = NonNullable<AutocompleteSearchGenericEntry['object']>
|
||||
type GenericUser = Extract<GenericEntryObject, { __typename: 'User' }>
|
||||
type GenericOrganization = Extract<GenericEntryObject, { __typename: 'Organization' }>
|
||||
|
||||
export const testOptions: AutocompleteSearchGenericEntry[] = [
|
||||
{
|
||||
__typename: 'AutocompleteSearchGenericEntry',
|
||||
@@ -14,8 +20,7 @@ export const testOptions: AutocompleteSearchGenericEntry[] = [
|
||||
heading: 'Zammad Foundation',
|
||||
headingPlaceholder: [],
|
||||
disabled: false,
|
||||
icon: null,
|
||||
object: nullableMock({
|
||||
object: nullableMock<GenericUser>({
|
||||
__typename: 'User',
|
||||
id: convertToGraphQLId('User', 2),
|
||||
internalId: 2,
|
||||
@@ -31,12 +36,6 @@ export const testOptions: AutocompleteSearchGenericEntry[] = [
|
||||
outOfOfficeEndAt: null,
|
||||
active: true,
|
||||
vip: false,
|
||||
createdAt: '2022-11-30T12:40:15Z',
|
||||
updatedAt: '2022-11-30T12:40:15Z',
|
||||
policy: {
|
||||
update: true,
|
||||
destroy: false,
|
||||
},
|
||||
organization: null,
|
||||
hasSecondaryOrganizations: false,
|
||||
}),
|
||||
@@ -49,8 +48,7 @@ export const testOptions: AutocompleteSearchGenericEntry[] = [
|
||||
heading: '%s people',
|
||||
headingPlaceholder: ['1'],
|
||||
disabled: true,
|
||||
icon: null,
|
||||
object: nullableMock({
|
||||
object: nullableMock<GenericOrganization>({
|
||||
__typename: 'Organization',
|
||||
id: convertToGraphQLId('Organization', 1),
|
||||
internalId: 1,
|
||||
@@ -61,7 +59,7 @@ export const testOptions: AutocompleteSearchGenericEntry[] = [
|
||||
edges: [
|
||||
{
|
||||
__typename: 'UserEdge',
|
||||
node: nullableMock({
|
||||
node: nullableMock<GenericUser>({
|
||||
__typename: 'User',
|
||||
id: convertToGraphQLId('User', 1),
|
||||
internalId: 1,
|
||||
@@ -77,28 +75,9 @@ export const testOptions: AutocompleteSearchGenericEntry[] = [
|
||||
outOfOfficeEndAt: null,
|
||||
active: true,
|
||||
vip: false,
|
||||
createdAt: '2022-11-30T12:40:15Z',
|
||||
updatedAt: '2022-11-30T12:40:15Z',
|
||||
policy: {
|
||||
update: true,
|
||||
destroy: false,
|
||||
},
|
||||
}),
|
||||
cursor: 'MH',
|
||||
},
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
startCursor: 'MH',
|
||||
},
|
||||
totalCount: 1,
|
||||
},
|
||||
createdAt: '2022-11-30T12:40:15Z',
|
||||
updatedAt: '2022-11-30T12:40:15Z',
|
||||
policy: {
|
||||
update: true,
|
||||
destroy: false,
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -2,9 +2,21 @@
|
||||
import type { AutoCompleteCustomerGenericOption } from '#shared/components/Form/fields/FieldCustomer/types.ts'
|
||||
import type { User } from '#shared/graphql/types.ts'
|
||||
|
||||
type AutocompleteCustomerUser = Extract<
|
||||
NonNullable<AutoCompleteCustomerGenericOption['object']>,
|
||||
{ __typename: 'User' }
|
||||
>
|
||||
|
||||
export const useFieldCustomerOption = (object: User): AutoCompleteCustomerGenericOption => ({
|
||||
__typename: 'AutocompleteSearchGenericEntry',
|
||||
value: object.internalId,
|
||||
label: (object.fullname || object.phone || object.login) as string,
|
||||
labelPlaceholder: null,
|
||||
heading: object.organization?.name,
|
||||
object,
|
||||
headingPlaceholder: null,
|
||||
disabled: false,
|
||||
object: {
|
||||
...object,
|
||||
__typename: 'User',
|
||||
} as AutocompleteCustomerUser,
|
||||
})
|
||||
|
||||
@@ -4,6 +4,7 @@ import { flushPromises } from '@vue/test-utils'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import { renderComponent } from '#tests/support/components/index.ts'
|
||||
import { nullableMock } from '#tests/support/utils.ts'
|
||||
|
||||
import type {
|
||||
MentionKnowledgeBaseItem,
|
||||
@@ -22,7 +23,7 @@ const baseProps = {
|
||||
|
||||
describe('component for rendering suggestions', () => {
|
||||
it('renders knowledge base article', () => {
|
||||
const items: MentionKnowledgeBaseItem[] = [
|
||||
const items = nullableMock<MentionKnowledgeBaseItem[]>([
|
||||
{
|
||||
__typename: 'KnowledgeBaseAnswerTranslation',
|
||||
id: convertToGraphQLId('KnowledgeBaseAnswerTranslation', 1),
|
||||
@@ -74,7 +75,7 @@ describe('component for rendering suggestions', () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
])
|
||||
|
||||
const view = renderComponent(FieldEditorSuggestionList, {
|
||||
props: {
|
||||
@@ -98,14 +99,14 @@ describe('component for rendering suggestions', () => {
|
||||
})
|
||||
|
||||
it('renders text item', () => {
|
||||
const items: MentionTextItem[] = [
|
||||
const items = nullableMock<MentionTextItem[]>([
|
||||
{
|
||||
name: 'Text Item',
|
||||
keywords: 'key',
|
||||
renderedContent: 'content',
|
||||
id: convertToGraphQLId('TextModule', 1),
|
||||
},
|
||||
]
|
||||
])
|
||||
|
||||
const view = renderComponent(FieldEditorSuggestionList, {
|
||||
props: {
|
||||
@@ -121,14 +122,14 @@ describe('component for rendering suggestions', () => {
|
||||
})
|
||||
|
||||
it('renders text item with spaces in search query', () => {
|
||||
const items: MentionTextItem[] = [
|
||||
const items = nullableMock<MentionTextItem[]>([
|
||||
{
|
||||
name: 'the best',
|
||||
keywords: 'best',
|
||||
renderedContent: 'wishing you all the best',
|
||||
id: convertToGraphQLId('TextModule', 1),
|
||||
},
|
||||
]
|
||||
])
|
||||
|
||||
const view = renderComponent(FieldEditorSuggestionList, {
|
||||
props: {
|
||||
@@ -144,7 +145,7 @@ describe('component for rendering suggestions', () => {
|
||||
})
|
||||
|
||||
it('renders user mention', () => {
|
||||
const items: MentionUserItem[] = [
|
||||
const items = nullableMock<MentionUserItem[]>([
|
||||
{
|
||||
id: convertToGraphQLId('User', 1),
|
||||
fullname: 'John Doe',
|
||||
@@ -156,7 +157,7 @@ describe('component for rendering suggestions', () => {
|
||||
fullname: 'Nicole Braun',
|
||||
internalId: 2,
|
||||
},
|
||||
]
|
||||
])
|
||||
|
||||
const view = renderComponent(FieldEditorSuggestionList, {
|
||||
props: {
|
||||
@@ -178,7 +179,7 @@ describe('component for rendering suggestions', () => {
|
||||
})
|
||||
|
||||
it('renders user mention with spaces in search query', () => {
|
||||
const items: MentionUserItem[] = [
|
||||
const items = nullableMock<MentionUserItem[]>([
|
||||
{
|
||||
id: convertToGraphQLId('User', 1),
|
||||
fullname: 'John Doe',
|
||||
@@ -190,7 +191,7 @@ describe('component for rendering suggestions', () => {
|
||||
fullname: 'Nicole Braun',
|
||||
internalId: 2,
|
||||
},
|
||||
]
|
||||
])
|
||||
|
||||
const view = renderComponent(FieldEditorSuggestionList, {
|
||||
props: {
|
||||
@@ -209,7 +210,7 @@ describe('component for rendering suggestions', () => {
|
||||
})
|
||||
|
||||
describe('actions in list', () => {
|
||||
const items: MentionUserItem[] = [
|
||||
const items = nullableMock<MentionUserItem[]>([
|
||||
{
|
||||
id: convertToGraphQLId('User', 1),
|
||||
fullname: 'John Doe',
|
||||
@@ -225,7 +226,7 @@ describe('actions in list', () => {
|
||||
fullname: 'Erik Wise',
|
||||
internalId: 3,
|
||||
},
|
||||
]
|
||||
])
|
||||
|
||||
const renderList = () => {
|
||||
const listExposed = ref<{
|
||||
@@ -312,7 +313,7 @@ describe('actions in list', () => {
|
||||
})
|
||||
|
||||
describe('accessibility (combobox + listbox wiring)', () => {
|
||||
const items: MentionTextItem[] = [
|
||||
const items = nullableMock<MentionTextItem[]>([
|
||||
{
|
||||
name: 'Greeting',
|
||||
keywords: 'hi',
|
||||
@@ -325,7 +326,7 @@ describe('accessibility (combobox + listbox wiring)', () => {
|
||||
renderedContent: 'Goodbye',
|
||||
id: convertToGraphQLId('TextModule', 2),
|
||||
},
|
||||
]
|
||||
])
|
||||
|
||||
it('uses the label prop as the listbox aria-label', () => {
|
||||
const view = renderComponent(FieldEditorSuggestionList, {
|
||||
|
||||
@@ -5,7 +5,36 @@ import type { Organization } from '#shared/graphql/types.ts'
|
||||
export const useFieldOrganizationOption = (
|
||||
organization: Organization,
|
||||
): AutoCompleteOrganizationOption => ({
|
||||
__typename: 'AutocompleteSearchOrganizationEntry',
|
||||
value: organization.internalId,
|
||||
label: organization.name as string,
|
||||
organization,
|
||||
labelPlaceholder: null,
|
||||
heading: null,
|
||||
headingPlaceholder: null,
|
||||
disabled: false,
|
||||
icon: null,
|
||||
organization: {
|
||||
__typename: 'Organization',
|
||||
id: organization.id,
|
||||
internalId: organization.internalId,
|
||||
name: organization.name,
|
||||
shared: organization.shared,
|
||||
domain: organization.domain,
|
||||
domainAssignment: organization.domainAssignment,
|
||||
active: organization.active,
|
||||
note: organization.note,
|
||||
vip: organization.vip,
|
||||
objectAttributeValues: organization.objectAttributeValues?.map(
|
||||
({ attribute, value, renderedLink }) => ({
|
||||
__typename: 'ObjectAttributeValue',
|
||||
value,
|
||||
renderedLink,
|
||||
attribute: {
|
||||
__typename: 'ObjectManagerFrontendAttribute',
|
||||
name: attribute.name,
|
||||
display: attribute.display,
|
||||
},
|
||||
}),
|
||||
),
|
||||
},
|
||||
})
|
||||
|
||||
@@ -29,10 +29,12 @@ const testOptions: AutocompleteSearchTicketQuery['autocompleteSearchTicket'][0][
|
||||
disabled: false,
|
||||
icon: null,
|
||||
ticket: nullableMock({
|
||||
__typename: 'Ticket',
|
||||
id: convertToGraphQLId('Ticket', 1),
|
||||
internalId: 1,
|
||||
number: '123456',
|
||||
state: nullableMock({
|
||||
__typename: 'TicketState' as const,
|
||||
id: convertToGraphQLId('TicketState', 1),
|
||||
name: 'open',
|
||||
}),
|
||||
@@ -49,10 +51,12 @@ const testOptions: AutocompleteSearchTicketQuery['autocompleteSearchTicket'][0][
|
||||
disabled: false,
|
||||
icon: null,
|
||||
ticket: nullableMock({
|
||||
__typename: 'Ticket',
|
||||
id: convertToGraphQLId('Ticket', 2),
|
||||
internalId: 1,
|
||||
number: '55555',
|
||||
state: nullableMock({
|
||||
__typename: 'TicketState' as const,
|
||||
id: convertToGraphQLId('TicketState', 1),
|
||||
name: 'open',
|
||||
}),
|
||||
@@ -69,10 +73,12 @@ const testOptions: AutocompleteSearchTicketQuery['autocompleteSearchTicket'][0][
|
||||
disabled: false,
|
||||
icon: null,
|
||||
ticket: nullableMock({
|
||||
__typename: 'Ticket',
|
||||
id: convertToGraphQLId('Ticket', 3),
|
||||
internalId: 1,
|
||||
number: '99999',
|
||||
state: nullableMock({
|
||||
__typename: 'TicketState' as const,
|
||||
id: convertToGraphQLId('TicketState', 1),
|
||||
name: 'open',
|
||||
}),
|
||||
|
||||
@@ -18,6 +18,7 @@ export const DetailSearchDocument = gql`
|
||||
) {
|
||||
totalCount
|
||||
items {
|
||||
__typename
|
||||
... on Ticket {
|
||||
id
|
||||
internalId
|
||||
|
||||
@@ -18,6 +18,7 @@ query detailSearch(
|
||||
) {
|
||||
totalCount
|
||||
items {
|
||||
__typename
|
||||
... on Ticket {
|
||||
id
|
||||
internalId
|
||||
|
||||
@@ -14,6 +14,7 @@ export const QuickSearchDocument = gql`
|
||||
) {
|
||||
totalCount
|
||||
items {
|
||||
__typename
|
||||
... on Organization {
|
||||
id
|
||||
internalId
|
||||
@@ -25,6 +26,7 @@ export const QuickSearchDocument = gql`
|
||||
quickSearchTickets: search(search: $search, onlyIn: Ticket, limit: $limit) {
|
||||
totalCount
|
||||
items {
|
||||
__typename
|
||||
... on Ticket {
|
||||
id
|
||||
internalId
|
||||
@@ -41,6 +43,7 @@ export const QuickSearchDocument = gql`
|
||||
quickSearchUsers: search(search: $search, onlyIn: User, limit: $limit) {
|
||||
totalCount
|
||||
items {
|
||||
__typename
|
||||
... on User {
|
||||
id
|
||||
internalId
|
||||
|
||||
@@ -2,6 +2,7 @@ query quickSearch($search: String!, $limit: Int = 10) {
|
||||
quickSearchOrganizations: search(search: $search, onlyIn: Organization, limit: $limit) {
|
||||
totalCount
|
||||
items {
|
||||
__typename
|
||||
... on Organization {
|
||||
id
|
||||
internalId
|
||||
@@ -13,6 +14,7 @@ query quickSearch($search: String!, $limit: Int = 10) {
|
||||
quickSearchTickets: search(search: $search, onlyIn: Ticket, limit: $limit) {
|
||||
totalCount
|
||||
items {
|
||||
__typename
|
||||
... on Ticket {
|
||||
id
|
||||
internalId
|
||||
@@ -29,6 +31,7 @@ query quickSearch($search: String!, $limit: Int = 10) {
|
||||
quickSearchUsers: search(search: $search, onlyIn: User, limit: $limit) {
|
||||
totalCount
|
||||
items {
|
||||
__typename
|
||||
... on User {
|
||||
id
|
||||
internalId
|
||||
|
||||
@@ -10,8 +10,10 @@ export const SearchTaskbarItemStateUpdatesDocument = gql`
|
||||
userCurrentTaskbarItemStateUpdates(taskbarItemId: $taskbarItemId) {
|
||||
stateUpdateType
|
||||
taskbarItem {
|
||||
__typename
|
||||
id
|
||||
entity {
|
||||
__typename
|
||||
... on UserTaskbarItemEntitySearch {
|
||||
query
|
||||
model
|
||||
|
||||
@@ -2,8 +2,10 @@ subscription searchTaskbarItemStateUpdates($taskbarItemId: ID!) {
|
||||
userCurrentTaskbarItemStateUpdates(taskbarItemId: $taskbarItemId) {
|
||||
stateUpdateType
|
||||
taskbarItem {
|
||||
__typename
|
||||
id
|
||||
entity {
|
||||
__typename
|
||||
... on UserTaskbarItemEntitySearch {
|
||||
query
|
||||
model
|
||||
|
||||
@@ -40,7 +40,7 @@ const router = useRouter()
|
||||
const goToUserProfile = () => {
|
||||
if (!props.filters.customerId) return
|
||||
|
||||
router.push(`/users/${getIdFromGraphQLId(props.filters.customerId)}`)
|
||||
router.push(`/users/${getIdFromGraphQLId(String(props.filters.customerId))}`)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
import type { TicketById } from '#shared/entities/ticket/types.ts'
|
||||
import type { Ticket } from '#shared/graphql/types.ts'
|
||||
import type { TicketLabel } from '#shared/entities/ticket/types.ts'
|
||||
import { useSessionStore } from '#shared/stores/session.ts'
|
||||
|
||||
import { type Props as CommonPopoverProps } from '#desktop//components/CommonPopover/CommonPopover.vue'
|
||||
@@ -14,7 +13,7 @@ import CommonTicketLabel from '#desktop/components/CommonTicketLabel/CommonTicke
|
||||
import TicketPopover from './TicketPopoverWithTrigger/TicketPopover.vue'
|
||||
|
||||
export interface Props {
|
||||
ticket: Partial<Ticket | TicketById>
|
||||
ticket: TicketLabel
|
||||
popoverConfig?: Omit<CommonPopoverProps, 'owner'>
|
||||
triggerClass?: string
|
||||
noTriggerLink?: boolean
|
||||
|
||||
@@ -5,13 +5,8 @@ import { computed } from 'vue'
|
||||
|
||||
import ObjectAttributes from '#shared/components/ObjectAttributes/ObjectAttributes.vue'
|
||||
import { useObjectAttributes } from '#shared/entities/object-attributes/composables/useObjectAttributes.ts'
|
||||
import type { TicketById } from '#shared/entities/ticket/types.ts'
|
||||
import {
|
||||
EnumObjectManagerObjects,
|
||||
type Organization,
|
||||
type Ticket,
|
||||
type User,
|
||||
} from '#shared/graphql/types.ts'
|
||||
import type { TicketById, TicketLabel } from '#shared/entities/ticket/types.ts'
|
||||
import { EnumObjectManagerObjects, type Organization, type User } from '#shared/graphql/types.ts'
|
||||
import QueryHandler from '#shared/server/apollo/handler/QueryHandler.ts'
|
||||
import { SYSTEM_USER_ID } from '#shared/utils/constants.ts'
|
||||
|
||||
@@ -25,7 +20,7 @@ import { useTicketInfoForPopoverQuery } from './graphql/queries/ticketInfoForPop
|
||||
import TicketPopoverSkeleton from './skeleton/TicketPopoverSkeleton.vue'
|
||||
|
||||
interface Props {
|
||||
ticket: Partial<Ticket | TicketById>
|
||||
ticket: TicketLabel
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import '#tests/graphql/builders/mocks.ts'
|
||||
import { renderComponent, type ExtendedMountingOptions } from '#tests/support/components/index.ts'
|
||||
import { mockRouterHooks } from '#tests/support/mock-vue-router.ts'
|
||||
import { waitForNextTick } from '#tests/support/utils.ts'
|
||||
import { nullableMock, waitForNextTick } from '#tests/support/utils.ts'
|
||||
|
||||
import type { TicketByList } from '#shared/entities/ticket/types.ts'
|
||||
import { EnumTicketStateColorCode } from '#shared/graphql/types.ts'
|
||||
@@ -37,7 +37,8 @@ const renderListTable = async (
|
||||
const tableHeaders = ['title', 'owner', 'state', 'stateIcon', 'priority']
|
||||
|
||||
const tableItems: TicketByList[] = [
|
||||
{
|
||||
nullableMock<TicketByList>({
|
||||
__typename: 'Ticket',
|
||||
id: convertToGraphQLId('Ticket', 1),
|
||||
title: 'Dummy ticket',
|
||||
owner: {
|
||||
@@ -59,6 +60,7 @@ const tableItems: TicketByList[] = [
|
||||
__typename: 'TicketPriority',
|
||||
id: convertToGraphQLId('TicketPriority', 3),
|
||||
name: '3 high',
|
||||
uiColor: null,
|
||||
},
|
||||
createdAt: '2021-01-01T12:00:00Z',
|
||||
internalId: 0,
|
||||
@@ -66,20 +68,20 @@ const tableItems: TicketByList[] = [
|
||||
updatedAt: '',
|
||||
stateColorCode: EnumTicketStateColorCode.Closed,
|
||||
customer: {
|
||||
__typename: undefined,
|
||||
__typename: 'User',
|
||||
id: '',
|
||||
fullname: undefined,
|
||||
},
|
||||
group: {
|
||||
__typename: undefined,
|
||||
__typename: 'Group',
|
||||
id: '',
|
||||
name: undefined,
|
||||
},
|
||||
policy: {
|
||||
__typename: undefined,
|
||||
__typename: 'PolicyTicket',
|
||||
update: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
]
|
||||
|
||||
describe('TicketListTable', () => {
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
import type { FormSchemaField } from '#shared/components/Form/types.ts'
|
||||
import { useOrganizationFormSchema } from '#shared/entities/organization/composables/useOrganizationFormSchema.ts'
|
||||
import { useOrganizationUpdateMutation } from '#shared/entities/organization/graphql/mutations/update.api.ts'
|
||||
import type { EditableOrganization } from '#shared/entities/organization/types.ts'
|
||||
import { defineFormSchema } from '#shared/form/defineFormSchema.ts'
|
||||
import type { OrganizationQuery } from '#shared/graphql/types.ts'
|
||||
import { EnumFormUpdaterId, EnumObjectManagerObjects } from '#shared/graphql/types.ts'
|
||||
import type { ConfidentTake } from '#shared/types/utils.ts'
|
||||
|
||||
import { openFlyout } from '#desktop/components/CommonFlyout/useFlyout.ts'
|
||||
import { useFlyoutObjectForm } from '#desktop/components/CommonFlyoutObjectForm/useFlyoutObjectForm.ts'
|
||||
@@ -46,7 +45,7 @@ const buildOrganizationEditFormChangeFields = (): Record<string, Partial<FormSch
|
||||
}
|
||||
|
||||
export const openOrganizationEditFlyout = async (
|
||||
organization: ConfidentTake<OrganizationQuery, 'organization'>,
|
||||
organization: EditableOrganization,
|
||||
options?: { title: string },
|
||||
) => {
|
||||
const formChangeFields = buildOrganizationEditFormChangeFields()
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
import type { FormSchemaField } from '#shared/components/Form/types.ts'
|
||||
import { useUserFormSchema } from '#shared/entities/user/composables/useUserFormSchema.ts'
|
||||
import { useUserUpdateMutation } from '#shared/entities/user/graphql/mutations/update.api.ts'
|
||||
import type { EditableUser } from '#shared/entities/user/types.ts'
|
||||
import { defineFormSchema } from '#shared/form/defineFormSchema.ts'
|
||||
import type { UserQuery } from '#shared/graphql/types.ts'
|
||||
import { EnumFormUpdaterId, EnumObjectManagerObjects } from '#shared/graphql/types.ts'
|
||||
import { useApplicationStore } from '#shared/stores/application.ts'
|
||||
import type { ConfidentTake } from '#shared/types/utils.ts'
|
||||
|
||||
import { openFlyout } from '#desktop/components/CommonFlyout/useFlyout.ts'
|
||||
import { useFlyoutObjectForm } from '#desktop/components/CommonFlyoutObjectForm/useFlyoutObjectForm.ts'
|
||||
@@ -49,10 +48,7 @@ const buildUserEditFormChangeFields = (): Record<string, Partial<FormSchemaField
|
||||
}
|
||||
}
|
||||
|
||||
export const openUserEditFlyout = async (
|
||||
user: ConfidentTake<UserQuery, 'user'>,
|
||||
options?: { title: string },
|
||||
) => {
|
||||
export const openUserEditFlyout = async (user: EditableUser, options?: { title: string }) => {
|
||||
const application = useApplicationStore()
|
||||
|
||||
const formChangeFields = buildUserEditFormChangeFields()
|
||||
|
||||
@@ -10,6 +10,7 @@ export const UserCurrentTaskbarItemAttributesFragmentDoc = gql`
|
||||
formId
|
||||
formNewArticlePresent
|
||||
entity {
|
||||
__typename
|
||||
... on Ticket {
|
||||
...ticketTaskbarTabAttributes
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ fragment userCurrentTaskbarItemAttributes on UserTaskbarItem {
|
||||
formId
|
||||
formNewArticlePresent
|
||||
entity {
|
||||
__typename
|
||||
... on Ticket {
|
||||
...ticketTaskbarTabAttributes
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ export type ReactiveFunction<TParam> = () => TParam;
|
||||
export const UserCurrentRecentCloseListDocument = gql`
|
||||
query userCurrentRecentCloseList($limit: Int) {
|
||||
userCurrentRecentCloseList(limit: $limit) {
|
||||
__typename
|
||||
... on Ticket {
|
||||
id
|
||||
internalId
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
query userCurrentRecentCloseList($limit: Int) {
|
||||
userCurrentRecentCloseList(limit: $limit) {
|
||||
__typename
|
||||
... on Ticket {
|
||||
id
|
||||
internalId
|
||||
|
||||
@@ -13,6 +13,7 @@ import { EnumTwoFactorAuthenticationMethod, type LoginMutation } from '#shared/g
|
||||
import { GraphQLErrorTypes } from '#shared/types/error.ts'
|
||||
|
||||
const twoFactorAuthentication = () => ({
|
||||
__typename: 'UserLoginTwoFactorMethods' as const,
|
||||
availableTwoFactorAuthenticationMethods: [EnumTwoFactorAuthenticationMethod.AuthenticatorApp],
|
||||
defaultTwoFactorAuthenticationMethod: EnumTwoFactorAuthenticationMethod.AuthenticatorApp,
|
||||
recoveryCodesAvailable: false,
|
||||
@@ -148,6 +149,7 @@ describe('two-factor login flow', () => {
|
||||
describe('alternative two-factor methods', () => {
|
||||
it.each([
|
||||
{
|
||||
__typename: 'UserLoginTwoFactorMethods' as const,
|
||||
availableTwoFactorAuthenticationMethods: [
|
||||
EnumTwoFactorAuthenticationMethod.AuthenticatorApp,
|
||||
],
|
||||
@@ -157,6 +159,7 @@ describe('two-factor login flow', () => {
|
||||
available: false,
|
||||
},
|
||||
{
|
||||
__typename: 'UserLoginTwoFactorMethods' as const,
|
||||
availableTwoFactorAuthenticationMethods: [
|
||||
EnumTwoFactorAuthenticationMethod.AuthenticatorApp,
|
||||
],
|
||||
@@ -166,6 +169,7 @@ describe('two-factor login flow', () => {
|
||||
available: true,
|
||||
},
|
||||
{
|
||||
__typename: 'UserLoginTwoFactorMethods' as const,
|
||||
availableTwoFactorAuthenticationMethods: [
|
||||
EnumTwoFactorAuthenticationMethod.AuthenticatorApp,
|
||||
EnumTwoFactorAuthenticationMethod.SecurityKeys,
|
||||
|
||||
@@ -10,12 +10,15 @@ import securityKeys from '#shared/entities/two-factor/plugins/security-keys.ts'
|
||||
import type { TwoFactorPlugin, TwoFactorSetupResult } from '#shared/entities/two-factor/types.ts'
|
||||
import { TwoFactorMethodInitiateAuthenticationDocument } from '#shared/graphql/mutations/twoFactorMethodInitiateAuthentication.api.ts'
|
||||
import type { TwoFactorMethodInitiateAuthenticationMutation } from '#shared/graphql/types.ts'
|
||||
import type { DeepPartial } from '#shared/types/utils.ts'
|
||||
import { createDeferred } from '#shared/utils/helpers.ts'
|
||||
|
||||
import LoginTwoFactor from '../LoginTwoFactor.vue'
|
||||
|
||||
const prepareInitialData = (
|
||||
data: TwoFactorMethodInitiateAuthenticationMutation['twoFactorMethodInitiateAuthentication'],
|
||||
data: DeepPartial<
|
||||
TwoFactorMethodInitiateAuthenticationMutation['twoFactorMethodInitiateAuthentication']
|
||||
>,
|
||||
) => {
|
||||
mockGraphQLResult<TwoFactorMethodInitiateAuthenticationMutation>(
|
||||
TwoFactorMethodInitiateAuthenticationDocument,
|
||||
|
||||
@@ -86,7 +86,8 @@ const schema = defineFormSchema([
|
||||
])
|
||||
|
||||
const initialFormValues = computed<NotificationFormData>((oldValues) => {
|
||||
const { notificationConfig = {}, notificationSound = {} } = user.value?.personalSettings || {}
|
||||
const notificationConfig = user.value?.personalSettings?.notificationConfig
|
||||
const notificationSound = user.value?.personalSettings?.notificationSound
|
||||
|
||||
const values: NotificationFormData = {
|
||||
group_ids: notificationConfig?.groupIds ?? [],
|
||||
|
||||
@@ -12,8 +12,8 @@ import { useTicketSidebar } from '#desktop/pages/ticket/composables/useTicketSid
|
||||
const { ticket } = useTicketInformation()
|
||||
const ticketSidebar = useTicketSidebar()
|
||||
|
||||
const referencingTickets = computed(
|
||||
() => ticket?.value?.referencingChecklistTickets as ReferencingTicket[],
|
||||
const referencingTickets = computed<ReferencingTicket[]>(
|
||||
() => ticket?.value?.referencingChecklistTickets ?? [],
|
||||
)
|
||||
|
||||
const checklist = computed(() => ticket?.value?.checklist)
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
// Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
|
||||
|
||||
import type { Ticket } from '#shared/graphql/types.ts'
|
||||
import type { ReferencingTicket } from '#shared/entities/ticket/types.ts'
|
||||
|
||||
import type { MenuItem } from '#desktop/components/CommonPopoverMenu/types.ts'
|
||||
|
||||
export type ReferencingTicket = Pick<
|
||||
Ticket,
|
||||
'id' | 'internalId' | 'number' | 'title' | 'state' | 'stateColorCode'
|
||||
>
|
||||
export type { ReferencingTicket } from '#shared/entities/ticket/types.ts'
|
||||
|
||||
export interface TicketReferenceMenuItem extends MenuItem {
|
||||
ticket: ReferencingTicket
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { TicketById } from '#shared/entities/ticket/types.ts'
|
||||
import { EnumTicketStateColorCode } from '#shared/graphql/types.ts'
|
||||
import { convertToGraphQLId } from '#shared/graphql/utils.ts'
|
||||
|
||||
export const testOptionsTopBar: TicketById = nullableMock({
|
||||
export const testOptionsTopBar: TicketById = nullableMock<TicketById>({
|
||||
__typename: 'Ticket',
|
||||
createArticleType: {
|
||||
__typename: 'TicketArticleType',
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import { computed, toRef, type Ref } from 'vue'
|
||||
|
||||
import type { TicketById } from '#shared/entities/ticket/types.ts'
|
||||
import { EnumTicketScreenBehavior } from '#shared/graphql/types.ts'
|
||||
import { useWalker } from '#shared/router/walker.ts'
|
||||
import { useSessionStore } from '#shared/stores/session.ts'
|
||||
@@ -34,7 +33,13 @@ export const useTicketScreenBehavior = (currentTaskbarTabId: Ref<string | undefi
|
||||
ticket,
|
||||
}: {
|
||||
screenBehaviour?: EnumTicketScreenBehavior
|
||||
ticket: TicketById
|
||||
ticket: {
|
||||
state: {
|
||||
stateType: {
|
||||
name: string
|
||||
}
|
||||
}
|
||||
}
|
||||
}) => {
|
||||
const currentScreenBehaviour = screenBehaviour || secondaryAction.value
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
type TicketChecklistUpdatesSubscription,
|
||||
} from '#shared/graphql/types.ts'
|
||||
import { convertToGraphQLId } from '#shared/graphql/utils.ts'
|
||||
import type { DeepPartial } from '#shared/types/utils.ts'
|
||||
import '#tests/graphql/builders/mocks.ts'
|
||||
|
||||
import { waitForTicketChecklistAddMutationCalls } from '#desktop/pages/ticket/graphql/mutations/ticketChecklistAdd.mocks.ts'
|
||||
@@ -76,63 +77,78 @@ const templateMocks: Partial<ChecklistTemplate>[] = [
|
||||
},
|
||||
]
|
||||
|
||||
const checklistItemsMock: Partial<ChecklistItem>[] = [
|
||||
{
|
||||
const checklistItemsMock: ChecklistItem[] = [
|
||||
nullableMock<ChecklistItem>({
|
||||
__typename: 'ChecklistItem',
|
||||
id: convertToGraphQLId('Checklist::Item', 1),
|
||||
text: 'Checklist item A',
|
||||
checked: false,
|
||||
ticketReference: null,
|
||||
},
|
||||
{
|
||||
}),
|
||||
nullableMock<ChecklistItem>({
|
||||
__typename: 'ChecklistItem',
|
||||
id: convertToGraphQLId('Checklist::Item', 2),
|
||||
text: 'Checklist item B',
|
||||
checked: false,
|
||||
ticketReference: null,
|
||||
},
|
||||
}),
|
||||
]
|
||||
|
||||
type TicketChecklist = NonNullable<
|
||||
TicketChecklistUpdatesSubscription['ticketChecklistUpdates']['ticketChecklist']
|
||||
>
|
||||
|
||||
// Overrides the default checklist values if set to null, checklist will be empty
|
||||
const mockChecklistUpdateSubscription = async (
|
||||
partialTicketChecklist: Partial<
|
||||
TicketChecklistUpdatesSubscription['ticketChecklistUpdates']['ticketChecklist']
|
||||
> | null,
|
||||
partialTicketChecklist: DeepPartial<TicketChecklist> | null,
|
||||
) => {
|
||||
if (partialTicketChecklist === null) {
|
||||
await getTicketChecklistUpdatesSubscriptionHandler().trigger({
|
||||
ticketChecklistUpdates: {
|
||||
ticketChecklist: null,
|
||||
},
|
||||
})
|
||||
await getTicketChecklistUpdatesSubscriptionHandler().trigger(
|
||||
nullableMock<TicketChecklistUpdatesSubscription>({
|
||||
ticketChecklistUpdates: {
|
||||
__typename: 'TicketChecklistUpdatesPayload',
|
||||
removedTicketChecklist: null,
|
||||
ticketChecklist: null,
|
||||
},
|
||||
}),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const ticketChecklist = {
|
||||
const ticketChecklist = nullableMock<TicketChecklist>({
|
||||
__typename: 'Checklist',
|
||||
completed: false,
|
||||
incomplete: 2,
|
||||
name: 'Checklist title',
|
||||
id: convertToGraphQLId('Checklist', 1),
|
||||
items: [
|
||||
{
|
||||
__typename: 'ChecklistItem',
|
||||
checked: false,
|
||||
id: convertToGraphQLId('Checklist::Item', 1),
|
||||
text: 'Checklist item A',
|
||||
ticketReference: null,
|
||||
},
|
||||
{
|
||||
__typename: 'ChecklistItem',
|
||||
checked: false,
|
||||
id: convertToGraphQLId('Checklist::Item', 2),
|
||||
text: 'Checklist item B',
|
||||
ticketReference: null,
|
||||
},
|
||||
],
|
||||
...partialTicketChecklist,
|
||||
}
|
||||
|
||||
await getTicketChecklistUpdatesSubscriptionHandler().trigger({
|
||||
ticketChecklistUpdates: {
|
||||
removedTicketChecklist: null,
|
||||
ticketChecklist,
|
||||
},
|
||||
})
|
||||
|
||||
await getTicketChecklistUpdatesSubscriptionHandler().trigger(
|
||||
nullableMock<TicketChecklistUpdatesSubscription>({
|
||||
ticketChecklistUpdates: {
|
||||
__typename: 'TicketChecklistUpdatesPayload',
|
||||
removedTicketChecklist: null,
|
||||
ticketChecklist,
|
||||
},
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
const openMenuAndClickAction = async (
|
||||
|
||||
@@ -203,6 +203,7 @@ describe('TicketSidebarCustomerContent.vue', () => {
|
||||
{
|
||||
...defaultTicket,
|
||||
policy: {
|
||||
__typename: 'PolicyTicket',
|
||||
update: false,
|
||||
agentReadAccess: true,
|
||||
},
|
||||
|
||||
@@ -14,31 +14,37 @@ const createTimeAccountingTicket = (
|
||||
|
||||
const timeUnitsPerType = [
|
||||
{
|
||||
__typename: 'TicketTimeAccountingTypeSum' as const,
|
||||
name: 'None',
|
||||
timeUnit: 6,
|
||||
timeUnitDisplay: '6.00',
|
||||
},
|
||||
{
|
||||
__typename: 'TicketTimeAccountingTypeSum' as const,
|
||||
name: 'Finance',
|
||||
timeUnit: 5,
|
||||
timeUnitDisplay: '5.00',
|
||||
},
|
||||
{
|
||||
__typename: 'TicketTimeAccountingTypeSum' as const,
|
||||
name: 'Business',
|
||||
timeUnit: 4,
|
||||
timeUnitDisplay: '4.00',
|
||||
},
|
||||
{
|
||||
__typename: 'TicketTimeAccountingTypeSum' as const,
|
||||
name: 'Development',
|
||||
timeUnit: 3,
|
||||
timeUnitDisplay: '3.00',
|
||||
},
|
||||
{
|
||||
__typename: 'TicketTimeAccountingTypeSum' as const,
|
||||
name: 'Testing',
|
||||
timeUnit: 2,
|
||||
timeUnitDisplay: '2.00',
|
||||
},
|
||||
{
|
||||
__typename: 'TicketTimeAccountingTypeSum' as const,
|
||||
name: 'Foo',
|
||||
timeUnit: 1,
|
||||
timeUnitDisplay: '1.00',
|
||||
|
||||
@@ -118,6 +118,7 @@ describe('TicketSidebarInformationContent', () => {
|
||||
const wrapper = renderInformationSidebar({
|
||||
...defaultTicket,
|
||||
policy: {
|
||||
__typename: 'PolicyTicket',
|
||||
update: false,
|
||||
agentReadAccess: true,
|
||||
},
|
||||
@@ -138,6 +139,7 @@ describe('TicketSidebarInformationContent', () => {
|
||||
const wrapper = renderInformationSidebar({
|
||||
...defaultTicket,
|
||||
policy: {
|
||||
__typename: 'PolicyTicket',
|
||||
update: true,
|
||||
agentReadAccess: false,
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@ import { getNode } from '@formkit/core'
|
||||
|
||||
import { renderComponent } from '#tests/support/components/index.ts'
|
||||
import { mockRouterHooks } from '#tests/support/mock-vue-router.ts'
|
||||
import { waitForNextTick } from '#tests/support/utils.ts'
|
||||
import { nullableMock, waitForNextTick } from '#tests/support/utils.ts'
|
||||
|
||||
import { waitForTicketSharedDraftStartCreateMutationCalls } from '#shared/entities/ticket-shared-draft-start/graphql/mutations/ticketSharedDraftStartCreate.mocks.ts'
|
||||
import { useTicketSharedDraftStartDeleteMutation } from '#shared/entities/ticket-shared-draft-start/graphql/mutations/ticketSharedDraftStartDelete.api.ts'
|
||||
@@ -35,6 +35,36 @@ vi.mock('#desktop/components/CommonFlyout/useFlyout.ts', async (importOriginal)
|
||||
|
||||
mockRouterHooks()
|
||||
|
||||
type SharedDraftStartItem = TicketSharedDraftStartListQuery['ticketSharedDraftStartList'][number]
|
||||
|
||||
const sharedDraftStart = (
|
||||
id: number,
|
||||
name: string,
|
||||
updatedAt: string,
|
||||
updatedBy?: {
|
||||
id: number
|
||||
firstname: string
|
||||
lastname: string
|
||||
fullname: string
|
||||
},
|
||||
): SharedDraftStartItem =>
|
||||
nullableMock<SharedDraftStartItem>({
|
||||
__typename: 'TicketSharedDraftStart',
|
||||
id: convertToGraphQLId('Ticket::SharedDraftStart', id),
|
||||
name,
|
||||
updatedAt,
|
||||
updatedBy: updatedBy
|
||||
? {
|
||||
__typename: 'User',
|
||||
id: convertToGraphQLId('User', updatedBy.id),
|
||||
internalId: updatedBy.id,
|
||||
firstname: updatedBy.firstname,
|
||||
lastname: updatedBy.lastname,
|
||||
fullname: updatedBy.fullname,
|
||||
}
|
||||
: null,
|
||||
})
|
||||
|
||||
const renderTicketSidebarSharedDraftStartContent = async (
|
||||
sharedDraftStartList: TicketSharedDraftStartListQuery['ticketSharedDraftStartList'],
|
||||
context: {
|
||||
@@ -83,37 +113,19 @@ describe('TicketSidebarSharedDraftStartContent.vue', () => {
|
||||
it('renders non-empty shared draft list', async () => {
|
||||
const wrapper = await renderTicketSidebarSharedDraftStartContent(
|
||||
[
|
||||
{
|
||||
id: convertToGraphQLId('Ticket::SharedDraftStart', 1),
|
||||
name: 'Test shared draft 1',
|
||||
updatedAt: '2024-07-03T13:48:09Z',
|
||||
updatedBy: {
|
||||
__typename: 'User',
|
||||
id: convertToGraphQLId('User', 2),
|
||||
internalId: 2,
|
||||
firstname: 'Erika',
|
||||
lastname: 'Mustermann',
|
||||
fullname: 'Erika Mustermann',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: convertToGraphQLId('Ticket::SharedDraftStart', 2),
|
||||
name: 'Test shared draft 2',
|
||||
updatedAt: '2024-07-03T13:30:00Z',
|
||||
updatedBy: {
|
||||
__typename: 'User',
|
||||
id: convertToGraphQLId('User', 3),
|
||||
internalId: 3,
|
||||
firstname: 'Max',
|
||||
lastname: 'Mustermann',
|
||||
fullname: 'Max Mustermann',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: convertToGraphQLId('Ticket::SharedDraftStart', 3),
|
||||
name: 'Test shared draft 3',
|
||||
updatedAt: '2024-07-02T12:00:00Z',
|
||||
},
|
||||
sharedDraftStart(1, 'Test shared draft 1', '2024-07-03T13:48:09Z', {
|
||||
id: 2,
|
||||
firstname: 'Erika',
|
||||
lastname: 'Mustermann',
|
||||
fullname: 'Erika Mustermann',
|
||||
}),
|
||||
sharedDraftStart(2, 'Test shared draft 2', '2024-07-03T13:30:00Z', {
|
||||
id: 3,
|
||||
firstname: 'Max',
|
||||
lastname: 'Mustermann',
|
||||
fullname: 'Max Mustermann',
|
||||
}),
|
||||
sharedDraftStart(3, 'Test shared draft 3', '2024-07-02T12:00:00Z'),
|
||||
],
|
||||
{
|
||||
formValues: {
|
||||
@@ -140,19 +152,12 @@ describe('TicketSidebarSharedDraftStartContent.vue', () => {
|
||||
it('supports previewing shared draft', async () => {
|
||||
const wrapper = await renderTicketSidebarSharedDraftStartContent(
|
||||
[
|
||||
{
|
||||
id: convertToGraphQLId('Ticket::SharedDraftStart', 1),
|
||||
name: 'Test shared draft 1',
|
||||
updatedAt: '2024-07-03T13:48:09Z',
|
||||
updatedBy: {
|
||||
__typename: 'User',
|
||||
id: convertToGraphQLId('User', 2),
|
||||
internalId: 2,
|
||||
firstname: 'Erika',
|
||||
lastname: 'Mustermann',
|
||||
fullname: 'Erika Mustermann',
|
||||
},
|
||||
},
|
||||
sharedDraftStart(1, 'Test shared draft 1', '2024-07-03T13:48:09Z', {
|
||||
id: 2,
|
||||
firstname: 'Erika',
|
||||
lastname: 'Mustermann',
|
||||
fullname: 'Erika Mustermann',
|
||||
}),
|
||||
],
|
||||
{
|
||||
formValues: {
|
||||
@@ -230,19 +235,12 @@ describe('TicketSidebarSharedDraftStartContent.vue', () => {
|
||||
it('supports updating existing shared draft', async () => {
|
||||
const wrapper = await renderTicketSidebarSharedDraftStartContent(
|
||||
[
|
||||
{
|
||||
id: convertToGraphQLId('Ticket::SharedDraftStart', 1),
|
||||
name: 'Test shared draft 1',
|
||||
updatedAt: '2024-07-03T13:48:09Z',
|
||||
updatedBy: {
|
||||
__typename: 'User',
|
||||
id: convertToGraphQLId('User', 2),
|
||||
internalId: 2,
|
||||
firstname: 'Erika',
|
||||
lastname: 'Mustermann',
|
||||
fullname: 'Erika Mustermann',
|
||||
},
|
||||
},
|
||||
sharedDraftStart(1, 'Test shared draft 1', '2024-07-03T13:48:09Z', {
|
||||
id: 2,
|
||||
firstname: 'Erika',
|
||||
lastname: 'Mustermann',
|
||||
fullname: 'Erika Mustermann',
|
||||
}),
|
||||
],
|
||||
{
|
||||
formValues: {
|
||||
|
||||
@@ -233,9 +233,11 @@ describe('TicketSidebarSummary', () => {
|
||||
it('hides sidebar when ticket got merged', async () => {
|
||||
const wrapper = renderRenderTicketSidebarSummary({
|
||||
state: {
|
||||
__typename: 'TicketState',
|
||||
name: 'merged',
|
||||
id: convertToGraphQLId('State', 5),
|
||||
stateType: {
|
||||
__typename: 'TicketStateType',
|
||||
id: convertToGraphQLId('StateType', 6),
|
||||
name: 'merged',
|
||||
},
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
import { computed, toRef, type Ref } from 'vue'
|
||||
|
||||
import type { TicketById } from '#shared/entities/ticket/types.ts'
|
||||
import type { TicketLabel } from '#shared/entities/ticket/types.ts'
|
||||
import { useApplicationStore } from '#shared/stores/application.ts'
|
||||
|
||||
export const useTicketNumber = (ticket: Ref<TicketById | undefined>) => {
|
||||
export const useTicketNumber = (ticket: Ref<TicketLabel | undefined>) => {
|
||||
const ticketNumber = computed(() => ticket?.value?.number?.toString())
|
||||
|
||||
const config = toRef(useApplicationStore(), 'config')
|
||||
|
||||
@@ -11,6 +11,7 @@ export const LinkAddDocument = gql`
|
||||
link {
|
||||
type
|
||||
item {
|
||||
__typename
|
||||
... on Ticket {
|
||||
id
|
||||
internalId
|
||||
|
||||
@@ -3,6 +3,7 @@ mutation linkAdd($input: LinkInput!) {
|
||||
link {
|
||||
type
|
||||
item {
|
||||
__typename
|
||||
... on Ticket {
|
||||
id
|
||||
internalId
|
||||
|
||||
@@ -10,6 +10,7 @@ export const LinkListDocument = gql`
|
||||
linkList(objectId: $objectId, targetType: $targetType) {
|
||||
type
|
||||
item {
|
||||
__typename
|
||||
... on Ticket {
|
||||
id
|
||||
internalId
|
||||
|
||||
@@ -2,6 +2,7 @@ query linkList($objectId: ID!, $targetType: String!) {
|
||||
linkList(objectId: $objectId, targetType: $targetType) {
|
||||
type
|
||||
item {
|
||||
__typename
|
||||
... on Ticket {
|
||||
id
|
||||
internalId
|
||||
|
||||
@@ -10,6 +10,7 @@ export const LinkUpdatesDocument = gql`
|
||||
linkUpdates(objectId: $objectId, targetType: $targetType) {
|
||||
links {
|
||||
item {
|
||||
__typename
|
||||
... on Ticket {
|
||||
id
|
||||
internalId
|
||||
|
||||
@@ -2,6 +2,7 @@ subscription linkUpdates($objectId: ID!, $targetType: String!) {
|
||||
linkUpdates(objectId: $objectId, targetType: $targetType) {
|
||||
links {
|
||||
item {
|
||||
__typename
|
||||
... on Ticket {
|
||||
id
|
||||
internalId
|
||||
|
||||
@@ -20,8 +20,20 @@ import type { FormKitNode } from '@formkit/core'
|
||||
|
||||
const mockQueryResult = (input: { query: string; limit: number }): AutocompleteSearchUserQuery => {
|
||||
const options = testOptions.map((option) =>
|
||||
nullableMock({
|
||||
nullableMock<AutocompleteSearchUserEntry>({
|
||||
...option,
|
||||
user: {
|
||||
...option.user,
|
||||
__typename: 'User',
|
||||
id: String(option.user.id),
|
||||
organization: option.user.organization
|
||||
? {
|
||||
...option.user.organization,
|
||||
__typename: 'Organization',
|
||||
id: String(option.user.organization.id),
|
||||
}
|
||||
: null,
|
||||
},
|
||||
labelPlaceholder: null,
|
||||
headingPlaceholder: null,
|
||||
disabled: null,
|
||||
@@ -39,12 +51,12 @@ const mockQueryResult = (input: { query: string; limit: number }): AutocompleteS
|
||||
// Search across options via their de-accented labels.
|
||||
const filteredOptions = options.filter(
|
||||
(option) =>
|
||||
filterRegex.test(deaccent(option.label)) || filterRegex.test(deaccent(option.heading)),
|
||||
filterRegex.test(deaccent(option.label)) || filterRegex.test(deaccent(option.heading || '')),
|
||||
) as unknown as AutocompleteSearchUserEntry[]
|
||||
|
||||
return {
|
||||
return nullableMock<AutocompleteSearchUserQuery>({
|
||||
autocompleteSearchUser: filteredOptions.slice(0, input.limit ?? 25),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const wrapperParameters = {
|
||||
|
||||
@@ -6,8 +6,10 @@ import { ref } from 'vue'
|
||||
import { renderComponent } from '#tests/support/components/index.ts'
|
||||
import { mockApplicationConfig } from '#tests/support/mock-applicationConfig.ts'
|
||||
import { mockPermissions } from '#tests/support/mock-permissions.ts'
|
||||
import { nullableMock } from '#tests/support/utils.ts'
|
||||
|
||||
import { mockAiAssistanceTextToolsRunMutation } from '#shared/graphql/mutations/aiAssistanceTextToolsRun.mocks.ts'
|
||||
import type { AiAssistanceTextToolsListQuery } from '#shared/graphql/types.ts'
|
||||
import { useAiAssistantTextToolsStore } from '#shared/stores/aiAssistantTextTools.ts'
|
||||
import getUuid from '#shared/utils/getUuid.ts'
|
||||
|
||||
@@ -312,15 +314,18 @@ describe('basic toolbar testing', () => {
|
||||
const store = useAiAssistantTextToolsStore()
|
||||
|
||||
vi.spyOn(store, 'lookupResult').mockReturnValue(
|
||||
ref({
|
||||
aiAssistanceTextToolsList: [
|
||||
{
|
||||
id: 'expand-tool-123',
|
||||
name: 'Expand',
|
||||
active: true,
|
||||
},
|
||||
],
|
||||
}),
|
||||
ref(
|
||||
nullableMock<AiAssistanceTextToolsListQuery>({
|
||||
aiAssistanceTextToolsList: [
|
||||
{
|
||||
__typename: 'AITextTool',
|
||||
id: 'expand-tool-123',
|
||||
name: 'Expand',
|
||||
active: true,
|
||||
},
|
||||
],
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
const wrapper = renderComponent(FieldEditorActionBar, {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { flushPromises } from '@vue/test-utils'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import { renderComponent } from '#tests/support/components/index.ts'
|
||||
import { nullableMock } from '#tests/support/utils.ts'
|
||||
|
||||
import type {
|
||||
MentionKnowledgeBaseItem,
|
||||
@@ -22,7 +23,7 @@ const baseProps = {
|
||||
|
||||
describe('component for rendering suggestions', () => {
|
||||
it('renders knowledge base article', () => {
|
||||
const items: MentionKnowledgeBaseItem[] = [
|
||||
const items = nullableMock<MentionKnowledgeBaseItem[]>([
|
||||
{
|
||||
__typename: 'KnowledgeBaseAnswerTranslation',
|
||||
id: convertToGraphQLId('KnowledgeBaseAnswerTranslation', 1),
|
||||
@@ -52,7 +53,7 @@ describe('component for rendering suggestions', () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
])
|
||||
|
||||
const view = renderComponent(FieldEditorSuggestionList, {
|
||||
props: {
|
||||
@@ -71,14 +72,14 @@ describe('component for rendering suggestions', () => {
|
||||
})
|
||||
|
||||
it('renders text item', () => {
|
||||
const items: MentionTextItem[] = [
|
||||
const items = nullableMock<MentionTextItem[]>([
|
||||
{
|
||||
name: 'Text Item',
|
||||
keywords: 'key',
|
||||
renderedContent: 'content',
|
||||
id: convertToGraphQLId('TextModule', 1),
|
||||
},
|
||||
]
|
||||
])
|
||||
|
||||
const view = renderComponent(FieldEditorSuggestionList, {
|
||||
props: {
|
||||
@@ -94,14 +95,14 @@ describe('component for rendering suggestions', () => {
|
||||
})
|
||||
|
||||
it('renders text item with spaces in search query', () => {
|
||||
const items: MentionTextItem[] = [
|
||||
const items = nullableMock<MentionTextItem[]>([
|
||||
{
|
||||
name: 'the best',
|
||||
keywords: 'best',
|
||||
renderedContent: 'wishing you all the best',
|
||||
id: convertToGraphQLId('TextModule', 1),
|
||||
},
|
||||
]
|
||||
])
|
||||
|
||||
const view = renderComponent(FieldEditorSuggestionList, {
|
||||
props: {
|
||||
@@ -117,7 +118,7 @@ describe('component for rendering suggestions', () => {
|
||||
})
|
||||
|
||||
it('renders user mention', () => {
|
||||
const items: MentionUserItem[] = [
|
||||
const items = nullableMock<MentionUserItem[]>([
|
||||
{
|
||||
id: convertToGraphQLId('User', 1),
|
||||
fullname: 'John Doe',
|
||||
@@ -129,7 +130,7 @@ describe('component for rendering suggestions', () => {
|
||||
fullname: 'Nicole Braun',
|
||||
internalId: 2,
|
||||
},
|
||||
]
|
||||
])
|
||||
|
||||
const view = renderComponent(FieldEditorSuggestionList, {
|
||||
props: {
|
||||
@@ -146,7 +147,7 @@ describe('component for rendering suggestions', () => {
|
||||
})
|
||||
|
||||
it('renders user mention with spaces in search query', () => {
|
||||
const items: MentionUserItem[] = [
|
||||
const items = nullableMock<MentionUserItem[]>([
|
||||
{
|
||||
id: convertToGraphQLId('User', 1),
|
||||
fullname: 'John Doe',
|
||||
@@ -158,7 +159,7 @@ describe('component for rendering suggestions', () => {
|
||||
fullname: 'Nicole Braun',
|
||||
internalId: 2,
|
||||
},
|
||||
]
|
||||
])
|
||||
|
||||
const view = renderComponent(FieldEditorSuggestionList, {
|
||||
props: {
|
||||
@@ -175,7 +176,7 @@ describe('component for rendering suggestions', () => {
|
||||
})
|
||||
|
||||
describe('actions in list', () => {
|
||||
const items: MentionUserItem[] = [
|
||||
const items = nullableMock<MentionUserItem[]>([
|
||||
{
|
||||
id: convertToGraphQLId('User', 1),
|
||||
fullname: 'John Doe',
|
||||
@@ -191,7 +192,7 @@ describe('actions in list', () => {
|
||||
fullname: 'Erik Wise',
|
||||
internalId: 3,
|
||||
},
|
||||
]
|
||||
])
|
||||
|
||||
const renderList = () => {
|
||||
const listExposed = ref<{
|
||||
@@ -276,7 +277,7 @@ describe('actions in list', () => {
|
||||
})
|
||||
|
||||
describe('accessibility (combobox + listbox wiring)', () => {
|
||||
const items: MentionTextItem[] = [
|
||||
const items = nullableMock<MentionTextItem[]>([
|
||||
{
|
||||
name: 'Greeting',
|
||||
keywords: 'hi',
|
||||
@@ -289,7 +290,7 @@ describe('accessibility (combobox + listbox wiring)', () => {
|
||||
renderedContent: 'Goodbye',
|
||||
id: convertToGraphQLId('TextModule', 2),
|
||||
},
|
||||
]
|
||||
])
|
||||
|
||||
it('uses the label prop as the listbox aria-label', () => {
|
||||
const view = renderComponent(FieldEditorSuggestionList, {
|
||||
|
||||
@@ -25,8 +25,13 @@ const mockQueryResult = (input: {
|
||||
limit: number
|
||||
}): AutocompleteSearchOrganizationQuery => {
|
||||
const options = testOptions.map((option) =>
|
||||
nullableMock({
|
||||
nullableMock<AutocompleteSearchOrganizationEntry>({
|
||||
...option,
|
||||
organization: {
|
||||
...option.organization,
|
||||
__typename: 'Organization',
|
||||
id: String(option.organization.id),
|
||||
},
|
||||
labelPlaceholder: null,
|
||||
headingPlaceholder: null,
|
||||
disabled: null,
|
||||
@@ -44,12 +49,12 @@ const mockQueryResult = (input: {
|
||||
// Search across options via their de-accented labels.
|
||||
const filteredOptions = options.filter(
|
||||
(option) =>
|
||||
filterRegex.test(deaccent(option.label)) || filterRegex.test(deaccent(option.heading)),
|
||||
filterRegex.test(deaccent(option.label)) || filterRegex.test(deaccent(option.heading || '')),
|
||||
) as unknown as AutocompleteSearchOrganizationEntry[]
|
||||
|
||||
return {
|
||||
return nullableMock<AutocompleteSearchOrganizationQuery>({
|
||||
autocompleteSearchOrganization: filteredOptions.slice(0, input.limit ?? 25),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const wrapperParameters = {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { convertToGraphQLId } from '#shared/graphql/utils.ts'
|
||||
import type { ConfidentTake } from '#shared/types/utils.ts'
|
||||
|
||||
export const defaultOrganization = (): ConfidentTake<OrganizationQuery, 'organization'> =>
|
||||
nullableMock({
|
||||
nullableMock<ConfidentTake<OrganizationQuery, 'organization'>>({
|
||||
__typename: 'Organization',
|
||||
id: convertToGraphQLId('Organization', 100),
|
||||
internalId: 100,
|
||||
|
||||
@@ -4,10 +4,9 @@ import { reactive } from 'vue'
|
||||
|
||||
import type { FormSchemaField } from '#shared/components/Form/types.ts'
|
||||
import { useOrganizationUpdateMutation } from '#shared/entities/organization/graphql/mutations/update.api.ts'
|
||||
import type { EditableOrganization } from '#shared/entities/organization/types.ts'
|
||||
import { defineFormSchema } from '#shared/form/defineFormSchema.ts'
|
||||
import type { OrganizationQuery } from '#shared/graphql/types.ts'
|
||||
import { EnumFormUpdaterId, EnumObjectManagerObjects } from '#shared/graphql/types.ts'
|
||||
import type { ConfidentTake } from '#shared/types/utils.ts'
|
||||
|
||||
import { useDialogObjectForm } from '#mobile/components/CommonDialogObjectForm/useDialogObjectForm.ts'
|
||||
|
||||
@@ -36,9 +35,7 @@ export const useOrganizationEdit = () => {
|
||||
{ showDirtyMark: true },
|
||||
)
|
||||
|
||||
const openEditOrganizationDialog = async (
|
||||
organization: ConfidentTake<OrganizationQuery, 'organization'>,
|
||||
) => {
|
||||
const openEditOrganizationDialog = async (organization: EditableOrganization) => {
|
||||
const formChangeFields = reactive<Record<string, Partial<FormSchemaField>>>({
|
||||
domain: {
|
||||
required: !!organization.domainAssignment,
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as Types from '#shared/graphql/types.ts';
|
||||
import gql from 'graphql-tag';
|
||||
import { TicketAttributesFragmentDoc } from '../../../../../../shared/entities/ticket/graphql/fragments/ticketAttributes.api';
|
||||
import { TicketMentionFragmentDoc } from '../../../../../../shared/entities/ticket/graphql/fragments/ticketMention.api';
|
||||
import { ReferencingTicketFragmentDoc } from '../../../../../../shared/entities/ticket/graphql/fragments/referencingTicket.api';
|
||||
import * as VueApolloComposable from '@vue/apollo-composable';
|
||||
import * as VueCompositionApi from 'vue';
|
||||
export type ReactiveFunction<TParam> = () => TParam;
|
||||
@@ -11,6 +12,7 @@ export const TicketWithMentionLimitDocument = gql`
|
||||
query ticketWithMentionLimit($ticketId: ID!, $mentionsCount: Int = null) {
|
||||
ticket(ticketId: $ticketId) {
|
||||
...ticketAttributes
|
||||
aiSummaryEnabled
|
||||
createArticleType {
|
||||
id
|
||||
name
|
||||
@@ -24,10 +26,21 @@ export const TicketWithMentionLimitDocument = gql`
|
||||
cursor
|
||||
}
|
||||
}
|
||||
checklist {
|
||||
id
|
||||
completed
|
||||
incomplete
|
||||
total
|
||||
complete
|
||||
}
|
||||
referencingChecklistTickets {
|
||||
...referencingTicket
|
||||
}
|
||||
}
|
||||
}
|
||||
${TicketAttributesFragmentDoc}
|
||||
${TicketMentionFragmentDoc}`;
|
||||
${TicketMentionFragmentDoc}
|
||||
${ReferencingTicketFragmentDoc}`;
|
||||
export function useTicketWithMentionLimitQuery(variables: Types.TicketWithMentionLimitQueryVariables | VueCompositionApi.Ref<Types.TicketWithMentionLimitQueryVariables> | ReactiveFunction<Types.TicketWithMentionLimitQueryVariables>, options: VueApolloComposable.UseQueryOptions<Types.TicketWithMentionLimitQuery, Types.TicketWithMentionLimitQueryVariables> | VueCompositionApi.Ref<VueApolloComposable.UseQueryOptions<Types.TicketWithMentionLimitQuery, Types.TicketWithMentionLimitQueryVariables>> | ReactiveFunction<VueApolloComposable.UseQueryOptions<Types.TicketWithMentionLimitQuery, Types.TicketWithMentionLimitQueryVariables>> = {}) {
|
||||
return VueApolloComposable.useQuery<Types.TicketWithMentionLimitQuery, Types.TicketWithMentionLimitQueryVariables>(TicketWithMentionLimitDocument, variables, options);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
query ticketWithMentionLimit($ticketId: ID!, $mentionsCount: Int = null) {
|
||||
ticket(ticketId: $ticketId) {
|
||||
...ticketAttributes
|
||||
aiSummaryEnabled
|
||||
createArticleType {
|
||||
id
|
||||
name
|
||||
@@ -14,5 +15,15 @@ query ticketWithMentionLimit($ticketId: ID!, $mentionsCount: Int = null) {
|
||||
cursor
|
||||
}
|
||||
}
|
||||
checklist {
|
||||
id
|
||||
completed
|
||||
incomplete
|
||||
total
|
||||
complete
|
||||
}
|
||||
referencingChecklistTickets {
|
||||
...referencingTicket
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
import type { FormSchemaField } from '#shared/components/Form/types.ts'
|
||||
import { useUserFormSchema } from '#shared/entities/user/composables/useUserFormSchema.ts'
|
||||
import { useUserUpdateMutation } from '#shared/entities/user/graphql/mutations/update.api.ts'
|
||||
import type { EditableUser } from '#shared/entities/user/types.ts'
|
||||
import { defineFormSchema } from '#shared/form/defineFormSchema.ts'
|
||||
import type { UserQuery } from '#shared/graphql/types.ts'
|
||||
import { EnumFormUpdaterId, EnumObjectManagerObjects } from '#shared/graphql/types.ts'
|
||||
import { useApplicationStore } from '#shared/stores/application.ts'
|
||||
import type { ConfidentTake } from '#shared/types/utils.ts'
|
||||
|
||||
import { useDialogObjectForm } from '#mobile/components/CommonDialogObjectForm/useDialogObjectForm.ts'
|
||||
|
||||
@@ -29,7 +28,7 @@ export const useUserEdit = () => {
|
||||
|
||||
const application = useApplicationStore()
|
||||
|
||||
const openEditUserDialog = async (user: ConfidentTake<UserQuery, 'user'>) => {
|
||||
const openEditUserDialog = async (user: EditableUser) => {
|
||||
const formChangeFields: Record<string, Partial<FormSchemaField>> = {
|
||||
note: {
|
||||
props: {
|
||||
|
||||
@@ -17,6 +17,7 @@ import { useLocaleStore } from '#shared/stores/locale.ts'
|
||||
|
||||
const locales: Record<string, LocalesQuery['locales'][number]> = {
|
||||
de: {
|
||||
__typename: 'Locale',
|
||||
locale: 'de-de',
|
||||
name: 'Deutsch',
|
||||
dir: EnumTextDirection.Ltr,
|
||||
@@ -24,6 +25,7 @@ const locales: Record<string, LocalesQuery['locales'][number]> = {
|
||||
active: true,
|
||||
},
|
||||
ar: {
|
||||
__typename: 'Locale',
|
||||
locale: 'ar',
|
||||
name: 'Arabic',
|
||||
dir: EnumTextDirection.Rtl,
|
||||
|
||||
@@ -13,6 +13,7 @@ import { LoginDocument } from '#shared/graphql/mutations/login.api.ts'
|
||||
import { EnumTwoFactorAuthenticationMethod, type LoginMutation } from '#shared/graphql/types.ts'
|
||||
|
||||
const twoFactorAuthentication = () => ({
|
||||
__typename: 'UserLoginTwoFactorMethods' as const,
|
||||
availableTwoFactorAuthenticationMethods: [EnumTwoFactorAuthenticationMethod.AuthenticatorApp],
|
||||
defaultTwoFactorAuthenticationMethod: EnumTwoFactorAuthenticationMethod.AuthenticatorApp,
|
||||
recoveryCodesAvailable: false,
|
||||
@@ -123,6 +124,7 @@ describe('two factor login flow', () => {
|
||||
describe('show "try another method"', () => {
|
||||
it.each([
|
||||
{
|
||||
__typename: 'UserLoginTwoFactorMethods' as const,
|
||||
availableTwoFactorAuthenticationMethods: [
|
||||
EnumTwoFactorAuthenticationMethod.AuthenticatorApp,
|
||||
],
|
||||
@@ -132,6 +134,7 @@ describe('two factor login flow', () => {
|
||||
available: false,
|
||||
},
|
||||
{
|
||||
__typename: 'UserLoginTwoFactorMethods' as const,
|
||||
availableTwoFactorAuthenticationMethods: [
|
||||
EnumTwoFactorAuthenticationMethod.AuthenticatorApp,
|
||||
],
|
||||
|
||||
@@ -12,12 +12,15 @@ import { TwoFactorMethodInitiateAuthenticationDocument } from '#shared/graphql/m
|
||||
import { ApplicationConfigDocument } from '#shared/graphql/queries/applicationConfig.api.ts'
|
||||
import { ConfigUpdatesDocument } from '#shared/graphql/subscriptions/configUpdates.api.ts'
|
||||
import type { TwoFactorMethodInitiateAuthenticationMutation } from '#shared/graphql/types.ts'
|
||||
import type { DeepPartial } from '#shared/types/utils.ts'
|
||||
import { createDeferred } from '#shared/utils/helpers.ts'
|
||||
|
||||
import LoginTwoFactor from '../LoginTwoFactor.vue'
|
||||
|
||||
const prepareInitialData = (
|
||||
data: TwoFactorMethodInitiateAuthenticationMutation['twoFactorMethodInitiateAuthentication'],
|
||||
data: DeepPartial<
|
||||
TwoFactorMethodInitiateAuthenticationMutation['twoFactorMethodInitiateAuthentication']
|
||||
>,
|
||||
) => {
|
||||
mockGraphQLApi(TwoFactorMethodInitiateAuthenticationDocument).willResolve({
|
||||
twoFactorMethodInitiateAuthentication: {
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
// Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
|
||||
|
||||
import type { SearchQuery } from '#shared/graphql/types.ts'
|
||||
import type { ConfidentTake } from '#shared/types/utils.ts'
|
||||
import type { ConfidentTake, DeepPartial } from '#shared/types/utils.ts'
|
||||
import { mockGraphQLApi } from '#tests/support/mock-graphql-api.ts'
|
||||
import { nullableMock } from '#tests/support/utils.ts'
|
||||
import { SearchDocument } from '../queries/searchOverview.api.ts'
|
||||
|
||||
export const mockSearchOverview = (search: ConfidentTake<SearchQuery, 'search'>) => {
|
||||
return mockGraphQLApi(SearchDocument).willResolve({
|
||||
search,
|
||||
})
|
||||
type SearchResult = ConfidentTake<SearchQuery, 'search'>
|
||||
|
||||
export const mockSearchOverview = (search: DeepPartial<SearchResult>) => {
|
||||
return mockGraphQLApi(SearchDocument).willResolve(
|
||||
nullableMock<SearchQuery>({
|
||||
search: {
|
||||
__typename: 'SearchResult',
|
||||
...search,
|
||||
},
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ export const SearchDocument = gql`
|
||||
search(search: $search, onlyIn: $onlyIn, limit: $limit) {
|
||||
totalCount
|
||||
items {
|
||||
__typename
|
||||
... on Ticket {
|
||||
id
|
||||
internalId
|
||||
|
||||
@@ -2,6 +2,7 @@ query search($search: String!, $onlyIn: EnumSearchableModels!, $limit: Int = 30)
|
||||
search(search: $search, onlyIn: $onlyIn, limit: $limit) {
|
||||
totalCount
|
||||
items {
|
||||
__typename
|
||||
... on Ticket {
|
||||
id
|
||||
internalId
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
type TicketArticlesQuery,
|
||||
} from '#shared/graphql/types.ts'
|
||||
import { convertToGraphQLId } from '#shared/graphql/utils.ts'
|
||||
import type { DeepPartial } from '#shared/types/utils.ts'
|
||||
|
||||
import type { LastArrayElement } from 'type-fest'
|
||||
|
||||
@@ -23,9 +24,9 @@ type ArticleNode = LastArrayElement<TicketArticlesQuery['articles']['edges']>['n
|
||||
|
||||
export const articleContent = (
|
||||
id: number,
|
||||
mockedArticleData: Partial<ArticleNode>,
|
||||
mockedArticleData: DeepPartial<ArticleNode>,
|
||||
): ArticleNode => {
|
||||
return {
|
||||
return nullableMock<ArticleNode>({
|
||||
__typename: 'TicketArticle',
|
||||
id: convertToGraphQLId('TicketArticle', id),
|
||||
internalId: id,
|
||||
@@ -40,8 +41,13 @@ export const articleContent = (
|
||||
firstname: 'John',
|
||||
lastname: 'Doe',
|
||||
fullname: 'John Doe',
|
||||
email: 'john.doe@example.com',
|
||||
active: true,
|
||||
image: null,
|
||||
vip: false,
|
||||
outOfOffice: false,
|
||||
outOfOfficeStartAt: null,
|
||||
outOfOfficeEndAt: null,
|
||||
authorizations: [],
|
||||
},
|
||||
internal: false,
|
||||
@@ -53,28 +59,29 @@ export const articleContent = (
|
||||
type: {
|
||||
__typename: 'TicketArticleType',
|
||||
name: 'article',
|
||||
communication: false,
|
||||
},
|
||||
contentType: 'text/html',
|
||||
attachmentsWithoutInline: [],
|
||||
preferences: {},
|
||||
...mockedArticleData,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const mockArticleQuery = (
|
||||
firstArticles: Partial<ArticleNode>,
|
||||
articles: Partial<ArticleNode>[] = [],
|
||||
firstArticles: DeepPartial<ArticleNode>,
|
||||
articles: DeepPartial<ArticleNode>[] = [],
|
||||
totalCount = articles.length + 1,
|
||||
): TicketArticlesQuery => {
|
||||
const articleNodes = articles.map((article, index) => {
|
||||
return {
|
||||
return nullableMock<TicketArticleEdge>({
|
||||
__typename: 'TicketArticleEdge',
|
||||
node: articleContent(article.internalId ?? index + 1, article),
|
||||
cursor: `MI${index}`,
|
||||
} as TicketArticleEdge
|
||||
})
|
||||
})
|
||||
|
||||
return nullableMock({
|
||||
return nullableMock<TicketArticlesQuery>({
|
||||
firstArticles: {
|
||||
__typename: 'TicketArticleConnection',
|
||||
edges: [
|
||||
|
||||
@@ -140,7 +140,7 @@ const address = {
|
||||
}
|
||||
|
||||
export const defaultArticles = (): TicketArticlesQuery =>
|
||||
nullableMock({
|
||||
nullableMock<TicketArticlesQuery>({
|
||||
firstArticles: {
|
||||
__typename: 'TicketArticleConnection',
|
||||
edges: [
|
||||
|
||||
@@ -340,6 +340,7 @@ describe('calling API to retry encryption', () => {
|
||||
encryptionSuccess: false,
|
||||
signingMessage: 'The certificate for verification could not be found.',
|
||||
signingSuccess: false,
|
||||
type: EnumSecurityStateType.Smime,
|
||||
}
|
||||
|
||||
const { waitUntilTicketLoaded } = mockTicketDetailViewGql({
|
||||
@@ -399,6 +400,7 @@ describe('calling API to retry encryption', () => {
|
||||
encryptionSuccess: false,
|
||||
signingMessage: 'The certificate for verification could not be found.',
|
||||
signingSuccess: false,
|
||||
type: EnumSecurityStateType.Smime,
|
||||
}
|
||||
|
||||
const { waitUntilTicketLoaded } = mockTicketDetailViewGql({
|
||||
@@ -459,9 +461,12 @@ describe('remote content removal', () => {
|
||||
}
|
||||
article.attachmentsWithoutInline = [
|
||||
{
|
||||
__typename: 'StoredFile',
|
||||
id: convertToGraphQLId('Store', 1),
|
||||
internalId: 1,
|
||||
name: 'message',
|
||||
size: null,
|
||||
type: null,
|
||||
preferences: {
|
||||
'original-format': true,
|
||||
},
|
||||
@@ -951,6 +956,7 @@ it('correctly redirects from ticket hash-based routes with other ids', async ()
|
||||
defaultArticles(),
|
||||
{
|
||||
articles: {
|
||||
__typename: 'TicketArticleConnection',
|
||||
edges: [],
|
||||
pageInfo: {
|
||||
endCursor: null,
|
||||
|
||||
@@ -101,8 +101,10 @@ describe('static organization', () => {
|
||||
...organization,
|
||||
allMembers: {
|
||||
...organization.allMembers,
|
||||
__typename: 'UserConnection',
|
||||
edges: organization.allMembers?.edges || [],
|
||||
pageInfo: {
|
||||
__typename: 'PageInfo',
|
||||
endCursor: null,
|
||||
},
|
||||
totalCount: 2,
|
||||
|
||||
@@ -8,7 +8,10 @@ import { mockPermissions } from '#tests/support/mock-permissions.ts'
|
||||
import { nullableMock, waitUntil } from '#tests/support/utils.ts'
|
||||
|
||||
import { AutocompleteSearchUserDocument } from '#shared/components/Form/fields/FieldCustomer/graphql/queries/autocompleteSearch/user.api.ts'
|
||||
import type { AutocompleteSearchUserQuery } from '#shared/graphql/types.ts'
|
||||
import type {
|
||||
AutocompleteSearchUserEntry,
|
||||
AutocompleteSearchUserQuery,
|
||||
} from '#shared/graphql/types.ts'
|
||||
import { MutationHandler } from '#shared/server/apollo/handler/index.ts'
|
||||
|
||||
import { mockTicketObjectAttributesGql } from '#mobile/entities/ticket/__tests__/mocks/ticket-mocks.ts'
|
||||
@@ -33,39 +36,40 @@ const mockUpdateMutationHandler = () => {
|
||||
|
||||
const defaultAutoCompleteSearchUserResult = (
|
||||
hasSecondaryOrganizations = false,
|
||||
): AutocompleteSearchUserQuery => ({
|
||||
autocompleteSearchUser: [
|
||||
nullableMock({
|
||||
value: 2,
|
||||
label: 'Nicole Braun',
|
||||
labelPlaceholder: null,
|
||||
heading: 'Zammad Foundation',
|
||||
headingPlaceholder: null,
|
||||
disabled: null,
|
||||
icon: null,
|
||||
user: {
|
||||
id: 'gid://zammad/User/2',
|
||||
internalId: 2,
|
||||
firstname: 'Nicole',
|
||||
lastname: 'Braun',
|
||||
fullname: 'Nicole Braun',
|
||||
image: null,
|
||||
objectAttributeValues: [],
|
||||
organization: {
|
||||
id: 'gid://zammad/Organization/1',
|
||||
internalId: 1,
|
||||
name: 'Zammad Foundation',
|
||||
active: true,
|
||||
): AutocompleteSearchUserQuery =>
|
||||
nullableMock<AutocompleteSearchUserQuery>({
|
||||
autocompleteSearchUser: [
|
||||
nullableMock<AutocompleteSearchUserEntry>({
|
||||
value: 2,
|
||||
label: 'Nicole Braun',
|
||||
labelPlaceholder: null,
|
||||
heading: 'Zammad Foundation',
|
||||
headingPlaceholder: null,
|
||||
disabled: null,
|
||||
icon: null,
|
||||
user: {
|
||||
id: 'gid://zammad/User/2',
|
||||
internalId: 2,
|
||||
firstname: 'Nicole',
|
||||
lastname: 'Braun',
|
||||
fullname: 'Nicole Braun',
|
||||
image: null,
|
||||
objectAttributeValues: [],
|
||||
__typename: 'Organization',
|
||||
organization: {
|
||||
id: 'gid://zammad/Organization/1',
|
||||
internalId: 1,
|
||||
name: 'Zammad Foundation',
|
||||
active: true,
|
||||
objectAttributeValues: [],
|
||||
__typename: 'Organization',
|
||||
},
|
||||
hasSecondaryOrganizations,
|
||||
__typename: 'User',
|
||||
},
|
||||
hasSecondaryOrganizations,
|
||||
__typename: 'User',
|
||||
},
|
||||
__typename: 'AutocompleteSearchUserEntry',
|
||||
}),
|
||||
],
|
||||
})
|
||||
__typename: 'AutocompleteSearchUserEntry',
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
const mockCustomerQueryResult = (autoCompleteSearchUserResult?: AutocompleteSearchUserQuery[]) => {
|
||||
return mockGraphQLApi(AutocompleteSearchUserDocument).willResolve(
|
||||
@@ -149,9 +153,9 @@ describe('TicketAction - change customer dialog', () => {
|
||||
test('show organization field for customers with multiple organization (and also hide again)', async () => {
|
||||
const mockCustomer = mockCustomerQueryResult([
|
||||
defaultAutoCompleteSearchUserResult(true),
|
||||
{
|
||||
nullableMock<AutocompleteSearchUserQuery>({
|
||||
autocompleteSearchUser: [
|
||||
nullableMock({
|
||||
nullableMock<AutocompleteSearchUserEntry>({
|
||||
value: 200,
|
||||
label: 'John Doe',
|
||||
labelPlaceholder: null,
|
||||
@@ -181,7 +185,7 @@ describe('TicketAction - change customer dialog', () => {
|
||||
__typename: 'AutocompleteSearchUserEntry',
|
||||
}),
|
||||
],
|
||||
},
|
||||
}),
|
||||
])
|
||||
|
||||
const view = renderComponent(TicketActionChangeCustomerDialog, {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
|
||||
|
||||
import { renderComponent } from '#tests/support/components/index.ts'
|
||||
import { nullableMock } from '#tests/support/utils.ts'
|
||||
|
||||
import type { TicketArticle } from '#shared/entities/ticket/types.ts'
|
||||
import { convertToGraphQLId } from '#shared/graphql/utils.ts'
|
||||
@@ -13,7 +14,7 @@ import ArticlesList from '../ArticlesList.vue'
|
||||
it('renders delivery messages', () => {
|
||||
const { ticket } = defaultTicket()
|
||||
const articles: TicketArticle[] = [
|
||||
{
|
||||
nullableMock<TicketArticle>({
|
||||
__typename: 'TicketArticle',
|
||||
id: convertToGraphQLId('TicketArticle', 1),
|
||||
internal: false,
|
||||
@@ -30,7 +31,7 @@ it('renders delivery messages', () => {
|
||||
__typename: 'User',
|
||||
id: convertToGraphQLId('User', 1),
|
||||
},
|
||||
},
|
||||
}),
|
||||
]
|
||||
|
||||
const view = renderComponent(ArticlesList, {
|
||||
|
||||
@@ -5,10 +5,12 @@ import { getAllByRole, waitFor } from '@testing-library/vue'
|
||||
import { getByIconName } from '#tests/support/components/iconQueries.ts'
|
||||
import { renderComponent } from '#tests/support/components/index.ts'
|
||||
import { mockGraphQLApi } from '#tests/support/mock-graphql-api.ts'
|
||||
import { nullableMock } from '#tests/support/utils.ts'
|
||||
|
||||
import { ObjectManagerFrontendAttributesDocument } from '#shared/entities/object-attributes/graphql/queries/objectManagerFrontendAttributes.api.ts'
|
||||
import type { TicketArticle } from '#shared/entities/ticket/types.ts'
|
||||
import { EnumSecurityStateType, type TicketArticleSecurityState } from '#shared/graphql/types.ts'
|
||||
import { EnumSecurityStateType } from '#shared/graphql/types.ts'
|
||||
import type { DeepPartial } from '#shared/types/utils.ts'
|
||||
|
||||
import { ticketArticleObjectAttributes } from '#mobile/entities/ticket/__tests__/mocks/ticket-mocks.ts'
|
||||
import { defaultArticles } from '#mobile/pages/ticket/__tests__/mocks/detail-view.ts'
|
||||
@@ -17,6 +19,7 @@ import ArticleMetadataDialog from '../ArticleMetadataDialog.vue'
|
||||
|
||||
// parsed is tested in unit test
|
||||
const getAddress = (raw: string) => ({
|
||||
__typename: 'AddressesField' as const,
|
||||
raw,
|
||||
parsed: null,
|
||||
})
|
||||
@@ -43,7 +46,9 @@ describe('visuals for metadata', () => {
|
||||
cc: getAddress('Joe Mike <joe.mike@zammad.org>'),
|
||||
replyTo: getAddress('Arthur Miller <arthur.miller@zammad.org>'),
|
||||
type: {
|
||||
__typename: 'TicketArticleType',
|
||||
name: 'email',
|
||||
communication: true,
|
||||
},
|
||||
detectedLanguage: 'de',
|
||||
createdAt,
|
||||
@@ -114,14 +119,17 @@ describe('visuals for metadata', () => {
|
||||
})
|
||||
|
||||
describe('rendering security field', () => {
|
||||
const mockArticle = (security: TicketArticleSecurityState): TicketArticle => ({
|
||||
...defaultArticles().firstArticles!.edges[0].node,
|
||||
internalId: 1,
|
||||
securityState: {
|
||||
__typename: 'TicketArticleSecurityState',
|
||||
...security,
|
||||
},
|
||||
})
|
||||
const mockArticle = (
|
||||
security: DeepPartial<NonNullable<TicketArticle['securityState']>>,
|
||||
): TicketArticle =>
|
||||
nullableMock<TicketArticle>({
|
||||
...defaultArticles().firstArticles!.edges[0].node,
|
||||
internalId: 1,
|
||||
securityState: {
|
||||
__typename: 'TicketArticleSecurityState',
|
||||
...security,
|
||||
},
|
||||
})
|
||||
|
||||
describe('renders type', () => {
|
||||
it('renders S/MIME type when provided', () => {
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
type ObjectManagerFrontendAttributesPayload,
|
||||
type FormUpdaterQuery,
|
||||
} from '#shared/graphql/types.ts'
|
||||
import type { DeepPartial } from '#shared/types/utils.ts'
|
||||
|
||||
import { FormUpdaterDocument } from '../../../graphql/queries/formUpdater.api.ts'
|
||||
import additionalFrontendObjectAttributes from '../../mocks/additionalFrontendObjectAttributes.json'
|
||||
@@ -207,7 +208,7 @@ const mergedObjectAttributes = mergeFrontendObjectAttributes(
|
||||
)
|
||||
|
||||
const renderForm = async (
|
||||
formUpdaterQueryResponse: FormUpdaterQuery | FormUpdaterQuery[],
|
||||
formUpdaterQueryResponse: DeepPartial<FormUpdaterQuery> | DeepPartial<FormUpdaterQuery>[],
|
||||
options: ExtendedMountingOptions<Props> = {},
|
||||
objectManagerFrontendAttributes = mergedObjectAttributes,
|
||||
) => {
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
type ObjectManagerFrontendAttributesPayload,
|
||||
type FormUpdaterQuery,
|
||||
} from '#shared/graphql/types.ts'
|
||||
import type { DeepPartial } from '#shared/types/utils.ts'
|
||||
|
||||
import { FormUpdaterDocument } from '../../../graphql/queries/formUpdater.api.ts'
|
||||
import additionalFrontendObjectAttributes from '../../mocks/additionalFrontendObjectAttributes.json'
|
||||
@@ -205,7 +206,7 @@ const mergedObjectAttributes = mergeFrontendObjectAttributes(
|
||||
)
|
||||
|
||||
const renderForm = async (
|
||||
formUpdaterQueryResponse: FormUpdaterQuery | FormUpdaterQuery[],
|
||||
formUpdaterQueryResponse: DeepPartial<FormUpdaterQuery> | DeepPartial<FormUpdaterQuery>[],
|
||||
options: ExtendedMountingOptions<Props> = {},
|
||||
objectManagerFrontendAttributes = mergedObjectAttributes,
|
||||
) => {
|
||||
|
||||
@@ -15,6 +15,7 @@ export const AutocompleteSearchGenericDocument = gql`
|
||||
headingPlaceholder
|
||||
disabled
|
||||
object {
|
||||
__typename
|
||||
... on User {
|
||||
id
|
||||
internalId
|
||||
@@ -31,6 +32,7 @@ export const AutocompleteSearchGenericDocument = gql`
|
||||
active
|
||||
vip
|
||||
organization {
|
||||
__typename
|
||||
id
|
||||
internalId
|
||||
name
|
||||
@@ -52,6 +54,7 @@ export const AutocompleteSearchGenericDocument = gql`
|
||||
allMembers(first: $membersCount) {
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
id
|
||||
internalId
|
||||
login
|
||||
|
||||
@@ -7,6 +7,7 @@ query autocompleteSearchGeneric($input: AutocompleteSearchGenericInput!, $member
|
||||
headingPlaceholder
|
||||
disabled
|
||||
object {
|
||||
__typename
|
||||
... on User {
|
||||
id
|
||||
internalId
|
||||
@@ -23,6 +24,7 @@ query autocompleteSearchGeneric($input: AutocompleteSearchGenericInput!, $member
|
||||
active
|
||||
vip
|
||||
organization {
|
||||
__typename
|
||||
id
|
||||
internalId
|
||||
name
|
||||
@@ -44,6 +46,7 @@ query autocompleteSearchGeneric($input: AutocompleteSearchGenericInput!, $member
|
||||
allMembers(first: $membersCount) {
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
id
|
||||
internalId
|
||||
login
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
import { flushPromises } from '@vue/test-utils'
|
||||
|
||||
import { initializePiniaStore } from '#tests/support/components/renderComponent.ts'
|
||||
import { nullableMock } from '#tests/support/utils.ts'
|
||||
|
||||
import type { Locale } from '#shared/graphql/types.ts'
|
||||
import type { LocalesQuery } from '#shared/graphql/types.ts'
|
||||
import { useLocaleStore } from '#shared/stores/locale.ts'
|
||||
|
||||
import { useDateFnsLocale, importDateFnsLocale } from '../useDateFnsLocale.ts'
|
||||
@@ -33,9 +34,10 @@ describe('useDateFnsLocale', () => {
|
||||
it('returns the correct date-fns locale based on the current locale store value', async () => {
|
||||
const locale = useLocaleStore()
|
||||
|
||||
locale.localeData = {
|
||||
locale.localeData = nullableMock<LocalesQuery['locales'][number]>({
|
||||
__typename: 'Locale',
|
||||
locale: 'en-gb',
|
||||
} as Locale
|
||||
})
|
||||
|
||||
const { dateFnsLocale } = useDateFnsLocale()
|
||||
|
||||
@@ -47,9 +49,10 @@ describe('useDateFnsLocale', () => {
|
||||
it('returns the fallback en-US locale if the current locale is unsupported', async () => {
|
||||
const locale = useLocaleStore()
|
||||
|
||||
locale.localeData = {
|
||||
locale.localeData = nullableMock<LocalesQuery['locales'][number]>({
|
||||
__typename: 'Locale',
|
||||
locale: 'xx-xx',
|
||||
} as Locale
|
||||
})
|
||||
|
||||
const { dateFnsLocale } = useDateFnsLocale()
|
||||
|
||||
|
||||
@@ -278,8 +278,10 @@ describe('Fields - FieldFile', () => {
|
||||
const file = new File([], 'foo.png', { type: 'image/png' })
|
||||
const uploadedFileQuery: FormUploadCacheAddMutation = {
|
||||
formUploadCacheAdd: {
|
||||
__typename: 'FormUploadCacheAddPayload',
|
||||
uploadedFiles: [
|
||||
{
|
||||
__typename: 'StoredFile',
|
||||
id: '1',
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { nullableMock } from '#tests/support/utils.ts'
|
||||
|
||||
import type { OnlineNotificationsQuery, Scalars } from '#shared/graphql/types.ts'
|
||||
import type { DeepPartial } from '#shared/types/utils.ts'
|
||||
|
||||
import type { LastArrayElement } from 'type-fest'
|
||||
|
||||
@@ -12,13 +13,14 @@ type OnlineNotificationNode = LastArrayElement<
|
||||
|
||||
export const mockOnlineNotification = (
|
||||
id: Scalars['ID']['output'],
|
||||
mockNotification: Partial<OnlineNotificationNode>,
|
||||
mockNotification: DeepPartial<OnlineNotificationNode>,
|
||||
): OnlineNotificationNode => {
|
||||
return {
|
||||
return nullableMock<OnlineNotificationNode>({
|
||||
__typename: 'OnlineNotification',
|
||||
seen: false,
|
||||
createdAt: new Date().toISOString(),
|
||||
createdBy: {
|
||||
__typename: 'User',
|
||||
id: '123',
|
||||
fullname: 'Full Name',
|
||||
lastname: 'Name',
|
||||
@@ -26,6 +28,8 @@ export const mockOnlineNotification = (
|
||||
email: 'email@example.org',
|
||||
vip: false,
|
||||
outOfOffice: false,
|
||||
outOfOfficeStartAt: null,
|
||||
outOfOfficeEndAt: null,
|
||||
active: true,
|
||||
image: null,
|
||||
},
|
||||
@@ -43,11 +47,11 @@ export const mockOnlineNotification = (
|
||||
},
|
||||
id,
|
||||
...mockNotification,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const mockOnlineNotificationQuery = (
|
||||
mockNotifications: Array<Partial<OnlineNotificationNode>>,
|
||||
mockNotifications: Array<DeepPartial<OnlineNotificationNode>>,
|
||||
): OnlineNotificationsQuery => {
|
||||
const edges = mockNotifications.map((item, index) => {
|
||||
const id = index + 1
|
||||
|
||||
@@ -32,6 +32,7 @@ export const OnlineNotificationsDocument = gql`
|
||||
createdByAi
|
||||
}
|
||||
metaObject {
|
||||
__typename
|
||||
... on Ticket {
|
||||
id
|
||||
internalId
|
||||
@@ -55,6 +56,7 @@ export const OnlineNotificationsDocument = gql`
|
||||
id
|
||||
internalId
|
||||
data {
|
||||
__typename
|
||||
... on OnlineNotificationStandaloneBulkJobData {
|
||||
total
|
||||
failedCount
|
||||
|
||||
@@ -24,6 +24,7 @@ query onlineNotifications {
|
||||
createdByAi
|
||||
}
|
||||
metaObject {
|
||||
__typename
|
||||
... on Ticket {
|
||||
id
|
||||
internalId
|
||||
@@ -47,6 +48,7 @@ query onlineNotifications {
|
||||
id
|
||||
internalId
|
||||
data {
|
||||
__typename
|
||||
... on OnlineNotificationStandaloneBulkJobData {
|
||||
total
|
||||
failedCount
|
||||
|
||||
8
app/frontend/shared/entities/organization/types.ts
Normal file
8
app/frontend/shared/entities/organization/types.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
|
||||
|
||||
import type { ObjectLike } from '#shared/types/utils.ts'
|
||||
|
||||
export interface EditableOrganization extends ObjectLike {
|
||||
id: string
|
||||
domainAssignment?: boolean | null
|
||||
}
|
||||
@@ -4,28 +4,34 @@ import { nullableMock } from '#tests/support/utils.ts'
|
||||
|
||||
import { EnumTicketArticleSenderName, type TicketArticlesQuery } from '#shared/graphql/types.ts'
|
||||
import { convertToGraphQLId } from '#shared/graphql/utils.ts'
|
||||
import type { DeepPartial } from '#shared/types/utils.ts'
|
||||
|
||||
import type { LastArrayElement } from 'type-fest'
|
||||
|
||||
export const mockTicketCreateAtDate = new Date(2011, 11, 11, 11, 11, 11, 11)
|
||||
|
||||
export const defaultAuthor = {
|
||||
__typename: 'User',
|
||||
__typename: 'User' as const,
|
||||
id: '11',
|
||||
firstname: 'Author',
|
||||
lastname: 'Joe',
|
||||
fullname: 'Author Joe',
|
||||
email: 'author.joe@example.com',
|
||||
active: true,
|
||||
image: null,
|
||||
vip: false,
|
||||
outOfOffice: false,
|
||||
outOfOfficeStartAt: null,
|
||||
outOfOfficeEndAt: null,
|
||||
authorizations: [],
|
||||
}
|
||||
|
||||
export const defaultFromAddress = {
|
||||
__typename: 'AddressesField',
|
||||
__typename: 'AddressesField' as const,
|
||||
raw: 'Nicole Braun <nicole.braun@zammad.org>',
|
||||
parsed: [
|
||||
{
|
||||
__typename: 'EmailAddressParsed',
|
||||
__typename: 'EmailAddressParsed' as const,
|
||||
name: 'Nicole Braun',
|
||||
emailAddress: 'nicole.braun@zammad.org',
|
||||
isSystemAddress: false,
|
||||
@@ -36,27 +42,28 @@ export const defaultFromAddress = {
|
||||
export const defaultBodyWithUrls = '<p>Default test body</p>'
|
||||
|
||||
type ArticleNode = LastArrayElement<TicketArticlesQuery['articles']['edges']>['node']
|
||||
type ArticleNodeOptions = DeepPartial<ArticleNode>
|
||||
|
||||
export const createDummyArticle = (options?: {
|
||||
articleId?: number
|
||||
from?: ArticleNode['from']
|
||||
author?: ArticleNode['author']
|
||||
internal?: ArticleNode['internal']
|
||||
bodyWithUrls?: ArticleNode['bodyWithUrls']
|
||||
to?: ArticleNode['to']
|
||||
cc?: ArticleNode['cc']
|
||||
replyTo?: ArticleNode['replyTo']
|
||||
subject?: ArticleNode['subject']
|
||||
from?: ArticleNodeOptions['from']
|
||||
author?: ArticleNodeOptions['author']
|
||||
internal?: ArticleNodeOptions['internal']
|
||||
bodyWithUrls?: ArticleNodeOptions['bodyWithUrls']
|
||||
to?: ArticleNodeOptions['to']
|
||||
cc?: ArticleNodeOptions['cc']
|
||||
replyTo?: ArticleNodeOptions['replyTo']
|
||||
subject?: ArticleNodeOptions['subject']
|
||||
articleType?: string
|
||||
attachmentsWithoutInline?: ArticleNode['attachmentsWithoutInline']
|
||||
contentType?: ArticleNode['contentType']
|
||||
securityState?: ArticleNode['securityState']
|
||||
attachmentsWithoutInline?: ArticleNodeOptions['attachmentsWithoutInline']
|
||||
contentType?: ArticleNodeOptions['contentType']
|
||||
securityState?: ArticleNodeOptions['securityState']
|
||||
senderName?: EnumTicketArticleSenderName
|
||||
mediaErrorState?: ArticleNode['mediaErrorState']
|
||||
preferences?: ArticleNode['preferences']
|
||||
detectedLanguage?: ArticleNode['detectedLanguage']
|
||||
mediaErrorState?: ArticleNodeOptions['mediaErrorState']
|
||||
preferences?: ArticleNodeOptions['preferences']
|
||||
detectedLanguage?: ArticleNodeOptions['detectedLanguage']
|
||||
}) => {
|
||||
return nullableMock({
|
||||
return nullableMock<ArticleNode>({
|
||||
__typename: 'TicketArticle',
|
||||
id: convertToGraphQLId('TicketArticle', options?.articleId || 1),
|
||||
internalId: options?.articleId || 1,
|
||||
@@ -68,7 +75,6 @@ export const createDummyArticle = (options?: {
|
||||
replyTo: options?.replyTo || null,
|
||||
messageIdMd5: null,
|
||||
contentType: options?.contentType || 'text/plain',
|
||||
references: null,
|
||||
attachmentsWithoutInline: options?.attachmentsWithoutInline || [],
|
||||
preferences: options?.preferences || {},
|
||||
bodyWithUrls: options?.bodyWithUrls || defaultBodyWithUrls,
|
||||
@@ -78,6 +84,7 @@ export const createDummyArticle = (options?: {
|
||||
type: {
|
||||
__typename: 'TicketArticleType',
|
||||
name: options?.articleType || 'string',
|
||||
communication: false,
|
||||
},
|
||||
sender: {
|
||||
__typename: 'TicketArticleSender',
|
||||
@@ -86,5 +93,5 @@ export const createDummyArticle = (options?: {
|
||||
securityState: options?.securityState === undefined ? null : options.securityState,
|
||||
mediaErrorState: options?.mediaErrorState === undefined ? null : options.mediaErrorState,
|
||||
detectedLanguage: options?.detectedLanguage ?? null,
|
||||
}) as ArticleNode
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { nullableMock } from '#tests/support/utils.ts'
|
||||
import type { TicketQuery } from '#shared/graphql/types.ts'
|
||||
import { EnumTicketStateColorCode, EnumTicketSummaryGeneration } from '#shared/graphql/types.ts'
|
||||
import { convertToGraphQLId } from '#shared/graphql/utils.ts'
|
||||
import type { DeepPartial } from '#shared/types/utils.ts'
|
||||
|
||||
export const mockTicketCreateDate = new Date(2011, 11, 11, 11, 11, 11, 11)
|
||||
export const mockTicketUpdateDate = new Date(2011, 12, 12, 12, 12, 12, 12)
|
||||
@@ -89,7 +90,7 @@ export const defaultPolicy = {
|
||||
}
|
||||
|
||||
export const defaultMentions = {
|
||||
__typename: 'Mentions',
|
||||
__typename: 'MentionConnection',
|
||||
totalCount: 0,
|
||||
edges: [],
|
||||
}
|
||||
@@ -100,25 +101,25 @@ export const defaultMentions = {
|
||||
* * */
|
||||
export const createDummyTicket = <R = TicketQuery['ticket']>(options?: {
|
||||
ticketId?: string
|
||||
owner?: TicketQuery['ticket']['owner']
|
||||
customer?: TicketQuery['ticket']['customer']
|
||||
organization?: TicketQuery['ticket']['organization']
|
||||
state?: TicketQuery['ticket']['state']
|
||||
owner?: DeepPartial<TicketQuery['ticket']['owner']>
|
||||
customer?: DeepPartial<TicketQuery['ticket']['customer']>
|
||||
organization?: DeepPartial<TicketQuery['ticket']['organization']>
|
||||
state?: DeepPartial<TicketQuery['ticket']['state']>
|
||||
articleType?: string
|
||||
group?: Partial<TicketQuery['ticket']['group']>
|
||||
defaultPriority?: TicketQuery['ticket']['priority']
|
||||
defaultPolicy?: TicketQuery['ticket']['policy']
|
||||
mentions?: TicketQuery['ticket']['mentions']
|
||||
group?: DeepPartial<TicketQuery['ticket']['group']>
|
||||
defaultPriority?: DeepPartial<TicketQuery['ticket']['priority']>
|
||||
defaultPolicy?: DeepPartial<TicketQuery['ticket']['policy']>
|
||||
mentions?: DeepPartial<TicketQuery['ticket']['mentions']>
|
||||
subscribed?: TicketQuery['ticket']['subscribed']
|
||||
colorCode?: EnumTicketStateColorCode
|
||||
title?: TicketQuery['ticket']['title']
|
||||
number?: TicketQuery['ticket']['number']
|
||||
checklist?: TicketQuery['ticket']['checklist']
|
||||
referencingChecklistTickets?: TicketQuery['ticket']['referencingChecklistTickets']
|
||||
checklist?: DeepPartial<TicketQuery['ticket']['checklist']>
|
||||
referencingChecklistTickets?: DeepPartial<TicketQuery['ticket']['referencingChecklistTickets']>
|
||||
timeUnit?: TicketQuery['ticket']['timeUnit']
|
||||
timeUnitsPerType?: TicketQuery['ticket']['timeUnitsPerType']
|
||||
timeUnitsPerType?: DeepPartial<TicketQuery['ticket']['timeUnitsPerType']>
|
||||
tags?: string[]
|
||||
externalReferences?: TicketQuery['ticket']['externalReferences']
|
||||
externalReferences?: DeepPartial<TicketQuery['ticket']['externalReferences']>
|
||||
preferences?: TicketQuery['ticket']['preferences']
|
||||
sharedDraftZoomId?: number
|
||||
aiAgentRunning?: TicketQuery['ticket']['aiAgentRunning']
|
||||
@@ -127,7 +128,7 @@ export const createDummyTicket = <R = TicketQuery['ticket']>(options?: {
|
||||
firstResponseEscalationAt?: TicketQuery['ticket']['firstResponseEscalationAt']
|
||||
updateEscalationAt?: TicketQuery['ticket']['updateEscalationAt']
|
||||
closeEscalationAt?: TicketQuery['ticket']['closeEscalationAt']
|
||||
objectAttributeValues?: TicketQuery['ticket']['objectAttributeValues']
|
||||
objectAttributeValues?: DeepPartial<TicketQuery['ticket']['objectAttributeValues']>
|
||||
}): R => {
|
||||
return nullableMock({
|
||||
__typename: 'Ticket',
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { mockApplicationConfig } from '#tests/support/mock-applicationConfig.ts'
|
||||
import { setupView } from '#tests/support/mock-user.ts'
|
||||
|
||||
import { type TicketView } from '#shared/entities/ticket/types.ts'
|
||||
import { type TicketArticle, type TicketView } from '#shared/entities/ticket/types.ts'
|
||||
|
||||
import { createTicketArticle, createTestArticleActions, createTicket } from './utils.ts'
|
||||
|
||||
@@ -51,7 +51,7 @@ describe('article permanent link action', () => {
|
||||
|
||||
const articlePermalink = actions.find((action) => action.name === 'article-permalink')
|
||||
|
||||
articlePermalink?.perform?.(ticket, article, {} as TicketArticlePerformOptions)
|
||||
articlePermalink?.perform?.(ticket, article as TicketArticle, {} as TicketArticlePerformOptions)
|
||||
|
||||
expect(copyToClipboardMock).toHaveBeenCalledWith(
|
||||
`https://example.com/desktop/tickets/${ticket.internalId}/${article.internalId}`,
|
||||
|
||||
@@ -1,23 +1,31 @@
|
||||
// Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
|
||||
|
||||
import { generateObjectData } from '#tests/graphql/builders/index.ts'
|
||||
import { nullableMock } from '#tests/support/utils.ts'
|
||||
|
||||
import type { TicketArticle } from '#shared/graphql/types.ts'
|
||||
import type { TicketArticle } from '#shared/entities/ticket/types.ts'
|
||||
import type { TicketArticleEmailForwardReplyMutation } from '#shared/graphql/types.ts'
|
||||
import type { ConfidentTake } from '#shared/types/utils.ts'
|
||||
|
||||
import { buildEmailForwardHeader } from '../email/forward.ts'
|
||||
|
||||
type ForwardReplyPayload = ConfidentTake<
|
||||
TicketArticleEmailForwardReplyMutation,
|
||||
'ticketArticleEmailForwardReply'
|
||||
>
|
||||
|
||||
describe('building header, when "forward" action is called', () => {
|
||||
it('renders all fields', () => {
|
||||
const article = generateObjectData<TicketArticle>('TicketArticle')
|
||||
article.subject = 'Article Subject'
|
||||
article.createdAt = new Date(2020, 1, 1).toISOString()
|
||||
|
||||
const meta = {
|
||||
const meta = nullableMock<ForwardReplyPayload>({
|
||||
quotableFrom: 'Jhon Doe <jhon.doe@email.dcom>',
|
||||
quotableCc: 'Agent Rodrigez',
|
||||
quotableTo: 'Agent Smith <smith.a@matrix.com>',
|
||||
attachments: [],
|
||||
}
|
||||
})
|
||||
|
||||
expect(buildEmailForwardHeader(article, meta)).toBe(
|
||||
'<p>Subject: Article Subject<br>Date: 2020-02-01 00:00<br>From: Jhon Doe <jhon.doe@email.dcom><br>To: Agent Smith <smith.a@matrix.com><br>CC: Agent Rodrigez<br><br></p>',
|
||||
@@ -29,12 +37,12 @@ describe('building header, when "forward" action is called', () => {
|
||||
article.subject = null
|
||||
article.createdAt = new Date(2020, 1, 1).toISOString()
|
||||
|
||||
const meta = {
|
||||
const meta = nullableMock<ForwardReplyPayload>({
|
||||
quotableFrom: 'Jhon Doe <jhon.doe@email.dcom>',
|
||||
quotableCc: null,
|
||||
quotableTo: 'Agent Smith <smith.a@matrix.com>',
|
||||
attachments: [],
|
||||
}
|
||||
})
|
||||
|
||||
expect(buildEmailForwardHeader(article, meta)).toBe(
|
||||
'<p>Date: 2020-02-01 00:00<br>From: Jhon Doe <jhon.doe@email.dcom><br>To: Agent Smith <smith.a@matrix.com><br><br></p>',
|
||||
|
||||
@@ -4,11 +4,7 @@ import { generateObjectData } from '#tests/graphql/builders/index.ts'
|
||||
import { initializeStore } from '#tests/support/components/initializeStore.ts'
|
||||
|
||||
import type { TicketArticle, TicketById } from '#shared/entities/ticket/types.ts'
|
||||
import {
|
||||
EnumTicketArticleSenderName,
|
||||
type PolicyTicket,
|
||||
type Ticket,
|
||||
} from '#shared/graphql/types.ts'
|
||||
import { EnumTicketArticleSenderName, type PolicyTicket } from '#shared/graphql/types.ts'
|
||||
import type { AppName } from '#shared/types/app.ts'
|
||||
import type { DeepPartial } from '#shared/types/utils.ts'
|
||||
|
||||
@@ -16,8 +12,10 @@ import { createArticleActions, createArticleTypes } from '../index.ts'
|
||||
|
||||
import type { TicketActionAddOptions } from '../types.ts'
|
||||
|
||||
export const createTicketArticle = (defaults?: DeepPartial<TicketArticle>) => {
|
||||
return generateObjectData<TicketArticle>('TicketArticle', defaults)
|
||||
type TestTicketArticle = DeepPartial<TicketArticle> & Pick<TicketArticle, 'id' | 'internalId'>
|
||||
|
||||
export const createTicketArticle = (defaults?: DeepPartial<TicketArticle>): TestTicketArticle => {
|
||||
return generateObjectData<TicketArticle>('TicketArticle', defaults) as TestTicketArticle
|
||||
}
|
||||
|
||||
const defaultOptions: Pick<TicketActionAddOptions, 'onDispose' | 'recalculate'> = {
|
||||
@@ -25,8 +23,8 @@ const defaultOptions: Pick<TicketActionAddOptions, 'onDispose' | 'recalculate'>
|
||||
onDispose: vi.fn(),
|
||||
}
|
||||
|
||||
export const createTicket = (defaults?: DeepPartial<Ticket>) =>
|
||||
generateObjectData<Ticket>('Ticket', defaults)
|
||||
export const createTicket = (defaults?: DeepPartial<TicketById>): TicketById =>
|
||||
generateObjectData<TicketById>('Ticket', defaults)
|
||||
|
||||
export const createEligibleTicketArticleReplyData = (
|
||||
type: string,
|
||||
@@ -48,14 +46,14 @@ export const createEligibleTicketArticleReplyData = (
|
||||
|
||||
export const createTestArticleActions = (
|
||||
ticket: TicketById,
|
||||
article: TicketArticle,
|
||||
article: TestTicketArticle,
|
||||
app: AppName = 'mobile',
|
||||
) => {
|
||||
initializeStore()
|
||||
return createArticleActions(ticket, article, app, defaultOptions)
|
||||
return createArticleActions(ticket, article as TicketArticle, app, defaultOptions)
|
||||
}
|
||||
|
||||
export const createTestArticleTypes = (ticket: TicketById, app: AppName = 'mobile') => {
|
||||
initializeStore()
|
||||
return createArticleTypes(ticket, app)
|
||||
return createArticleTypes(ticket as TicketById, app)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// PolicyTicket,
|
||||
import { generateObjectData } from '#tests/graphql/builders/index.ts'
|
||||
|
||||
import type { Ticket } from '#shared/graphql/types.ts'
|
||||
import type { TicketById } from '#shared/entities/ticket/types.ts'
|
||||
// import type { AppName } from '#shared/types/app.ts'
|
||||
import type { DeepPartial } from '#shared/types/utils.ts'
|
||||
|
||||
@@ -27,8 +27,8 @@ import type { DeepPartial } from '#shared/types/utils.ts'
|
||||
// onDispose: vi.fn(),
|
||||
// }
|
||||
|
||||
export const createTicket = (defaults?: DeepPartial<Ticket>) =>
|
||||
generateObjectData<Ticket>('Ticket', defaults)
|
||||
export const createTicket = (defaults?: DeepPartial<TicketById>): TicketById =>
|
||||
generateObjectData<TicketById>('Ticket', defaults)
|
||||
|
||||
// export const createEligibleTicketArticleReplyData = (
|
||||
// type: string,
|
||||
|
||||
@@ -5,12 +5,12 @@ import { ref } from 'vue'
|
||||
import { generateObjectData } from '#tests/graphql/builders/index.ts'
|
||||
import { mockPermissions } from '#tests/support/mock-permissions.ts'
|
||||
|
||||
import type { Ticket } from '#shared/graphql/types.ts'
|
||||
import type { TicketById } from '#shared/entities/ticket/types.ts'
|
||||
|
||||
import { useTicketView } from '../useTicketView.ts'
|
||||
|
||||
const ticketDefault = generateObjectData<Ticket>('Ticket')
|
||||
const ticket = ref<Ticket | undefined>(ticketDefault)
|
||||
const ticketDefault = generateObjectData<TicketById>('Ticket')
|
||||
const ticket = ref<TicketById | undefined>(ticketDefault)
|
||||
|
||||
describe('useTicketView', () => {
|
||||
it('check agent permission', () => {
|
||||
|
||||
@@ -4,17 +4,20 @@ import { computed, toRef, type Ref } from 'vue'
|
||||
|
||||
import { useApplicationStore } from '#shared/stores/application.ts'
|
||||
|
||||
import type { TicketById } from '../types.ts'
|
||||
import type { TicketLabel } from '../types.ts'
|
||||
|
||||
export const useTicketNumberAndTitle = (ticket?: Ref<Partial<TicketById> | undefined>) => {
|
||||
export const useTicketNumberAndTitle = (ticket?: Ref<TicketLabel | undefined>) => {
|
||||
const config = toRef(useApplicationStore(), 'config')
|
||||
|
||||
const getTicketNumberWithHook = (ticketNumber?: number | string) => {
|
||||
const getTicketNumberWithHook = (ticketNumber?: number | string | null) => {
|
||||
if (!ticketNumber) return ''
|
||||
return `${config.value.ticket_hook}${ticketNumber}`
|
||||
}
|
||||
|
||||
const getTicketNumberWithTitle = (ticketNumber?: number | string, ticketTitle?: string) => {
|
||||
const getTicketNumberWithTitle = (
|
||||
ticketNumber?: number | string | null,
|
||||
ticketTitle?: string | null,
|
||||
) => {
|
||||
if (!ticketNumber || !ticketTitle) return ''
|
||||
return `${getTicketNumberWithHook(ticketNumber)} - ${ticketTitle}`
|
||||
}
|
||||
|
||||
@@ -5,12 +5,14 @@ import type { SecurityValue } from '#shared/components/Form/fields/FieldSecurity
|
||||
import type { FormFieldValue } from '#shared/components/Form/types.ts'
|
||||
import type { TicketArticleFormValues } from '#shared/entities/ticket-article/action/plugins/types.ts'
|
||||
import {
|
||||
type TicketQuery,
|
||||
type EnumSecurityOption,
|
||||
type EnumTaskbarApp,
|
||||
type EnumTicketStateColorCode,
|
||||
type ReferencingTicketFragment,
|
||||
type TicketArticlesQuery,
|
||||
type TicketLiveUser,
|
||||
type TicketQuery,
|
||||
type TicketsCachedByOverviewQuery,
|
||||
type EnumTaskbarApp,
|
||||
type EnumSecurityOption,
|
||||
} from '#shared/graphql/types.ts'
|
||||
import type { ConfidentTake, PartialRequired } from '#shared/types/utils.ts'
|
||||
|
||||
@@ -45,6 +47,20 @@ export type TicketByList = NonNullable<
|
||||
|
||||
export type TicketArticle = ConfidentTake<TicketArticlesQuery, 'articles.edges.node'>
|
||||
|
||||
export interface TicketLabel {
|
||||
id?: string
|
||||
internalId?: number
|
||||
number?: number | string | null
|
||||
title?: string | null
|
||||
createdAt?: string | null
|
||||
stateColorCode?: EnumTicketStateColorCode | null
|
||||
state?: {
|
||||
name?: string | null
|
||||
} | null
|
||||
}
|
||||
|
||||
export type ReferencingTicket = ReferencingTicketFragment
|
||||
|
||||
export interface TicketCustomerUpdateFormData {
|
||||
customer_id: number
|
||||
organization_id?: number
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
// Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
|
||||
|
||||
import type { ObjectLike } from '#shared/types/utils.ts'
|
||||
|
||||
export interface SignupFormData {
|
||||
firstname: string
|
||||
lastname: string
|
||||
email: string
|
||||
password: string
|
||||
}
|
||||
|
||||
export interface EditableUser extends ObjectLike {
|
||||
id: string
|
||||
organization?: {
|
||||
internalId?: number | null
|
||||
} | null
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as Types from '#shared/graphql/types.ts';
|
||||
import gql from 'graphql-tag';
|
||||
export const HistoryIssuerFragmentDoc = gql`
|
||||
fragment HistoryIssuer on HistoryRecordIssuer {
|
||||
__typename
|
||||
... on User {
|
||||
id
|
||||
internalId
|
||||
@@ -41,6 +42,7 @@ export const HistoryIssuerFragmentDoc = gql`
|
||||
`;
|
||||
export const HistoryEventObjectFragmentDoc = gql`
|
||||
fragment HistoryEventObject on HistoryRecordEventObject {
|
||||
__typename
|
||||
... on Checklist {
|
||||
id
|
||||
name
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
# app/frontend/shared/server/apollo/cache/initializer/history.ts
|
||||
|
||||
fragment HistoryIssuer on HistoryRecordIssuer {
|
||||
__typename
|
||||
... on User {
|
||||
id
|
||||
internalId
|
||||
@@ -41,6 +42,7 @@ fragment HistoryIssuer on HistoryRecordIssuer {
|
||||
}
|
||||
|
||||
fragment HistoryEventObject on HistoryRecordEventObject {
|
||||
__typename
|
||||
... on Checklist {
|
||||
id
|
||||
name
|
||||
|
||||
6173
app/frontend/shared/graphql/schema-types.ts
Normal file
6173
app/frontend/shared/graphql/schema-types.ts
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -17,6 +17,7 @@ describe('localeFinder', () => {
|
||||
|
||||
const locales = [
|
||||
{
|
||||
__typename: 'Locale' as const,
|
||||
active: true,
|
||||
alias: 'de',
|
||||
dir: EnumTextDirection.Ltr,
|
||||
@@ -24,6 +25,7 @@ describe('localeFinder', () => {
|
||||
name: 'Deutsch',
|
||||
},
|
||||
{
|
||||
__typename: 'Locale' as const,
|
||||
active: true,
|
||||
alias: 'es',
|
||||
dir: EnumTextDirection.Ltr,
|
||||
@@ -31,6 +33,7 @@ describe('localeFinder', () => {
|
||||
name: 'Español',
|
||||
},
|
||||
{
|
||||
__typename: 'Locale' as const,
|
||||
active: true,
|
||||
alias: '',
|
||||
dir: EnumTextDirection.Ltr,
|
||||
@@ -52,6 +55,7 @@ describe('localeFinder', () => {
|
||||
it('returns default locale for no match', () => {
|
||||
windowSpy.mockImplementation(() => ['sv-SV'])
|
||||
expect(localeForBrowserLanguage(locales)).toStrictEqual({
|
||||
__typename: 'Locale',
|
||||
active: true,
|
||||
alias: 'en',
|
||||
dir: EnumTextDirection.Ltr,
|
||||
|
||||
@@ -23,6 +23,7 @@ const localeForBrowserLanguage = (
|
||||
}
|
||||
|
||||
return {
|
||||
__typename: 'Locale',
|
||||
locale: 'en-us',
|
||||
alias: 'en',
|
||||
// eslint-disable-next-line zammad/zammad-detect-translatable-string
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
|
||||
import { nullableMock } from '#tests/support/utils.ts'
|
||||
|
||||
import { errorOptions } from '#shared/router/error.ts'
|
||||
import { useAuthenticationStore } from '#shared/stores/authentication.ts'
|
||||
import { useSessionStore } from '#shared/stores/session.ts'
|
||||
import type { UserData } from '#shared/types/store.ts'
|
||||
|
||||
import permissionGuard from '../permission.ts'
|
||||
|
||||
@@ -60,14 +63,16 @@ describe('permissionGuard', () => {
|
||||
} as RouteLocationNormalized
|
||||
|
||||
useAuthenticationStore().authenticated = true
|
||||
useSessionStore().user = {
|
||||
useSessionStore().user = nullableMock<UserData>({
|
||||
__typename: 'User',
|
||||
id: '123',
|
||||
internalId: 1,
|
||||
permissions: {
|
||||
__typename: 'UserPermission',
|
||||
names: ['example.view'],
|
||||
},
|
||||
objectAttributeValues: [],
|
||||
}
|
||||
})
|
||||
|
||||
const result = permissionGuard(to, from, vi.fn())
|
||||
|
||||
@@ -99,14 +104,16 @@ describe('permissionGuard', () => {
|
||||
} as RouteLocationNormalized
|
||||
|
||||
useAuthenticationStore().authenticated = true
|
||||
useSessionStore().user = {
|
||||
useSessionStore().user = nullableMock<UserData>({
|
||||
__typename: 'User',
|
||||
id: '123',
|
||||
internalId: 1,
|
||||
permissions: {
|
||||
__typename: 'UserPermission',
|
||||
names: ['ticket.agent'],
|
||||
},
|
||||
objectAttributeValues: [],
|
||||
}
|
||||
})
|
||||
|
||||
const result = permissionGuard(to, from, vi.fn())
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ const mockQueryResult = (): LocalesQuery => {
|
||||
return {
|
||||
locales: [
|
||||
{
|
||||
__typename: 'Locale',
|
||||
locale: 'de-de',
|
||||
name: 'Deutsch',
|
||||
dir: EnumTextDirection.Ltr,
|
||||
@@ -21,6 +22,7 @@ const mockQueryResult = (): LocalesQuery => {
|
||||
active: true,
|
||||
},
|
||||
{
|
||||
__typename: 'Locale',
|
||||
locale: 'ar',
|
||||
name: 'Arabic',
|
||||
dir: EnumTextDirection.Rtl,
|
||||
|
||||
@@ -164,7 +164,10 @@ export const useSessionStore = defineStore(
|
||||
const setUserPreference = (key: string, value: JsonValue) => {
|
||||
if (!user.value) return
|
||||
|
||||
user.value.preferences[key] = value
|
||||
user.value.preferences = {
|
||||
...user.value.preferences,
|
||||
[key]: value,
|
||||
}
|
||||
|
||||
return user.value
|
||||
}
|
||||
|
||||
@@ -49,11 +49,12 @@ export type OperationMutationOptions<TResult, TVariables> =
|
||||
| ReactiveFunction<UseMutationOptions<TResult, TVariables>>
|
||||
|
||||
export type OperationMutationFunction<
|
||||
TResult = OperationMutationResult,
|
||||
// oxlint-disable-next-line no-explicit-any
|
||||
TVariables = any,
|
||||
TResult = any,
|
||||
// oxlint-disable-next-line no-explicit-any
|
||||
TVariables extends OperationVariables = any,
|
||||
> = (
|
||||
options: OperationMutationOptions<TResult, TVariables>,
|
||||
options?: OperationMutationOptions<TResult, TVariables>,
|
||||
) => UseMutationReturn<TResult, TVariables>
|
||||
|
||||
export type OperationQueryFunction<
|
||||
|
||||
@@ -1,29 +1,36 @@
|
||||
// Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
|
||||
|
||||
import { useSessionStore } from '#shared/stores/session.ts'
|
||||
import type { UserData } from '#shared/types/store.ts'
|
||||
|
||||
import { initializeStore } from './components/initializeStore.ts'
|
||||
import { nullableMock } from './utils.ts'
|
||||
|
||||
export const mockPermissions = (permissions: string[]) => {
|
||||
initializeStore()
|
||||
|
||||
const session = useSessionStore()
|
||||
if (!session.user) {
|
||||
session.user = {
|
||||
session.user = nullableMock({
|
||||
__typename: 'User',
|
||||
id: '123',
|
||||
internalId: 1,
|
||||
objectAttributeValues: [],
|
||||
}
|
||||
permissions: {
|
||||
__typename: 'UserPermission',
|
||||
names: permissions,
|
||||
},
|
||||
}) as unknown as UserData
|
||||
}
|
||||
|
||||
session.user!.permissions = { names: permissions }
|
||||
session.user!.permissions = { __typename: 'UserPermission', names: permissions }
|
||||
|
||||
if (Symbol.for('tests.permissions') in globalThis) return
|
||||
|
||||
Object.defineProperty(globalThis, Symbol.for('tests.permissions'), {
|
||||
get() {
|
||||
const session = useSessionStore()
|
||||
return session.user?.permissions || { names: [] }
|
||||
return session.user?.permissions || { __typename: 'UserPermission', names: [] }
|
||||
},
|
||||
configurable: true,
|
||||
})
|
||||
|
||||
@@ -3,17 +3,21 @@
|
||||
import { convertToGraphQLId } from '#shared/graphql/utils.ts'
|
||||
import { useSessionStore } from '#shared/stores/session.ts'
|
||||
import type { UserData } from '#shared/types/store.ts'
|
||||
import type { DeepPartial } from '#shared/types/utils.ts'
|
||||
|
||||
import { initializePiniaStore } from './components/renderComponent.ts'
|
||||
import { nullableMock } from './utils.ts'
|
||||
|
||||
export const mockUserCurrent = (mockUser: Partial<UserData> = {}) => {
|
||||
export const mockUserCurrent = (mockUser: DeepPartial<UserData> = {}) => {
|
||||
initializePiniaStore()
|
||||
const session = useSessionStore()
|
||||
session.user = {
|
||||
session.user = nullableMock({
|
||||
__typename: 'User',
|
||||
id: convertToGraphQLId('User', 2),
|
||||
internalId: 2,
|
||||
objectAttributeValues: [],
|
||||
preferences: {},
|
||||
permissions: null,
|
||||
...mockUser,
|
||||
}
|
||||
}) as UserData
|
||||
}
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
|
||||
import { FormUpdaterDocument } from '#shared/components/Form/graphql/queries/formUpdater.api.ts'
|
||||
import type { FormUpdaterQuery } from '#shared/graphql/types.ts'
|
||||
import type { DeepPartial } from '#shared/types/utils.ts'
|
||||
|
||||
import { mockGraphQLApi } from '../mock-graphql-api.ts'
|
||||
|
||||
export const mockFormUpdater = (formUpdater?: FormUpdaterQuery) => {
|
||||
export const mockFormUpdater = (formUpdater?: DeepPartial<FormUpdaterQuery>) => {
|
||||
return mockGraphQLApi(FormUpdaterDocument).willResolve(
|
||||
formUpdater || {
|
||||
formUpdater: {},
|
||||
formUpdater: { __typename: 'FormUpdaterResult' },
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,12 @@ import { TicketOverviewUpdatesDocument } from '#mobile/entities/ticket/graphql/s
|
||||
|
||||
import { mockGraphQLApi, mockGraphQLSubscription } from '../mock-graphql-api.ts'
|
||||
|
||||
const column = (key: string, value: string) => ({
|
||||
__typename: 'KeyValue' as const,
|
||||
key,
|
||||
value,
|
||||
})
|
||||
|
||||
export const getApiTicketOverviews = (): TicketOverviewsQuery => ({
|
||||
ticketOverviews: [
|
||||
{
|
||||
@@ -25,17 +31,17 @@ export const getApiTicketOverviews = (): TicketOverviewsQuery => ({
|
||||
prio: 100,
|
||||
active: true,
|
||||
viewColumns: [
|
||||
{ key: 'number', value: 'Number' },
|
||||
{ key: 'title', value: 'Title' },
|
||||
{ key: 'created_at', value: 'Created at' },
|
||||
{ key: 'updated_at', value: 'Updated at' },
|
||||
{ key: 'priority', value: 'Priority' },
|
||||
column('number', 'Number'),
|
||||
column('title', 'Title'),
|
||||
column('created_at', 'Created at'),
|
||||
column('updated_at', 'Updated at'),
|
||||
column('priority', 'Priority'),
|
||||
],
|
||||
orderColumns: [
|
||||
{ key: 'number', value: 'Number' },
|
||||
{ key: 'title', value: 'Title' },
|
||||
{ key: 'created_at', value: 'Created at' },
|
||||
{ key: 'updated_at', value: 'Updated at' },
|
||||
column('number', 'Number'),
|
||||
column('title', 'Title'),
|
||||
column('created_at', 'Created at'),
|
||||
column('updated_at', 'Updated at'),
|
||||
],
|
||||
groupBy: null,
|
||||
},
|
||||
@@ -53,16 +59,16 @@ export const getApiTicketOverviews = (): TicketOverviewsQuery => ({
|
||||
prio: 200,
|
||||
active: true,
|
||||
viewColumns: [
|
||||
{ key: 'number', value: 'Number' },
|
||||
{ key: 'title', value: 'Title' },
|
||||
{ key: 'created_at', value: 'Created at' },
|
||||
{ key: 'updated_at', value: 'Updated at' },
|
||||
column('number', 'Number'),
|
||||
column('title', 'Title'),
|
||||
column('created_at', 'Created at'),
|
||||
column('updated_at', 'Updated at'),
|
||||
],
|
||||
orderColumns: [
|
||||
{ key: 'number', value: 'Number' },
|
||||
{ key: 'title', value: 'Title' },
|
||||
{ key: 'created_at', value: 'Created at' },
|
||||
{ key: 'updated_at', value: 'Updated at' },
|
||||
column('number', 'Number'),
|
||||
column('title', 'Title'),
|
||||
column('created_at', 'Created at'),
|
||||
column('updated_at', 'Updated at'),
|
||||
],
|
||||
groupBy: null,
|
||||
},
|
||||
@@ -80,16 +86,16 @@ export const getApiTicketOverviews = (): TicketOverviewsQuery => ({
|
||||
prio: 300,
|
||||
active: true,
|
||||
viewColumns: [
|
||||
{ key: 'number', value: 'Number' },
|
||||
{ key: 'title', value: 'Title' },
|
||||
{ key: 'created_at', value: 'Created at' },
|
||||
{ key: 'updated_at', value: 'Updated at' },
|
||||
column('number', 'Number'),
|
||||
column('title', 'Title'),
|
||||
column('created_at', 'Created at'),
|
||||
column('updated_at', 'Updated at'),
|
||||
],
|
||||
orderColumns: [
|
||||
{ key: 'number', value: 'Number' },
|
||||
{ key: 'title', value: 'Title' },
|
||||
{ key: 'created_at', value: 'Created at' },
|
||||
{ key: 'updated_at', value: 'Updated at' },
|
||||
column('number', 'Number'),
|
||||
column('title', 'Title'),
|
||||
column('created_at', 'Created at'),
|
||||
column('updated_at', 'Updated at'),
|
||||
],
|
||||
groupBy: null,
|
||||
},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user