attachments.ts 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738
  1. import { expect, type Page } from '@playwright/test'
  2. /**
  3. * Putting a file on a work order, through the files card's tab that takes it.
  4. *
  5. * Shared because two specs need it for different reasons: one asks what an
  6. * attachment does to the printed invoice, the other needs a file that
  7. * genuinely belongs to one workshop before it can check another cannot fetch
  8. * it. Neither may depend on the other having run.
  9. */
  10. export async function attach(
  11. page: Page,
  12. tab: 'Photos' | 'Documents',
  13. file: { name: string; mimeType: string; buffer: Buffer }
  14. ): Promise<void> {
  15. // The tab counts what it holds, "Documents 1" once there is one, so it is
  16. // found by what it starts with rather than by its whole name. The first
  17. // click can land before hydration, so it is repeated until the tab turns.
  18. const files = page.getByTestId('files-media')
  19. const trigger = files.getByRole('tab', { name: new RegExp(`^${tab}`) })
  20. await expect(async () => {
  21. await trigger.click()
  22. await expect(trigger).toHaveAttribute('aria-selected', 'true', { timeout: 2_000 })
  23. }).toPass({ timeout: 30_000 })
  24. await files.locator('input[type="file"]').setInputFiles(file)
  25. // Each file lands as a tile named for it. A photo is re-encoded in the
  26. // browser on the way up and may come back with another extension.
  27. const stem = file.name.replace(/\.[^.]+$/, '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
  28. await expect(
  29. files
  30. .getByTestId('media-tile')
  31. .filter({ has: page.getByLabel(new RegExp(stem)) })
  32. .first(),
  33. `${file.name} reached the job`
  34. ).toBeVisible({ timeout: 30_000 })
  35. }