-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Description
I am working on an SPA that I recently added a service worker to, which pre-caches the application shell using a cache-first strategy and caches navigation requests using a network -first strategy.
The SPA contains some logic that displays a "A new version is available!" banner with a "Reload" button at the top of the page when it detects a "waiting" service worker. I would like to write an end-to-end test for this logic and the logic behind the "Reload" button but cannot figure out how to trigger a service worker update in Playwright.
The approach that I have been playing with is modifying the service worker response after the service worker has been installed and a page reload has been triggered. I can modify the response and its response headers but it is not triggering an update event in the browser.
Here is the test code:
test("shows an update button when the service worker file has changed", async ({
page,
context
}, testInfo) => {
const newScriptBytes = "/*test*/"
const timeout = testInfo.timeout
await page.goto("/")
await waitForReadyServiceWorker({ context, page, timeout })
// change the bytes of all additional service worker requests
await page.route("/service-worker.js", async route => {
const serverResponse = await page.request.fetch(serviceWorkerUrl, {
ignoreHTTPSErrors: true
})
let serviceWorkerScript = await serverResponse.text()
serviceWorkerScript = serviceWorkerScript.concat(newScriptBytes)
await route.fulfill({
response: serverResponse,
body: serviceWorkerScript,
headers: {
...serverResponse.headers(),
"Etag": "v1",
"Last-Modified": new Date().toUTCString(),
"Content-Length": serviceWorkerScript.length.toString()
}
})
})
await page.reload()
await expect(
page.locator(`text=A new version is available!`)
).toBeVisible()
})
export async function waitForReadyServiceWorker(args: {
context: BrowserContext
page: Page
timeout?: number
}): Promise<Worker | undefined> {
const timeoutId = createOperationTimeout(args.timeout)
const url = await args.page.evaluate(async () => {
const readySw = await navigator.serviceWorker.ready
return readySw.active.scriptURL
})
clearTimeout(timeoutId)
return args.context.serviceWorkers().find(worker => worker.url() === url)
}Here is the initial service worker response
Here is the modified service worker response

