Playwright Library and Advanced Browser Control
By Syncfusion®, Inc. · Last updated
Playwright Test versus the Playwright Library, writing automation scripts with chromium.launch(), launch options, persistent contexts and connecting to running browsers, PDFs and full-page screenshots, controlling time with page.clock, running code in the page with evaluate, addInitScript and exposeFunction, waiting for page conditions with waitForFunction, and CDP sessions.
Playwright Library and Advanced Browser Control
- So far, you used Playwright Test: the test runner with
test,expect, fixtures and reports. - Under it is the Playwright Library: the part that drives browsers. You can use it alone, to write scripts that are not tests.
- This lesson also covers tools for unusual jobs: controlling time, running your own code in the page, and talking to the browser at a lower level.
Simple lesson: Playwright Test is for testing. The Library is for any browser automation. Both share the same page API.
A Real-World Example
- The sale banner shows a countdown to midnight. Its text changes every moment, so how can a test check it?
page.clock.setFixedTime()stops the page's clock at a chosen moment. Now the text is always the same.
- The page's
Datenow returns 20:00 every time. Timers still run, but the time they read never moves.
What You Will Learn
- Playwright Test versus the Playwright Library.
- Writing automation scripts: launch, context, page, close.
- Launch options, persistent profiles and connecting to a running browser.
- Controlling time with
page.clock. - Running code in the page:
evaluate,addInitScript,exposeFunctionandwaitForFunction. - CDP sessions and other low-level tools.
Playwright Test or the Library
Two Packages
| Playwright Test | Playwright Library | |
|---|---|---|
| Package | @playwright/test | playwright |
| You get | test, expect, fixtures, config, reporters, retries, parallel workers | chromium, firefox, webkit, and the browser API |
| You manage | Nothing, the runner opens and closes browsers | Launching, contexts, closing, errors |
| Use it for | Testing web apps | Scraping, PDFs, screenshots, bots, custom tools |
- Both use the same
Page,LocatorandBrowserContextAPIs. Everything you learned works in both. - Playwright Test includes the Library, so test projects need only
@playwright/test.
A Library Script
- A script launches a browser, makes a context and a page, does its job, and closes the browser.
- Run it with Node.js:
node save-prices.mjs. There is notest()and noexpect().
// save-prices.ts
import { chromium } from 'playwright';
const browser = await chromium.launch();
const context = await browser.newContext({ locale: 'en-IN' });
const page = await context.newPage();
await page.goto('https://practice.devtutorials.ai/products');
const names = await page.getByTestId('product-card').getByRole('heading').allTextContents();
const prices = await page.locator('.price').allTextContents();
console.log(names.map((name, i) => `${name}: ${prices[i]}`).join('\n'));
await page.pdf({ path: 'products.pdf', format: 'A4' }); // Chromium only
await page.screenshot({ path: 'products.png', fullPage: true });
await browser.close();
- Always close the browser, even after an error. Wrap the work in
try { ... } finally { await browser.close(); }. page.pdf()works only in Chromium, in headless mode.
The lab runs Playwright Test only, so Library scripts on this page are plain code. Copy them into a project with npm install playwright and run them with Node.js.
Simple lesson: Library scripts do the setup and cleanup the test runner does for you.
Launching Browsers
Launch Options
chromium.launch({ headless: false })shows the window.slowMo: 250waits after each action.channel: 'chrome'orchannel: 'msedge'uses the installed Google Chrome or Microsoft Edge.- In Playwright Test, the same options go in
use: { launchOptions: { ... } }oruse: { channel: 'chrome' }.
Persistent Contexts
- Normal contexts are like incognito windows: empty every time.
chromium.launchPersistentContext('./profile')keeps cookies, storage and extensions in a folder, like a real user profile.- Use it for browser extensions, or for tools that must stay logged in between runs.
const context = await chromium.launchPersistentContext('./my-profile', {
headless: false,
args: ['--load-extension=./my-extension'],
});
const page = context.pages()[0] ?? await context.newPage();
Connecting to a Browser That Is Already Running
chromium.connectOverCDP('http://localhost:9222')attaches to a Chrome started with--remote-debugging-port=9222.browserType.connect(wsEndpoint)connects to a Playwright browser server, for example a cloud browser service.- Remote browsers let a small CI machine drive many browsers somewhere else.
Controlling Time
Why Fake Time
- Pages show clocks, countdowns, "posted 5 minutes ago" text and session timeouts.
- Waiting for real time is slow, and the text changes on every run.
page.clockreplaces the page'sDate,setTimeoutandsetIntervalwith a clock you control.
The Clock Methods
setFixedTime(time):Datealways returns this time. Timers still run.install({ time }): start a fake clock at this time. Time flows, but you can move it.pauseAt(time): jump to a time and stop there.fastForward(ticks): jump ahead. Each due timer fires once. Ticks are milliseconds, or text like'30:00'.runFor(ticks): move ahead step by step, firing every timer on the way.resume(): let time flow again. Call clock methods beforegoto()when the page reads the time as it loads.
Animation: a timeline from 20:00 to 21:30. pauseAt jumps to 21:00 and stops. fastForward jumps 30 minutes to 21:30. The countdown text changes from 04:00:00 to 03:00:00 to 02:30:00
20:00 21:00 21:30
Countdown: 04:00:00 pauseAt(21:00) → 03:00:00 fastForward('30:00') → 02:30:00
The page's time moves only when the test says so.
With a fake clock, a test can jump hours ahead in milliseconds.
page.clock changes time only inside the page. Your test code and expect timeouts still use real time. Check the page's state with web-first assertions, as above.
Simple lesson: setFixedTime freezes the date. install plus pauseAt, fastForward and runFor move it.
Running Code in the Page
page.evaluate()
page.evaluate(fn, arg)runs a function inside the page and returns the result to the test.- The function cannot see your test's variables. Pass values as the second argument.
- Results must be plain data: strings, numbers, arrays and objects. DOM elements cannot come back.
- Prefer locators and assertions for what users see. Use
evaluatefor data users cannot see, like storage or app state.
addInitScript()
page.addInitScript(fn)runs code before the page's own scripts, on every page load.- Use it to set up state, for example a cart in local storage, a feature flag, or a fake API.
exposeFunction() and waitForFunction()
page.exposeFunction(name, fn)adds a function to the page'swindowthat runs in your test. The page can call back into Node.js, for example to hash data or read a file.page.waitForFunction(fn)waits until a function in the page returns a truthy value. Use it for conditions no locator can describe.
Simple lesson: evaluate reads the page, addInitScript prepares it, exposeFunction lets it call back.
Lower-Level Tools
CDP Sessions
- Chromium speaks the Chrome DevTools Protocol (CDP). It can do things Playwright has no API for, such as CPU throttling.
context.newCDPSession(page)opens a session. It works only in Chromium.
test('slow CPU', async ({ page }) => {
const client = await page.context().newCDPSession(page);
await client.send('Emulation.setCPUThrottlingRate', { rate: 4 });
await page.goto('/products');
// ... measure how the page behaves on a slow phone
});
More Places Playwright Runs
- Electron desktop apps:
_electron.launch()fromplaywright. - Android:
_androiddrives Chrome on Android devices (experimental). - Other languages: Playwright also has Python, Java and .NET versions with the same API names.
Reach for low-level tools last. Locators, web-first assertions, routes and the clock cover almost every test. Lower-level code is harder to read and often works in one browser only.
Simple lesson: CDP and friends exist for rare needs. Most tests never need them.
Try It in Your Project
- These steps take the lesson from the lab to your own app.
- Find one page in your app that shows dates or countdowns and test it with
page.clock. - Use
addInitScript()to set feature flags or test data before load. - Write one small Library script that takes screenshots of your key pages.
Simple lesson: use one idea from this lesson on your own app this week.
TL;DR
@playwright/testis for testing.playwright(the Library) is for scripts. Both share the page API.- Scripts launch a browser, make a context and page, and must close the browser.
launchPersistentContextkeeps a profile.connectOverCDPattaches to a running Chrome.page.clockfakes time:setFixedTime,install,pauseAt,fastForward,runFor.evaluateruns code in the page.addInitScriptruns before page scripts.exposeFunctioncalls back to the test.- CDP sessions, Electron and Android cover rare needs.
Common mistakes
| Mistake | What happens | Fix |
|---|---|---|
| A Library script that never closes the browser | The script hangs or leaks processes | try { ... } finally { await browser.close() } |
Using a test variable inside evaluate | ReferenceError in the page | Pass it as the second argument |
Returning a DOM element from evaluate | The result is empty or an error | Return plain data, or use a locator |
| Setting the clock after the page read the time | The page shows the real time | Call clock methods before goto() |
| Using CDP for normal checks | Chromium-only, hard-to-read tests | Use locators and assertions |
Check Yourself
Exercises
What to Remember
- Playwright Test for tests, the Library for scripts. Same page API.
- Scripts must launch and close browsers themselves.
page.clockmakes time-based pages testable.evaluate,addInitScriptandexposeFunctionreach inside the page.- Low-level tools are for rare needs.
Next: AI-Assisted Testing with Agents, MCP, and CLI