fix(e2e): match the modal OK button exactly so a random suffix cannot collide (#253)
Linting / lint (pull_request) Successful in 2m25s
SonarQube Analysis / sonarqube (pull_request) Successful in 26m29s

Playwright matches an accessible name case-insensitively and as a substring unless exact is passed, so `getByRole('button', { name: 'OK' })` matched any button whose name merely contained "ok".

Caught on a full serial run of main:

    strict mode violation: getByRole('button', { name: 'OK' }) resolved to 2 elements:
        1) aka getByRole('button', { name: 'Freed rmtiq9okg22k9e' })
        2) aka getByRole('button', { name: 'OK', exact: true })

A leftover "Freed …" toast was still on screen and the random base36 suffix happened to contain "ok". Roughly one suffix in a few hundred does, which is the profile of a test that fails occasionally and reproduces for nobody.

This is not the contention #241 addressed. It reproduced with workers: 1, serially, on a fresh database — it needs only a stale toast and an unlucky suffix. It is at least part of what #245 skipped a real test to work around.

Also makes the taxonomy modal's OK and the sold-filter 'All' radio exact, the only other targets short enough to appear inside a random suffix. The dozen or so remaining short names are left alone deliberately: they would need a collision in deliberately-chosen test data rather than a random one, and substring matching is load-bearing in some of them.

Verified with two consecutive full e2e passes, 155 of 155 both times.

Closes #253

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-01 09:14:20 -05:00
co-authored by Claude Opus 5
parent 42412e8eeb
commit 74936e6d6a
3 changed files with 10 additions and 3 deletions
+6 -1
View File
@@ -27,7 +27,12 @@ export class AdminInventory {
this.price = page.getByLabel('Price (USD)');
this.category = page.getByLabel('Category', { exact: true });
this.saveButton = page.getByRole('button', { name: 'Save', exact: true });
this.confirmButton = page.getByRole('button', { name: 'OK' });
// exact, because Playwright matches an accessible name case-insensitively
// as a *substring* by default. Item names carry a random base36 suffix, and
// one containing "ok" — plus any leftover toast still on screen, such as
// "Freed rmtiq9okg22k9e" — makes this resolve to two buttons and fail as a
// strict mode violation. See #253.
this.confirmButton = page.getByRole('button', { name: 'OK', exact: true });
this.formDialog = page.getByRole('dialog');
this.tagPicker = page.getByText('Pick existing tags');
}
+2 -1
View File
@@ -21,7 +21,8 @@ export class AdminTaxonomy {
this.addCategoryButton = page.getByRole('button', { name: 'Add Category' });
this.addTagButton = page.getByRole('button', { name: 'Add Tag' });
this.name = page.getByLabel('Name');
this.confirmButton = page.getByRole('button', { name: 'OK' });
// Substring matching would collide with random suffixes. See #253.
this.confirmButton = page.getByRole('button', { name: 'OK', exact: true });
this.tagAddedNotice = page.getByText('Tag added');
}
+2 -1
View File
@@ -59,7 +59,8 @@ test.describe('Filtering the storefront by availability', () => {
await page.reload();
await expect(storefront.gridCell(NAMES.sold)).toBeVisible();
await expect(page.getByRole('radio', { name: 'All' })).toBeChecked();
// exact: 'All' is short enough to appear inside other labels. See #253.
await expect(page.getByRole('radio', { name: 'All', exact: true })).toBeChecked();
});
// Not sold is the default, so it is held as "no preference" rather than as an