zammad/zammad

View on GitHub
app/frontend/shared/components/Form/fields/FieldEditor/__tests__/SuggestionsList.spec.ts

Summary

Maintainability
B
4 hrs
Test Coverage
// Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/

import { flushPromises } from '@vue/test-utils'
import { ref } from 'vue'

import { renderComponent } from '#tests/support/components/index.ts'

import SuggestionsList from '../SuggestionsList.vue'

import type {
  MentionKnowledgeBaseItem,
  MentionTextItem,
  MentionUserItem,
} from '../types.ts'

describe('component for rendering suggestions', () => {
  it('renders knowledge base article', () => {
    const items: MentionKnowledgeBaseItem[] = [
      {
        __typename: 'KnowledgeBaseAnswerTranslation',
        id: btoa('Test 1'),
        title: 'Test 1',
        categoryTreeTranslation: [
          {
            __typename: 'KnowledgeBaseCategoryTranslation',
            id: btoa('Category 1.1'),
            title: 'Category 1.1',
          },
        ],
      },
      {
        __typename: 'KnowledgeBaseAnswerTranslation',
        id: btoa('Test 2'),
        title: 'Test 2',
        categoryTreeTranslation: [
          {
            __typename: 'KnowledgeBaseCategoryTranslation',
            id: btoa('Category 2.1'),
            title: 'Category 2.1',
          },
          {
            __typename: 'KnowledgeBaseCategoryTranslation',
            id: btoa('Category 2.2'),
            title: 'Category 2.2',
          },
        ],
      },
    ]

    const view = renderComponent(SuggestionsList, {
      props: {
        items,
        type: 'knowledge-base',
        command: vi.fn(),
      },
    })

    expect(
      view.getByRole('option', { name: 'Category 1.1 Test 1' }),
    ).toBeInTheDocument()
    expect(
      view.getByRole('option', { name: 'Category 2.1 Category 2.2 Test 2' }),
    ).toBeInTheDocument()
  })

  it('renders text item', () => {
    const items: MentionTextItem[] = [
      {
        name: 'Text Item',
        keywords: 'key',
        renderedContent: 'content',
        id: btoa('Text Item'),
      },
    ]

    const view = renderComponent(SuggestionsList, {
      props: {
        items,
        type: 'text',
        command: vi.fn(),
      },
    })

    expect(
      view.getByRole('option', { name: 'Text Item key' }),
    ).toBeInTheDocument()
  })

  it('renders user mention', () => {
    const items: MentionUserItem[] = [
      {
        id: btoa('John Doe'),
        fullname: 'John Doe',
        internalId: 1,
        email: 'john@mail.com',
      },
      {
        id: btoa('Nicole Braun'),
        fullname: 'Nicole Braun',
        internalId: 2,
      },
    ]

    const view = renderComponent(SuggestionsList, {
      props: {
        items,
        type: 'user',
        command: vi.fn(),
      },
    })

    expect(
      view.getByRole('option', { name: 'John Doe <john@mail.com>' }),
    ).toBeInTheDocument()
    expect(
      view.getByRole('option', { name: 'Nicole Braun' }),
    ).toBeInTheDocument()
  })
})

describe('actions in list', () => {
  const items: MentionUserItem[] = [
    {
      id: btoa('John Doe'),
      fullname: 'John Doe',
      internalId: 1,
    },
    {
      id: btoa('Nicole Braun'),
      fullname: 'Nicole Braun',
      internalId: 2,
    },
    {
      id: btoa('Erik Wise'),
      fullname: 'Erik Wise',
      internalId: 3,
    },
  ]

  const renderList = () => {
    const listExposed = ref<{
      onKeyDown: (e: any) => void
    }>()
    const command = vi.fn()
    const view = renderComponent(
      {
        components: { SuggestionsList },
        template: `<SuggestionsList v-bind="$props" ref="listExposed" />`,
        setup: () => ({ listExposed }),
      },
      {
        props: {
          items,
          type: 'user',
          command,
        },
      },
    )
    const triggerKey = async (key: string) => {
      listExposed.value?.onKeyDown({ event: { key } })

      await flushPromises()
    }

    return {
      view,
      command,
      triggerKey,
    }
  }

  it('can travers with arrow keys', async () => {
    const { view, triggerKey } = renderList()

    const options = view.getAllByRole('option')

    expect(options[0]).toHaveClass('bg-gray-400')

    await triggerKey('ArrowDown')

    expect(options[0]).not.toHaveClass('bg-gray-400')
    expect(options[1]).toHaveClass('bg-gray-400')

    await triggerKey('ArrowDown')

    expect(options[0]).not.toHaveClass('bg-gray-400')
    expect(options[1]).not.toHaveClass('bg-gray-400')
    expect(options[2]).toHaveClass('bg-gray-400')

    await triggerKey('ArrowDown')

    expect(options[0]).toHaveClass('bg-gray-400')
    expect(options[1]).not.toHaveClass('bg-gray-400')
    expect(options[2]).not.toHaveClass('bg-gray-400')

    await triggerKey('ArrowUp')

    expect(options[0]).not.toHaveClass('bg-gray-400')
    expect(options[1]).not.toHaveClass('bg-gray-400')
    expect(options[2]).toHaveClass('bg-gray-400')
  })

  it('selects on enter', async () => {
    const { command, triggerKey } = renderList()

    await triggerKey('Enter')

    expect(command).toHaveBeenCalledWith(items[0])
  })

  it('selects on tab', async () => {
    const { command, triggerKey } = renderList()

    await triggerKey('Enter')

    expect(command).toHaveBeenCalledWith(items[0])
  })
})