zammad/zammad

View on GitHub
app/frontend/apps/desktop/components/Form/fields/FieldToggleList/__tests__/FieldToggleList.spec.ts

Summary

Maintainability
D
2 days
Test Coverage
// Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/

import { FormKit } from '@formkit/vue'
import { waitFor } from '@testing-library/vue'
import { getNode } from '@formkit/core'
import type { SetRequired } from 'type-fest'
import { renderComponent } from '#tests/support/components/index.ts'
import { waitForNextTick } from '#tests/support/utils.ts'
import type { ToggleListOption } from '../types.ts'

const testOptions: SetRequired<ToggleListOption, 'label'>[] = [
  {
    value: 0,
    label: 'Item A',
  },
  {
    value: 1,
    label: 'Item B',
  },
  {
    value: 2,
    label: 'Item C',
  },
]

const testOptionsWithDescription: SetRequired<ToggleListOption, 'label'>[] = [
  {
    value: 0,
    label: 'Item A',
    description: 'A Description',
  },
]

const wrapperParameters = {
  form: true,
  formField: true,
  dialog: true,
}

const renderToggleListInput = async (props: Record<string, unknown> = {}) => {
  const view = renderComponent(FormKit, {
    ...wrapperParameters,
    props: {
      id: 'toggleList',
      type: 'toggleList',
      name: 'toggleList',
      label: 'Toggle list',
      formId: 'form',
      options: testOptions,
      ...props,
    },
    form: true,
  })

  await waitForNextTick(true)

  return view
}

describe('Form - Field - Toggle List', () => {
  it('renders given options', async () => {
    const wrapper = await renderToggleListInput()

    const selectOptions = wrapper.getAllByRole('listitem')

    expect(selectOptions).toHaveLength(testOptions.length)

    selectOptions.forEach((selectOption, index) => {
      expect(selectOption).toHaveTextContent(testOptions[index].label)
    })
  })

  it('shows optional description', async () => {
    const wrapper = await renderToggleListInput({
      options: testOptionsWithDescription,
    })

    const selectOptions = wrapper.getAllByRole('listitem')

    expect(selectOptions).toHaveLength(testOptionsWithDescription.length)

    selectOptions.forEach((selectOption, index) => {
      expect(selectOption).toHaveTextContent(
        testOptionsWithDescription[index].label,
      )
    })
  })
})

// Cover all use cases from the FormKit custom input checklist.
//   More info here: https://formkit.com/essentials/custom-inputs#input-checklist
describe('Fields - Field Toggle List - Input Checklist', () => {
  it('implements input id attribute', async () => {
    const view = await renderToggleListInput({
      id: 'test_id',
    })

    expect(view.getByLabelText('Toggle list')).toHaveAttribute('id', 'test_id')
  })

  it('implements input name', async () => {
    const view = await renderToggleListInput({
      name: 'test_name',
    })

    expect(view.getByLabelText('Toggle list')).toHaveAttribute(
      'name',
      'test_name',
    )
  })

  it('implements blur handler', async () => {
    const blurHandler = vi.fn()

    const view = await renderToggleListInput({
      onBlur: blurHandler,
    })

    view.getByLabelText('Toggle list').focus()

    await view.events.tab()

    expect(blurHandler).toHaveBeenCalledOnce()
  })

  it('implements input handler', async () => {
    const wrapper = await renderToggleListInput()

    for await (const [i, item] of [testOptions[1], testOptions[2]].entries()) {
      wrapper.events.click(wrapper.getByLabelText(item.label))

      await waitFor(() => {
        expect(wrapper.emitted().inputRaw[i]).toBeTruthy()
      })
    }

    await waitFor(() => {
      expect(getNode('toggleList')?.value).toEqual([
        testOptions[1].value,
        testOptions[2].value,
      ])
    })
  })

  it('implements input value display', async () => {
    const wrapper = await renderToggleListInput({
      value: [testOptions[1].value],
    })

    const toggle1 = wrapper.getByLabelText(testOptions[0].label)
    expect(toggle1).not.toBeChecked()

    const toggle2 = wrapper.getByLabelText(testOptions[1].label)
    expect(toggle2).toBeChecked()

    const toggle3 = wrapper.getByLabelText(testOptions[2].label)
    expect(toggle3).not.toBeChecked()
  })

  it('implements disabled', async () => {
    const view = await renderToggleListInput({
      disabled: true,
    })

    expect(view.getByLabelText('Toggle list')).toBeDisabled()

    for (const option of testOptions) {
      expect(view.getByLabelText(option.label)).toBeDisabled()
    }
  })

  it('implements attribute passthrough', async () => {
    const view = await renderToggleListInput({
      'test-attribute': 'test_value',
    })

    expect(view.getByLabelText('Toggle list')).toHaveAttribute(
      'test-attribute',
      'test_value',
    )
  })

  it('implements standardized classes', async () => {
    const view = await renderToggleListInput()

    expect(view.getByLabelText('Toggle list')).toHaveClass('formkit-input')
  })
})