Skip to content

Commit daf8061

Browse files
test: adds assert helpers to the a11y suite (#15132)
1 parent 536582f commit daf8061

File tree

5 files changed

+107
-125
lines changed

5 files changed

+107
-125
lines changed

test/a11y/e2e.spec.ts

Lines changed: 61 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Page, TestInfo } from '@playwright/test'
1+
import type { Page } from '@playwright/test'
22

33
import { expect, test } from '@playwright/test'
44
import { openNav } from 'helpers/e2e/toggleNav.js'
@@ -7,8 +7,11 @@ import { fileURLToPath } from 'url'
77

88
import { ensureCompilationIsDone, initPageConsoleErrorCatch } from '../helpers.js'
99
import { AdminUrlUtil } from '../helpers/adminUrlUtil.js'
10-
import { checkFocusIndicators } from '../helpers/e2e/checkFocusIndicators.js'
11-
import { checkHorizontalOverflow } from '../helpers/e2e/checkHorizontalOverflow.js'
10+
import { assertAllElementsHaveFocusIndicators } from '../helpers/e2e/checkFocusIndicators.js'
11+
import {
12+
assertNoHorizontalOverflow,
13+
checkHorizontalOverflow,
14+
} from '../helpers/e2e/checkHorizontalOverflow.js'
1215
import { runAxeScan } from '../helpers/e2e/runAxeScan.js'
1316
import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
1417
import { TEST_TIMEOUT_LONG } from '../playwright.config.js'
@@ -46,7 +49,7 @@ test.describe('A11y', () => {
4649
test('Dashboard', async ({}, testInfo) => {
4750
await page.goto(postsUrl.admin)
4851

49-
await page.locator('.dashboard').waitFor()
52+
await expect(page.locator('.dashboard')).toBeVisible()
5053

5154
const accessibilityScanResults = await runAxeScan({ page, testInfo })
5255

@@ -68,7 +71,7 @@ test.describe('A11y', () => {
6871
test.fixme('Account page', async ({}, testInfo) => {
6972
await page.goto(postsUrl.account)
7073

71-
await page.locator('.auth-fields').waitFor()
74+
await expect(page.locator('.auth-fields')).toBeVisible()
7275

7376
const accessibilityScanResults = await runAxeScan({
7477
page,
@@ -83,7 +86,7 @@ test.describe('A11y', () => {
8386
test.fixme('list view', async ({}, testInfo) => {
8487
await page.goto(postsUrl.list)
8588

86-
await page.locator('.list-controls').waitFor()
89+
await expect(page.locator('.list-controls')).toBeVisible()
8790

8891
const accessibilityScanResults = await runAxeScan({ page, testInfo })
8992

@@ -93,7 +96,7 @@ test.describe('A11y', () => {
9396
test.fixme('create view', async ({}, testInfo) => {
9497
await page.goto(postsUrl.create)
9598

96-
await page.locator('#field-title').waitFor()
99+
await expect(page.locator('#field-title')).toBeVisible()
97100

98101
const accessibilityScanResults = await runAxeScan({ page, testInfo })
99102

@@ -104,7 +107,7 @@ test.describe('A11y', () => {
104107
await page.goto(postsUrl.list)
105108

106109
await page.locator('.table a').first().click()
107-
await page.locator('#field-title').waitFor()
110+
await expect(page.locator('#field-title')).toBeVisible()
108111

109112
const accessibilityScanResults = await runAxeScan({ page, testInfo })
110113

@@ -116,7 +119,7 @@ test.describe('A11y', () => {
116119
test('list view', async ({}, testInfo) => {
117120
await page.goto(mediaUrl.list)
118121

119-
await page.locator('.list-controls').waitFor()
122+
await expect(page.locator('.list-controls')).toBeVisible()
120123

121124
const accessibilityScanResults = await runAxeScan({ page, testInfo })
122125

@@ -126,7 +129,7 @@ test.describe('A11y', () => {
126129
test.fixme('create view', async ({}, testInfo) => {
127130
await page.goto(mediaUrl.create)
128131

129-
await page.locator('.file-field').first().waitFor()
132+
await expect(page.locator('.file-field').first()).toBeVisible()
130133

131134
const accessibilityScanResults = await runAxeScan({ page, testInfo })
132135

@@ -138,176 +141,131 @@ test.describe('A11y', () => {
138141
test('Dashboard - should have visible focus indicators', async ({}, testInfo) => {
139142
await page.goto(postsUrl.admin)
140143

141-
await page.locator('.dashboard').waitFor()
144+
await expect(page.locator('.dashboard')).toBeVisible()
142145

143-
const result = await checkFocusIndicators({
146+
await assertAllElementsHaveFocusIndicators({
144147
page,
145148
testInfo,
146149
verbose: false,
147150
selector: '.dashboard',
148151
})
149-
150-
expect.soft(result.totalFocusableElements).toBeGreaterThan(0)
151-
expect.soft(result.elementsWithoutIndicators).toBe(0)
152152
})
153153

154154
test('Posts create view - fields should have visible focus indicators', async ({}, testInfo) => {
155155
await page.goto(postsUrl.create)
156156

157-
await page.locator('#field-title').waitFor()
157+
await expect(page.locator('#field-title')).toBeVisible()
158158

159-
const result = await checkFocusIndicators({
159+
await assertAllElementsHaveFocusIndicators({
160160
page,
161161
selector: 'main.collection-edit',
162162
testInfo,
163163
})
164-
165-
expect.soft(result.totalFocusableElements).toBeGreaterThan(0)
166-
expect.soft(result.elementsWithoutIndicators).toBe(0)
167164
})
168165

169-
test.fixme(
170-
'Posts create view - breadcrumbs should have visible focus indicators',
171-
async ({}, testInfo) => {
172-
await page.goto(postsUrl.create)
173-
174-
await page.locator('#field-title').waitFor()
175-
176-
const result = await checkFocusIndicators({
177-
page,
178-
selector: '.app-header__controls-wrapper',
179-
testInfo,
180-
})
166+
test.fixme('Posts create view - breadcrumbs should have visible focus indicators', async ({}, testInfo) => {
167+
await page.goto(postsUrl.create)
181168

182-
expect.soft(result.totalFocusableElements).toBeGreaterThan(0)
183-
expect.soft(result.elementsWithoutIndicators).toBe(0)
184-
},
185-
)
169+
await expect(page.locator('#field-title')).toBeVisible()
186170

187-
test.fixme(
188-
'Navigation sidebar - should have visible focus indicators',
189-
async ({}, testInfo) => {
190-
await page.goto(postsUrl.admin)
171+
await assertAllElementsHaveFocusIndicators({
172+
page,
173+
selector: '.app-header__controls-wrapper',
174+
testInfo,
175+
})
176+
})
191177

192-
await page.locator('.nav').waitFor()
178+
test.fixme('Navigation sidebar - should have visible focus indicators', async ({}, testInfo) => {
179+
await page.goto(postsUrl.admin)
193180

194-
await openNav(page)
181+
await expect(page.locator('.nav')).toBeVisible()
195182

196-
const result = await checkFocusIndicators({
197-
page,
198-
selector: '.nav',
199-
testInfo,
200-
})
183+
await openNav(page)
201184

202-
expect.soft(result.totalFocusableElements).toBeGreaterThan(0)
203-
expect.soft(result.elementsWithoutIndicators).toBe(0)
204-
},
205-
)
185+
await assertAllElementsHaveFocusIndicators({
186+
page,
187+
selector: '.nav',
188+
testInfo,
189+
})
190+
})
206191

207192
test.fixme('Account page - should have visible focus indicators', async ({}, testInfo) => {
208193
await page.goto(postsUrl.account)
209194

210-
await page.locator('.auth-fields').waitFor()
195+
await expect(page.locator('.auth-fields')).toBeVisible()
211196

212-
const result = await checkFocusIndicators({
197+
await assertAllElementsHaveFocusIndicators({
213198
page,
214199
testInfo,
215200
verbose: false,
216201
})
217-
218-
expect.soft(result.totalFocusableElements).toBeGreaterThan(0)
219-
expect.soft(result.elementsWithoutIndicators).toBe(0)
220202
})
221203
})
222204

223205
test.describe('WCAG 2.1 - Reflow (320px width)', () => {
224206
test('Dashboard - should not have horizontal overflow at 320px', async ({}, testInfo) => {
225207
await page.setViewportSize({ width: 320, height: 568 })
226208
await page.goto(postsUrl.admin)
227-
await page.locator('.dashboard').waitFor()
228-
229-
const result = await checkHorizontalOverflow(page, testInfo)
209+
await expect(page.locator('.dashboard')).toBeVisible()
230210

231-
expect(result.hasHorizontalOverflow).toBe(false)
232-
expect(result.overflowingElements.length).toBe(0)
211+
await assertNoHorizontalOverflow(page, testInfo)
233212
})
234213

235214
test('Account page - should not have horizontal overflow at 320px', async ({}, testInfo) => {
236215
await page.setViewportSize({ width: 320, height: 568 })
237216
await page.goto(postsUrl.account)
238-
await page.locator('.auth-fields').waitFor()
239-
240-
const result = await checkHorizontalOverflow(page, testInfo)
217+
await expect(page.locator('.auth-fields')).toBeVisible()
241218

242-
expect(result.hasHorizontalOverflow).toBe(false)
243-
expect(result.overflowingElements.length).toBe(0)
219+
await assertNoHorizontalOverflow(page, testInfo)
244220
})
245221

246222
test('Posts list view - should not have horizontal overflow at 320px', async ({}, testInfo) => {
247223
await page.setViewportSize({ width: 320, height: 568 })
248224
await page.goto(postsUrl.list)
249-
await page.locator('.collection-list').waitFor()
225+
await expect(page.locator('.collection-list')).toBeVisible()
250226

251-
const result = await checkHorizontalOverflow(page, testInfo)
252-
253-
expect(result.hasHorizontalOverflow).toBe(false)
254-
expect(result.overflowingElements.length).toBe(0)
227+
await assertNoHorizontalOverflow(page, testInfo)
255228
})
256229

257230
test('Posts create view - should not have horizontal overflow at 320px', async ({}, testInfo) => {
258231
await page.setViewportSize({ width: 320, height: 568 })
259232
await page.goto(postsUrl.create)
260-
await page.locator('#field-title').waitFor()
261-
262-
const result = await checkHorizontalOverflow(page, testInfo)
233+
await expect(page.locator('#field-title')).toBeVisible()
263234

264-
expect(result.hasHorizontalOverflow).toBe(false)
265-
expect(result.overflowingElements.length).toBe(0)
235+
await assertNoHorizontalOverflow(page, testInfo)
266236
})
267237

268238
test('Posts edit view - should not have horizontal overflow at 320px', async ({}, testInfo) => {
269239
await page.setViewportSize({ width: 320, height: 568 })
270240
await page.goto(postsUrl.list)
271241
await page.locator('.table a').first().click()
272-
await page.locator('#field-title').waitFor()
273-
274-
const result = await checkHorizontalOverflow(page, testInfo)
242+
await expect(page.locator('#field-title')).toBeVisible()
275243

276-
expect(result.hasHorizontalOverflow).toBe(false)
277-
expect(result.overflowingElements.length).toBe(0)
244+
await assertNoHorizontalOverflow(page, testInfo)
278245
})
279246

280247
test('Media list view - should not have horizontal overflow at 320px', async ({}, testInfo) => {
281248
await page.setViewportSize({ width: 320, height: 568 })
282249
await page.goto(mediaUrl.list)
283-
await page.locator('.list-controls').waitFor()
250+
await expect(page.locator('.list-controls')).toBeVisible()
284251

285-
const result = await checkHorizontalOverflow(page, testInfo)
286-
287-
expect(result.hasHorizontalOverflow).toBe(false)
288-
expect(result.overflowingElements.length).toBe(0)
252+
await assertNoHorizontalOverflow(page, testInfo)
289253
})
290254

291255
test('Media create view - should not have horizontal overflow at 320px', async ({}, testInfo) => {
292256
await page.setViewportSize({ width: 320, height: 568 })
293257
await page.goto(mediaUrl.create)
294-
await page.locator('.file-field').first().waitFor()
295-
296-
const result = await checkHorizontalOverflow(page, testInfo)
258+
await expect(page.locator('.file-field').first()).toBeVisible()
297259

298-
expect(result.hasHorizontalOverflow).toBe(false)
299-
expect(result.overflowingElements.length).toBe(0)
260+
await assertNoHorizontalOverflow(page, testInfo)
300261
})
301262

302263
test('Navigation sidebar - should not have horizontal overflow at 320px', async ({}, testInfo) => {
303264
await page.setViewportSize({ width: 320, height: 568 })
304265
await page.goto(postsUrl.admin)
305-
await page.locator('.nav').waitFor()
306-
307-
const result = await checkHorizontalOverflow(page, testInfo)
266+
await expect(page.locator('.nav')).toBeVisible()
308267

309-
expect(result.hasHorizontalOverflow).toBe(false)
310-
expect(result.overflowingElements.length).toBe(0)
268+
await assertNoHorizontalOverflow(page, testInfo)
311269
})
312270
})
313271

@@ -323,7 +281,7 @@ test.describe('A11y', () => {
323281
for (const { level, scale } of zoomLevels) {
324282
test(`should be usable at ${level}% zoom`, async ({}, testInfo) => {
325283
await page.goto(postsUrl.admin)
326-
await page.locator('.dashboard').waitFor()
284+
await expect(page.locator('.dashboard')).toBeVisible()
327285

328286
// Simulate zoom by setting device scale factor
329287
await page.evaluate((zoomScale) => {
@@ -335,6 +293,7 @@ test.describe('A11y', () => {
335293

336294
// At high zoom levels, some horizontal overflow might be acceptable
337295
// but we should at least verify the page is still functional
296+
// eslint-disable-next-line playwright/no-conditional-in-test
338297
if (level <= 200) {
339298
// At 200% or less, should not have overflow
340299
expect(overflowResult.hasHorizontalOverflow).toBe(false)
@@ -351,7 +310,7 @@ test.describe('A11y', () => {
351310
for (const { level, scale } of zoomLevels) {
352311
test(`should be usable at ${level}% zoom`, async ({}, testInfo) => {
353312
await page.goto(postsUrl.create)
354-
await page.locator('#field-title').waitFor()
313+
await expect(page.locator('#field-title')).toBeVisible()
355314

356315
await page.evaluate((zoomScale) => {
357316
document.body.style.zoom = String(zoomScale)
@@ -378,7 +337,7 @@ test.describe('A11y', () => {
378337
for (const { level, scale } of zoomLevels) {
379338
test(`should be usable at ${level}% zoom`, async ({}, testInfo) => {
380339
await page.goto(postsUrl.list)
381-
await page.locator('.list-controls').waitFor()
340+
await expect(page.locator('.list-controls')).toBeVisible()
382341

383342
await page.evaluate((zoomScale) => {
384343
document.body.style.zoom = String(zoomScale)
@@ -405,7 +364,7 @@ test.describe('A11y', () => {
405364
for (const { level, scale } of zoomLevels) {
406365
test.fixme(`should be usable at ${level}% zoom`, async ({}, testInfo) => {
407366
await page.goto(postsUrl.account)
408-
await page.locator('.auth-fields').waitFor()
367+
await expect(page.locator('.auth-fields')).toBeVisible()
409368

410369
await page.evaluate((zoomScale) => {
411370
document.body.style.zoom = String(zoomScale)
@@ -427,7 +386,7 @@ test.describe('A11y', () => {
427386
for (const { level, scale } of zoomLevels) {
428387
test(`Media list view should be usable at ${level}% zoom`, async ({}, testInfo) => {
429388
await page.goto(mediaUrl.list)
430-
await page.locator('.collection-list').waitFor()
389+
await expect(page.locator('.collection-list')).toBeVisible()
431390

432391
await page.evaluate((zoomScale) => {
433392
document.body.style.zoom = String(zoomScale)
@@ -449,7 +408,7 @@ test.describe('A11y', () => {
449408
for (const { level, scale } of zoomLevels) {
450409
test(`should be usable at ${level}% zoom`, async ({}, testInfo) => {
451410
await page.goto(postsUrl.admin)
452-
await page.locator('.nav').waitFor()
411+
await expect(page.locator('.nav')).toBeVisible()
453412

454413
await page.evaluate((zoomScale) => {
455414
document.body.style.zoom = String(zoomScale)

test/a11y/payload-types.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ export interface Config {
8888
db: {
8989
defaultIDType: string;
9090
};
91+
fallbackLocale: null;
9192
globals: {
9293
menu: Menu;
9394
};
@@ -235,10 +236,6 @@ export interface PayloadLockedDocument {
235236
relationTo: 'media';
236237
value: string | Media;
237238
} | null)
238-
| ({
239-
relationTo: 'payload-kv';
240-
value: string | PayloadKv;
241-
} | null)
242239
| ({
243240
relationTo: 'users';
244241
value: string | User;

test/eslint.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ export const testEslintConfig = [
7777
'assertURLParams',
7878
'uploadImage',
7979
'getRowByCellValueAndAssert',
80+
'assertAllElementsHaveFocusIndicators',
81+
'assertNoHorizontalOverflow',
8082
],
8183
},
8284
],

0 commit comments

Comments
 (0)