Mock browser APIs
For browser APIs that Playwright doesn't have a dedicated method for — Battery, cookieEnabled, matchMedia — you inject a mock object before the page loads using page.addInitScript(). The script runs in the browser context, before any page JS, so the app never knows it's talking to a fake.
page.addInitScript — inject before the page loads
page.addInitScript() runs your callback in the browser context before any page JavaScript. The page loads, your mock is already in place — the app sees a fake navigator.getBattery and never questions it.
Our dashboard shows a battery warning banner when the device is below 20%. The Battery API isn't fully supported in all browsers and I can't drain a laptop to test it. So I mock it.
Read-only navigator properties
Some browser properties are read-only — direct assignment silently does nothing. navigator.cookieEnabled = false has zero effect. For configurable properties, use Object.defineProperty to override the getter.
I use this to test the 'cookies disabled' warning page. Our app shows a special error screen when cookies are blocked — which I can't reproduce naturally in a test.
Verify which browser APIs the page calls
Sometimes I need to check that the app called the right browser API in the right order. page.exposeFunction() bridges the browser context back to Node.js — the mock in the browser calls a function that appends to a log array in the test.
Simulate runtime API changes
Static mocks only test the initial state. To test that the UI reacts to changes — like the battery level dropping mid-session — I expose the mock object on window so the test can trigger updates via page.evaluate().