fix(07): WR-07 prove SW block via getRegistration, skip when SW unavailable
This commit is contained in:
@@ -38,19 +38,33 @@ test.describe('TEST-02 preconditions — auth bypass and SW block', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('no service-worker controller (serviceWorkers: block enforced)', async ({ page }) => {
|
test('no service-worker registration (serviceWorkers: block enforced)', async ({ page }) => {
|
||||||
await page.goto('/calendar')
|
await page.goto('/calendar')
|
||||||
|
|
||||||
// serviceWorkers: 'block' in playwright.config.ts prevents SW registration.
|
// serviceWorkers: 'block' in playwright.config.ts prevents SW registration.
|
||||||
// navigator.serviceWorker.controller is null when no SW is controlling the page.
|
//
|
||||||
// Note: navigator.serviceWorker may be undefined in some contexts (e.g. non-https),
|
// WR-07: asserting `navigator.serviceWorker.controller === null` is near-vacuous —
|
||||||
// but in the dev server context it is defined. Treat undefined as no-controller (safe).
|
// (a) on WebKit over plain http://localhost, `serviceWorker` is often *absent* from
|
||||||
const controller = await page.evaluate(() => {
|
// navigator (secure-context strictness), so the old guard returned null and the
|
||||||
if (typeof navigator === 'undefined') return null
|
// assertion passed without ever proving the block worked; and
|
||||||
if (!('serviceWorker' in navigator)) return null
|
// (b) `controller` is null on a first uncontrolled load even when SW *is* available,
|
||||||
return navigator.serviceWorker.controller
|
// regardless of the block setting.
|
||||||
})
|
// Instead probe getRegistration() — when SW is available and `block` is in effect, no
|
||||||
expect(controller, 'Service worker controller should be null (serviceWorkers:block enforced)').toBeNull()
|
// registration exists, so it resolves to undefined. Where `serviceWorker` is absent
|
||||||
|
// entirely (WebKit/http), skip rather than let an unavailable API masquerade as a pass.
|
||||||
|
const swAvailable = await page.evaluate(
|
||||||
|
() => typeof navigator !== 'undefined' && 'serviceWorker' in navigator,
|
||||||
|
)
|
||||||
|
test.skip(
|
||||||
|
!swAvailable,
|
||||||
|
'navigator.serviceWorker is unavailable in this context (e.g. WebKit over http://localhost) — block is unobservable here',
|
||||||
|
)
|
||||||
|
|
||||||
|
const registration = await page.evaluate(() => navigator.serviceWorker.getRegistration())
|
||||||
|
expect(
|
||||||
|
registration,
|
||||||
|
'No service worker should be registered (serviceWorkers:block enforced)',
|
||||||
|
).toBeUndefined()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -116,9 +130,9 @@ test.describe('Rule 5 — calendar error state (API mocked to 500)', () => {
|
|||||||
|
|
||||||
const retryBtn = page.getByRole('button', { name: 'Retry' })
|
const retryBtn = page.getByRole('button', { name: 'Retry' })
|
||||||
await expect(retryBtn).toBeVisible()
|
await expect(retryBtn).toBeVisible()
|
||||||
|
// No manual unroute (WR-05): Playwright gives each test a fresh page/context, so route
|
||||||
// Unroute so the mock does not leak to subsequent tests (T-07-11)
|
// handlers do not leak across tests. A trailing unroute also never runs if an `expect`
|
||||||
await page.unroute('/api/events*')
|
// above throws — it was misleading "cleanup" that guaranteed nothing.
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Retry button meets 44px touch-target minimum in error state (Rule 1)', async ({
|
test('Retry button meets 44px touch-target minimum in error state (Rule 1)', async ({
|
||||||
@@ -137,8 +151,7 @@ test.describe('Rule 5 — calendar error state (API mocked to 500)', () => {
|
|||||||
const box = await retryBtn.boundingBox()
|
const box = await retryBtn.boundingBox()
|
||||||
expect(box, 'Retry button bounding box must not be null').not.toBeNull()
|
expect(box, 'Retry button bounding box must not be null').not.toBeNull()
|
||||||
expect(box!.height, 'Retry button height must be ≥ 44px (Rule 1)').toBeGreaterThanOrEqual(44)
|
expect(box!.height, 'Retry button height must be ≥ 44px (Rule 1)').toBeGreaterThanOrEqual(44)
|
||||||
|
// No manual unroute (WR-05): per-test context isolation handles route cleanup.
|
||||||
await page.unroute('/api/events*')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test('no horizontal overflow in error state (Rule 2)', async ({ page }) => {
|
test('no horizontal overflow in error state (Rule 2)', async ({ page }) => {
|
||||||
@@ -160,7 +173,6 @@ test.describe('Rule 5 — calendar error state (API mocked to 500)', () => {
|
|||||||
overflow.scrollWidth,
|
overflow.scrollWidth,
|
||||||
`scrollWidth (${overflow.scrollWidth}) must be ≤ clientWidth (${overflow.clientWidth}) in error state`,
|
`scrollWidth (${overflow.scrollWidth}) must be ≤ clientWidth (${overflow.clientWidth}) in error state`,
|
||||||
).toBeLessThanOrEqual(overflow.clientWidth)
|
).toBeLessThanOrEqual(overflow.clientWidth)
|
||||||
|
// No manual unroute (WR-05): per-test context isolation handles route cleanup.
|
||||||
await page.unroute('/api/events*')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user