tasks-mutate-drawer.test.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import { useState } from 'react'
  2. import { beforeEach, describe, expect, it, vi } from 'vitest'
  3. import { render } from 'vitest-browser-react'
  4. import { userEvent } from 'vitest/browser'
  5. import { showSubmittedData } from '@/lib/show-submitted-data'
  6. import { type Task } from '../data/schema'
  7. import { TasksMutateDrawer } from './tasks-mutate-drawer'
  8. vi.mock('@/lib/show-submitted-data', () => ({ showSubmittedData: vi.fn() }))
  9. const MOCK_TASK = {
  10. id: 'task-1',
  11. title: 'Existing task',
  12. status: 'in progress',
  13. label: 'feature',
  14. priority: 'medium',
  15. } as const satisfies Task
  16. describe('TasksMutateDrawer', () => {
  17. beforeEach(() => vi.clearAllMocks())
  18. it('renders create title and description', async () => {
  19. const { getByRole, getByText } = await render(
  20. <TasksMutateDrawer open onOpenChange={vi.fn()} />
  21. )
  22. const title = getByRole('heading', {
  23. level: 2,
  24. name: /Create Task/i,
  25. })
  26. const desc = getByText(/Add a new task/i)
  27. await expect.element(title).toBeInTheDocument()
  28. await expect.element(desc).toBeInTheDocument()
  29. })
  30. it('renders edit title, description, and prefilled title', async () => {
  31. const { getByRole, getByText } = await render(
  32. <TasksMutateDrawer open onOpenChange={vi.fn()} currentRow={MOCK_TASK} />
  33. )
  34. const title = getByRole('heading', {
  35. level: 2,
  36. name: /Update Task/i,
  37. })
  38. const desc = getByText(/Update the task/i)
  39. const titleInput = getByRole('textbox', { name: /Title/i })
  40. const statusSelect = getByRole('combobox', { name: /Status/i })
  41. const labelRadio = getByRole('radio', { name: MOCK_TASK.label })
  42. const priorityRadio = getByRole('radio', { name: MOCK_TASK.priority })
  43. await expect.element(title).toBeInTheDocument()
  44. await expect.element(desc).toBeInTheDocument()
  45. await expect.element(titleInput).toHaveValue(MOCK_TASK.title)
  46. await expect
  47. .element(statusSelect)
  48. .toHaveTextContent(new RegExp(MOCK_TASK.status, 'i'))
  49. await expect.element(labelRadio).toBeChecked()
  50. await expect.element(priorityRadio).toBeChecked()
  51. })
  52. it('shows validation messages when submitting an empty form', async () => {
  53. const { getByRole, getByText } = await render(
  54. <TasksMutateDrawer open onOpenChange={vi.fn()} />
  55. )
  56. const saveButton = getByRole('button', { name: /Save changes/i })
  57. await userEvent.click(saveButton)
  58. await expect.element(getByText(/Title is required.$/i)).toBeInTheDocument()
  59. await expect
  60. .element(getByText(/Please select a status.$/i))
  61. .toBeInTheDocument()
  62. await expect
  63. .element(getByText(/Please select a label.$/i))
  64. .toBeInTheDocument()
  65. await expect
  66. .element(getByText(/Please choose a priority.$/i))
  67. .toBeInTheDocument()
  68. })
  69. it('submits create form and shows submitted data', async () => {
  70. const onOpenChange = vi.fn()
  71. const { getByRole } = await render(
  72. <TasksMutateDrawer open onOpenChange={onOpenChange} />
  73. )
  74. const titleInput = getByRole('textbox', { name: /Title/i })
  75. await userEvent.fill(titleInput, 'New task title')
  76. const statusSelect = getByRole('combobox', { name: /Status/i })
  77. await userEvent.click(statusSelect)
  78. await userEvent.click(getByRole('option', { name: /Todo/i }))
  79. await userEvent.click(getByRole('radio', { name: /^Bug$/i }))
  80. await userEvent.click(getByRole('radio', { name: /^Low$/i }))
  81. const saveButton = getByRole('button', { name: /Save changes/i })
  82. await userEvent.click(saveButton)
  83. expect(onOpenChange).toHaveBeenCalledOnce()
  84. expect(onOpenChange).toHaveBeenCalledWith(false)
  85. expect(showSubmittedData).toHaveBeenCalledOnce()
  86. expect(showSubmittedData).toHaveBeenCalledWith({
  87. title: 'New task title',
  88. status: 'todo',
  89. label: 'bug',
  90. priority: 'low',
  91. })
  92. })
  93. it('closes when Close is clicked', async () => {
  94. const onOpenChange = vi.fn()
  95. const { getByRole } = await render(
  96. <TasksMutateDrawer open onOpenChange={onOpenChange} />
  97. )
  98. const closeButtons = getByRole('dialog')
  99. .getByRole('button', {
  100. name: /Close/i,
  101. })
  102. .all()
  103. expect(closeButtons).toHaveLength(2)
  104. await userEvent.click(closeButtons[1])
  105. expect(onOpenChange).toHaveBeenCalledOnce()
  106. expect(onOpenChange).toHaveBeenCalledWith(false)
  107. })
  108. it('resets entered values when the sheet is closed and reopened', async () => {
  109. function Harness() {
  110. const [open, setOpen] = useState(true)
  111. return (
  112. <>
  113. <button type='button' onClick={() => setOpen(true)}>
  114. Reopen
  115. </button>
  116. <TasksMutateDrawer open={open} onOpenChange={setOpen} />
  117. </>
  118. )
  119. }
  120. const { getByRole } = await render(<Harness />)
  121. const titleInput = getByRole('textbox', { name: /Title/i })
  122. await userEvent.fill(titleInput, 'Draft title')
  123. await expect.element(titleInput).toHaveValue('Draft title')
  124. const statusSelect = getByRole('combobox', { name: /Status/i })
  125. await userEvent.click(statusSelect)
  126. await userEvent.click(getByRole('option', { name: /Todo/i }))
  127. await expect.element(statusSelect).toHaveTextContent(/Todo/i)
  128. const labelRadio = getByRole('radio', { name: /^Documentation$/i })
  129. await userEvent.click(labelRadio)
  130. await expect.element(labelRadio).toBeChecked()
  131. const priorityRadio = getByRole('radio', { name: /^High$/i })
  132. await userEvent.click(priorityRadio)
  133. await expect.element(priorityRadio).toBeChecked()
  134. const closeButtons = getByRole('dialog')
  135. .getByRole('button', {
  136. name: /Close/i,
  137. })
  138. .all()
  139. await userEvent.click(closeButtons[0])
  140. const reopenButton = getByRole('button', { name: /Reopen/i })
  141. await userEvent.click(reopenButton)
  142. await expect.element(titleInput).toHaveValue('')
  143. await expect.element(statusSelect).not.toHaveTextContent(/Todo/i)
  144. await expect.element(labelRadio).not.toBeChecked()
  145. await expect.element(priorityRadio).not.toBeChecked()
  146. })
  147. })