|
| 1 | +import { expect, test } from '@playwright/test' |
| 2 | + |
| 3 | +// Accessibility regression tests for the recipient attribute-entry form |
| 4 | +// (issues encryption4all/postguard-website#268 and #269). |
| 5 | +// |
| 6 | +// #268: a screenreader did not clearly announce whether a field was required. |
| 7 | +// The required email field must expose `aria-required`, carry a visible |
| 8 | +// asterisk, and the form must show an explanatory legend for the asterisk. |
| 9 | +// #269: it was not clear that the phone number and birthday attributes are |
| 10 | +// optional. Each optional attribute label must say "(optional)" so it is |
| 11 | +// announced to screenreader users. |
| 12 | + |
| 13 | +test.beforeEach(async ({ page }) => { |
| 14 | + await page.goto('/fileshare/') |
| 15 | + // The recipient form renders in the initial FileSelection state. |
| 16 | + await expect( |
| 17 | + page.getByRole('textbox', { name: /email address/i }) |
| 18 | + ).toBeVisible() |
| 19 | +}) |
| 20 | + |
| 21 | +test('required email field exposes aria-required and a visible asterisk', async ({ |
| 22 | + page, |
| 23 | +}) => { |
| 24 | + const email = page.getByRole('textbox', { name: /email address/i }) |
| 25 | + await expect(email).toHaveAttribute('aria-required', 'true') |
| 26 | + // Native required is still present so browser validation also fires. |
| 27 | + await expect(email).toHaveAttribute('required', '') |
| 28 | + |
| 29 | + // The asterisk is rendered in the label and hidden from the a11y tree |
| 30 | + // (the required state itself is conveyed by aria-required). |
| 31 | + const asterisk = page.locator('label.field-label .required-asterisk') |
| 32 | + await expect(asterisk).toHaveText('*') |
| 33 | + await expect(asterisk).toHaveAttribute('aria-hidden', 'true') |
| 34 | +}) |
| 35 | + |
| 36 | +test('the form shows a legend explaining the required-field asterisk', async ({ |
| 37 | + page, |
| 38 | +}) => { |
| 39 | + const legend = page.locator('#required-fields-legend') |
| 40 | + await expect(legend).toBeVisible() |
| 41 | + await expect(legend).toContainText('*') |
| 42 | + |
| 43 | + // The required email input points at the legend for assistive tech. |
| 44 | + const email = page.getByRole('textbox', { name: /email address/i }) |
| 45 | + await expect(email).toHaveAttribute( |
| 46 | + 'aria-describedby', |
| 47 | + 'required-fields-legend' |
| 48 | + ) |
| 49 | +}) |
| 50 | + |
| 51 | +test('optional attributes are announced as optional via their label', async ({ |
| 52 | + page, |
| 53 | +}) => { |
| 54 | + // Add the mobile-number attribute (#269 phone number). |
| 55 | + await page.getByRole('button', { name: /mobile phone number/i }).click() |
| 56 | + const phone = page.getByLabel(/mobile phone number/i) |
| 57 | + await expect(phone).toBeVisible() |
| 58 | + // The accessible name of the field must include "(optional)". |
| 59 | + await expect(phone).toHaveAccessibleName(/\(optional\)/i) |
| 60 | + |
| 61 | + // Add the date-of-birth attribute (#269 birthday). |
| 62 | + await page.getByRole('button', { name: /date of birth/i }).click() |
| 63 | + const birthday = page.getByLabel(/date of birth/i) |
| 64 | + await expect(birthday).toBeVisible() |
| 65 | + await expect(birthday).toHaveAccessibleName(/\(optional\)/i) |
| 66 | +}) |
0 commit comments