feat(kiosk): implement kiosk report submission functionality with offline support test(outbox): add tests for outbox functionality including report submission and error handling feat(outbox): create outbox management for queued reports with IndexedDB persistence feat(uuid): add UUID generation utility for client IDs feat(qr): implement QR code generation for reporting links by site and area feat(kiosk): create Kiosk page for reporting incidents without an account
93 lines
4.7 KiB
TypeScript
93 lines
4.7 KiB
TypeScript
import { expect, test } from '@playwright/test'
|
||
import { signIn, unique, users } from './fixtures'
|
||
|
||
const token = () => process.env.E2E_KIOSK_TOKEN!
|
||
const site = () => process.env.E2E_SITE_ID!
|
||
const area = () => process.env.E2E_AREA_ID!
|
||
|
||
test('saves a report offline and sends it when the connection returns', async ({ page, context }) => {
|
||
const title = unique('Offline report: frayed sling')
|
||
await signIn(page, 'reporter')
|
||
await page.goto('/report')
|
||
await expect(page.getByLabel('Incident title')).toBeVisible()
|
||
|
||
await context.setOffline(true)
|
||
await page.getByLabel('Incident title').fill(title)
|
||
await page.getByRole('button', { name: 'Submit report' }).click()
|
||
await expect(page.getByRole('heading', { name: 'Saved on this device' })).toBeVisible()
|
||
await expect(page.getByText('1 report waiting to send')).toBeVisible()
|
||
|
||
await context.setOffline(false)
|
||
await expect(page.getByText('1 report waiting to send')).toBeHidden({ timeout: 15_000 })
|
||
await page.goto('/incidents')
|
||
await expect(page.getByRole('link', { name: title })).toBeVisible()
|
||
})
|
||
|
||
test('kiosk: reports without an account, in Spanish, from a QR code', async ({ browser, page }) => {
|
||
const title = unique('Cable suelto en el pasillo')
|
||
const kiosk = await (await browser.newContext({ locale: 'es-MX' })).newPage()
|
||
await kiosk.goto(`/k/${token()}?site=${site()}&area=${area()}`)
|
||
await expect(kiosk.getByRole('heading', { name: 'Reporte un problema de seguridad' })).toBeVisible()
|
||
await expect(kiosk.getByLabel('Edificio o área')).toHaveValue(area())
|
||
|
||
await kiosk.getByText('Peligro', { exact: true }).click()
|
||
await kiosk.getByText('Alta', { exact: true }).click()
|
||
await kiosk.getByLabel('En pocas palabras, ¿qué vio?').fill(title)
|
||
await kiosk.getByLabel('Su nombre').fill('Luis')
|
||
await kiosk.getByRole('button', { name: 'Enviar reporte' }).click()
|
||
await expect(kiosk.getByRole('heading', { name: 'Gracias' })).toBeVisible()
|
||
const reference = (await kiosk.getByText(/Su reporte INC-\d{4}/).textContent())!.match(/INC-\d{4}/)![0]
|
||
|
||
// Switching language works on the kiosk too
|
||
await kiosk.getByRole('button', { name: 'Reportar otra cosa' }).click()
|
||
await kiosk.getByRole('button', { name: 'English' }).click()
|
||
await expect(kiosk.getByRole('heading', { name: 'Report a safety concern' })).toBeVisible()
|
||
|
||
// The safety team sees who reported it and through which link
|
||
await signIn(page, 'admin')
|
||
await page.goto(`/incidents/${Number(reference.slice(4))}`)
|
||
await expect(page.getByRole('heading', { level: 1 })).toHaveText(title)
|
||
await expect(page.getByText('Reported by Luis via Canteen tablet')).toBeVisible()
|
||
await expect(page.getByText('Press Shop').first()).toBeVisible()
|
||
})
|
||
|
||
test('kiosk: a made-up or revoked link explains itself', async ({ page }) => {
|
||
await page.goto('/k/not-a-real-token')
|
||
await expect(page.getByText('This reporting link isn’t working')).toBeVisible()
|
||
})
|
||
|
||
test('QR codes open the report form with the location filled in', async ({ page, isMobile }) => {
|
||
test.skip(isMobile, 'Printing QR sheets is a desktop task')
|
||
await signIn(page, 'admin')
|
||
await page.goto(`/locations/qr?site=${site()}`)
|
||
await expect(page.getByRole('img', { name: /QR code for/ })).toHaveCount(2) // whole site + Press Shop
|
||
const url = await page.locator('main').getByText(/\/report\?site=/).nth(1).textContent()
|
||
expect(url).toContain(`area=${area()}`)
|
||
|
||
await page.goto(url!.trim())
|
||
await expect(page.getByText('Location filled in from the QR code.')).toBeVisible()
|
||
await expect(page.getByLabel('Building or area')).toHaveValue(area())
|
||
})
|
||
|
||
test('the signed-in app speaks Spanish too', async ({ page, isMobile }) => {
|
||
await signIn(page, 'reporter')
|
||
if (isMobile) await page.getByRole('button', { name: 'Open menu' }).click()
|
||
await page.getByRole('button', { name: 'Español' }).click()
|
||
if (isMobile) await page.getByRole('button', { name: 'Cerrar menú' }).click()
|
||
await page.goto('/report')
|
||
await expect(page.getByRole('heading', { name: 'Reportar un incidente' })).toBeVisible()
|
||
await expect(page.locator('html')).toHaveAttribute('lang', 'es')
|
||
await page.goto('/actions')
|
||
await expect(page.getByRole('heading', { name: 'Mis acciones' })).toBeVisible()
|
||
expect(users.reporter.email).toBeTruthy()
|
||
})
|
||
|
||
test('is installable: manifest and service worker', async ({ page }) => {
|
||
await page.goto('/login')
|
||
const manifest = await (await page.request.get('/manifest.webmanifest')).json()
|
||
expect(manifest).toMatchObject({ name: 'Open SafetyLog EHS', display: 'standalone' })
|
||
expect(manifest.icons.map((icon: { sizes: string }) => icon.sizes)).toEqual(expect.arrayContaining(['192x192', '512x512']))
|
||
const scope = await page.evaluate(async () => (await navigator.serviceWorker.ready).scope)
|
||
expect(scope).toMatch(/\/$/)
|
||
})
|