Usage in Playwright
Use Playwright when you want the real browser runtime but still need direct control over GraphQL traffic. The key constraint is timing: if the app fires queries during the initial render, setup after page.goto() is already too late.
Make sure the app actually enables Laika
If your app only loads Laika behind a query parameter or a test flag, navigate with that flag enabled.
For patterns such as ?laika=1 or ?e2e=true, see Conditionally loading Laika.
Install interceptors before navigation
Use page.addInitScript() or browserContext.addInitScript() to register callbacks on window.laikaReadyCallbacks.
Laika runs every callback in that array when createGlobalLaikaLink() or createLazyLoadableLaikaLink() creates the global singleton.
import { expect, test } from '@playwright/test'
test.beforeEach(async ({ page }) => {
await page.addInitScript(
(mockedData) => {
window.__laikaTestState = {}
window.laikaReadyCallbacks = window.laikaReadyCallbacks ?? []
window.laikaReadyCallbacks.push((laika) => {
const getDataInterceptor = laika.intercept({
clientName: 'fe-client',
operationName: 'GetData',
})
getDataInterceptor.mockResult({
result: {
data: mockedData,
},
})
window.__laikaTestState.getDataInterceptor = getDataInterceptor
})
},
{
testData: [{ id: '1', name: 'Mouse' }],
},
)
})
test.afterEach(async ({ page }) => {
await page.evaluate(() => {
window.laika?.mockRestoreAll()
})
})
test('shows mocked data from the first query', async ({ page }) => {
await page.goto('http://localhost:4200/login?laika=1')
await expect(page.getByText('Mouse')).toBeVisible()
})
This keeps the mocking logic in the test harness instead of moving it into the application through onLaikaReady.
Update mocks after the page has loaded
If you store interceptors on window, you can reconfigure them later in the same test:
await page.evaluate(
(nextData) => {
window.__laikaTestState.getDataInterceptor.mockReset().mockResult({
result: {
data: nextData,
},
})
},
{
testData: [{ id: '2', name: 'Bamboo' }],
},
)
Assert which variables were used
Each interceptor keeps a calls array with every set of variables that matched it:
const calls = await page.evaluate(() => {
return window.__laikaTestState.getDataInterceptor.calls
})
expect(calls).toEqual([{ page: 1 }])
When waiting for window.laika is enough
If you do not need to affect requests fired during app boot, a simpler pattern is fine:
await page.goto('http://localhost:4200/login?laika=1')
await page.waitForFunction(() => Boolean(window.laika))
From there you can create interceptors through page.evaluate() or page.evaluateHandle().
For initial requests, keep using addInitScript() and ready callbacks.
Custom global property names
If your app uses globalPropertyName: 'myLaika', register callbacks on window.myLaikaReadyCallbacks instead of window.laikaReadyCallbacks.
The callback suffix always matches the configured global property name:
await page.addInitScript(() => {
window.myLaikaReadyCallbacks = window.myLaikaReadyCallbacks ?? []
window.myLaikaReadyCallbacks.push((laika) => {
laika.intercept({ clientName: 'fe-client', operationName: 'GetData' })
})
})
Reset state between tests
If Playwright reuses the page between tests, clear interceptors in afterEach:
test.afterEach(async ({ page }) => {
await page.evaluate(() => {
window.laika?.mockRestoreAll()
})
})
For more on cleanup behavior and active subscriptions, see Resetting Between Tests.