4
0

condition-map.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import { expect, type Locator, type Page } from '@playwright/test'
  2. import { settle } from './hydration'
  3. import { type PdfContent, pdfContent } from './pdf'
  4. /**
  5. * Driving the vehicle condition map: the drawing on a work order's Drop-off
  6. * tab and on an inspection's condition map check, the mark editor, and the
  7. * documents that print it.
  8. *
  9. * A mark is added by clicking a panel of the drawing. Every panel is an SVG
  10. * path carrying its view and panel id, so a spec names the panel it means
  11. * rather than aiming at pixels. The click creates the mark at once, with the
  12. * first kind on offer, and opens the editor on it; the editor saves each
  13. * change as it is made.
  14. */
  15. /** The map card on the page (one per work order, one per map check). */
  16. export function conditionMap(page: Page): Locator {
  17. return page.getByTestId('condition-map').first()
  18. }
  19. /** Opens the job's Drop-off tab and waits for its map. */
  20. export async function openDropoff(page: Page, jobUrl: string): Promise<Locator> {
  21. await page.goto(jobUrl)
  22. await settle(page)
  23. const files = page.getByTestId('files-media')
  24. const tab = files.getByRole('tab', { name: /^Drop-off/ })
  25. await expect(async () => {
  26. await tab.click()
  27. await expect(tab).toHaveAttribute('aria-selected', 'true', { timeout: 2_000 })
  28. }).toPass({ timeout: 30_000 })
  29. const map = conditionMap(page)
  30. await expect(map).toBeVisible()
  31. return map
  32. }
  33. /** The count on the Drop-off tab: its photos and this visit's marks. */
  34. export function dropoffTab(page: Page): Locator {
  35. return page.getByTestId('files-media').getByRole('tab', { name: /^Drop-off/ })
  36. }
  37. /** The mark editor, whichever mark it is open on. */
  38. export function markEditor(page: Page): Locator {
  39. return page.getByRole('dialog').filter({ has: page.getByRole('group', { name: 'Severity' }) })
  40. }
  41. /**
  42. * Clicks a panel of the drawing and returns the editor the new mark opens in.
  43. * Repeated until the editor opens: a click before hydration does nothing.
  44. */
  45. export async function addMark(
  46. page: Page,
  47. map: Locator,
  48. view: 'top' | 'left' | 'right' | 'front' | 'rear',
  49. panel: string
  50. ): Promise<Locator> {
  51. const target = map.locator(`path[data-view="${view}"][data-panel="${panel}"]`)
  52. const editor = markEditor(page)
  53. await expect(async () => {
  54. await target.click()
  55. await expect(editor).toBeVisible({ timeout: 3_000 })
  56. }).toPass({ timeout: 30_000 })
  57. return editor
  58. }
  59. /** Closes the editor through "Done", which also keeps the note. */
  60. export async function closeEditor(page: Page): Promise<void> {
  61. const editor = markEditor(page)
  62. await editor.getByRole('button', { name: 'Done', exact: true }).click()
  63. await expect(editor).toBeHidden()
  64. }
  65. /** This visit's marks, as the legend lists them: "Mark 1: Dent on the Left front door". */
  66. export function ownMarks(map: Locator): Locator {
  67. return map.getByRole('region', { name: 'Marks' }).getByRole('button', { name: /^Mark \d+:/ })
  68. }
  69. /** The marks from other visits, drawn grey. */
  70. export function earlierSection(map: Locator): Locator {
  71. return map.getByRole('region', { name: 'Recorded on an earlier visit' })
  72. }
  73. /** A PDF the page's own session may fetch, read as text. */
  74. export async function pdfAt(page: Page, path: string): Promise<PdfContent> {
  75. const response = await page.request.get(path)
  76. expect(response.status(), `${path} answers`).toBe(200)
  77. return pdfContent(await response.body())
  78. }
  79. export const workOrderPdf = (page: Page, jobId: string) =>
  80. pdfAt(page, `/api/protected/services/${jobId}/work-order-pdf`)
  81. export const invoicePdf = (page: Page, jobId: string) =>
  82. pdfAt(page, `/api/protected/services/${jobId}/pdf`)
  83. export const certificatePdf = (page: Page, inspectionId: string) =>
  84. pdfAt(page, `/api/protected/inspections/${inspectionId}/pdf`)
  85. /** Whether a printed document carries the condition map section at all. */
  86. export function printsMap(pdf: PdfContent): boolean {
  87. return /VEHICLE CONDITION/i.test(pdf.flat)
  88. }