Skip to main content

Advanced Usage

Laika exposes a few capabilities that are easy to miss if you only start from the basic intercept(...).mockResult(...) flow.

Match more precisely

Interceptors can match on:

  • operationName
  • operation
  • variables
  • clientName
  • feature
  • a custom matcher function
const interceptor = laika.intercept({
clientName: 'support-web',
feature: 'ticket-sidebar',
operationName: 'GetTicket',
variables: { id: '123' },
})

If another link tags the operation context with feature, you can use that to scope logging and mocking more narrowly.

Every interceptor also records its matched variables in interceptor.calls, similar to jest.fn().mock.calls.

Queue responses and choose how long they live

Use mockResultOnce() when a response should be consumed a limited number of times:

const interceptor = laika.intercept({ operationName: 'GetUsers' })

interceptor
.mockResultOnce({ result: { data: { users: [{ id: '1' }] } } })
.mockResultOnce({ result: { data: { users: [{ id: '2' }] } } })
.mockResult({ result: { data: { users: [] } } })

Use:

  • mockReset() to clear the current mock configuration but keep the interceptor registered
  • mockRestore() to remove just that interceptor
  • mockRestoreAll() to remove every interceptor created by the Laika instance

Wait for subscriptions and mount events

You can wait for the next matching operation:

const interceptor = laika.intercept({ operationName: 'GetUsers' })

await interceptor.waitForNextSubscription()

Or attach a lifecycle callback:

const interceptor = laika.intercept({ operationName: 'GetUsers' })

interceptor.onSubscribe(({ operation, observer }) => {
if (operation.variables.preview) {
observer.next?.({
data: {
users: [],
},
})
observer.complete?.()
}
})

Return a cleanup function from onSubscribe() if the interceptor needs to dispose any test-local state when the observer disconnects.

Turn off network fallback

By default, unmatched queries and mutations can still fall through to the next Apollo link. If you want a request to stay in a loading state until your test explicitly responds, disable that fallback:

const interceptor = laika.intercept({ operationName: 'GetUsers' })

interceptor.disableNetworkFallback()

interceptor.onSubscribe(({ observer }) => {
observer.next?.({
data: {
users: [{ id: '1', name: 'Mouse' }],
},
})
observer.complete?.()
})

Use allowNetworkFallback() to restore the default passthrough behavior.

Modify real backend responses without fully mocking them

modifyRemote() is the higher-level API for the "let the request go through, then adjust the result" pattern:

laika.modifyRemote({ operationName: 'GetTicket' }, (result) => ({
...result,
data: {
...result.data,
injectedByLaika: true,
},
}))

This is useful for fuzzing edge cases while still exercising the real backend response shape.

Customize the global singleton

createGlobalLaikaLink() and createLazyLoadableLaikaLink() support a few options that are easy to overlook:

createLazyLoadableLaikaLink({
clientName: 'support-web',
globalPropertyName: 'supportLaika',
startLoggingImmediately: true,
onLaikaReady: (laika) => {
laika.log.startRecording('opening a ticket')
},
})

This changes the singleton name from window.laika to window.supportLaika.

Browser tests can hook into the same lifecycle with:

window.supportLaikaReadyCallbacks = window.supportLaikaReadyCallbacks ?? []
window.supportLaikaReadyCallbacks.push((laika) => {
laika.intercept({ operationName: 'GetTicket' })
})

See Usage in Playwright and Usage in Cypress for the browser-runner patterns built on top of that hook.