@enekesabel/playwright-lite
v0.9.3
Published
Playwright-compatible Page and Locator operations in the current browser document
Maintainers
Readme
playwright-lite
Use Playwright's Page and Locator APIs in the browser document you're already in.
playwright-lite runs inside your application, without launching a browser or connecting to a Playwright server.
import { createPage } from "@enekesabel/playwright-lite";
const page = createPage({
testIdAttribute: "data-test",
actionTimeout: 5_000,
navigationTimeout: 10_000,
});
await page.getByRole("textbox", { name: "Name" }).fill("Ada");
await page.getByRole("button", { name: "Save" }).click();Assertions
import { createPage, expect } from "@enekesabel/playwright-lite";
const page = createPage();
await expect(page.getByRole("button", { name: "Save" })).toBeEnabled();
await expect(page.locator(".notice")).toContainText("Saved");See Expect for the supported matchers and how a failed assertion surfaces.
Use cases
- Automate your own application in the browser you're using.
- Let an in-app assistant inspect and interact with the page.
- Build guided onboarding and automation flows.
- Run the supported parts of existing Playwright scripts inside your application.
Installation
playwright-lite is pre-1.0; APIs may change.
npm add @enekesabel/playwright-liteRuntime boundaries
- Current document only. No iframe traversal,
Frame, orFrameLocatorsupport. - Navigation ends execution.
goto(),reload(),goBack()andgoForward()can start navigation, but replacing the document destroys the JavaScript context running your script, so automation cannot continue across a full-page navigation or reload. - Synthetic input. Input events are not browser-trusted, so they grant no user activation and never apply CSS
:hoveror:active; native keyboard behavior such as cursor movement, deletion and focus traversal is not simulated. - No browser control. No browser launch, browser contexts, or browser-level control over network traffic, downloads, or other tabs.
- Content Security Policy applies. A string expression is evaluated with
eval, which the page's policy must allow; a function runs as the function it is, needing noevaland keeping its closure and its strict mode, where Playwright evaluates its source in the page as sloppy-mode code unless its body says otherwise; a function whose source does not re-parse, such as a bound or native function, runs too. - Closing keeps the document.
close()disposes thePageobject, not the document; see Closing a page. - Hidden tabs keep their pace. Actions and waits run at foreground speed in a background tab, with a few exceptions; see Hidden tabs.
- Shared JavaScript globals. The library runs alongside the page's own scripts, so some changes the page makes to built-in globals reach it; see Page globals.
Page functions playwright-lite replaces
| Functions | Replaced when you first… | Put back when… |
| -------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| window.fetch and XMLHttpRequest.prototype.open, setRequestHeader and send | add a request, response, requestfinished or requestfailed listener, call waitForRequest(), waitForResponse() or requests(), or start a networkidle wait | the last of those listeners is removed and no networkidle wait is running. After a requests() call they stay until that page is closed, so its log keeps filling. |
| Every console.* method Playwright's ConsoleMessage covers | add a console listener or call consoleMessages() | the last console listener is removed. After a consoleMessages() call they stay until that page is closed, so its log keeps filling. |
| window.alert, window.confirm and window.prompt | add a dialog listener | the last dialog listener is removed. |
| HTMLElement.prototype.click and HTMLInputElement.prototype.showPicker | add a filechooser listener | the last filechooser listener is removed. |
| Element.prototype.setPointerCapture, releasePointerCapture and hasPointerCapture, and moveBefore on Element, Document and DocumentFragment | press a mouse button or tap, with page.mouse, page.touchscreen or a pointer action | every mouse button is up, no element holds the mouse's capture and the tap has ended. |
Nothing is replaced until you subscribe, and a function the page replaced itself is never put back. Closing a page removes its listeners and waits, so the functions are put back once no open page still uses them. The network and console replacements forward every call unchanged. The dialog replacements pass each dialog to your listeners instead of showing it; see Dialog. The click() replacement forwards every call, reporting a file input it activates outside the document, and the showPicker() replacement reports a file input; see FileChooser. The pointer capture replacements forward every call and keep capture for playwright-lite's own pointers, which the browser does not know, including through a moveBefore() move; see Mouse.
Network idle
waitForLoadState("networkidle") and waitUntil: "networkidle" resolve once no fetch() or XMLHttpRequest call of the current document has been in flight for 500 ms.
- Only
fetch()andXMLHttpRequestcalls count as in flight. An image, script, stylesheet or other resource that finishes loading restarts the 500 ms, but one still loading never holds it. /favicon.icorequests are excluded, as in Playwright.- A call started before you subscribed is not seen, so a wait started while such a call is in flight can resolve before it ends.
- Calls from iframes, workers and other realms are not seen.
- Playwright documents
networkidleas "DISCOURAGED consider operation to be finished when there are no network connections for at least500ms. Don't use this method for testing, rely on web assertions to assess readiness instead."
- The end of each call and resource is noticed a few milliseconds late, so a wait can resolve slightly more than 500 ms after it.
Closing a page
close() and [Symbol.asyncDispose]() dispose the Page object createPage() returned, where Playwright closes the browser page. The document stays open, and another createPage() can still automate it.
closefires with the page, andisClosed()returnstruefrom then on.- Every pending call of the page, its locators, handles,
keyboard,mouse,touchscreen,localStorageandsessionStoragerejects withTarget page, context or browser has been closed, or with thereasonpassed toclose(). Every later async call rejects with the default message; sync members such asurl()andlocator()keep working, as in Playwright. - The functions the page exposed with
exposeFunction()orexposeBinding()are removed. runBeforeUnload: trueis rejected, since nothing is unloaded;falsecloses as usual.
waitForEvent()called afterclose()rejects at once, where Playwright waits until its timeout.- An assertion started after
close()fails with the closed error in its call log, where Playwright rejects with the closed error. - A call with an invalid argument after
close()rejects with the closed error, where Playwright reports the invalid argument. - A pending
request.response()orresponse.finished()is not interrupted byclose(), where Playwright rejects it. - Highlights added with
locator.highlight()stay in the document, where Playwright's go with the closed page. - The closed error has no call log, where Playwright appends the log of the interrupted call.
Page globals
Playwright runs its page scripts in an isolated world, which no change the page makes to a global reaches. playwright-lite shares the page's globals and keeps its own reference to Node, Element, NodeFilter, HTMLElement, Document, ShadowRoot, MutationObserver, Event, CustomEvent, EventTarget, Map, Set, WeakMap, WeakSet, Promise, Symbol, Error, TypeError, RegExp, Array, Object, URL, Date, JSON and Math. When you call createPage(), each is the page's current global while that is still a function (an object, for JSON and Math), and otherwise the one playwright-lite took when it loaded, as in Playwright's fallback for browsers without an isolated world. A page that deletes or replaces one of them after that does not affect playwright-lite.
- A global the page deleted before playwright-lite loaded stays missing: with
Nodedeleted,click()throwsCannot read properties of undefined (reading 'ELEMENT_NODE'). - A change to a built-in's methods or prototype reaches playwright-lite: after
Array.prototype.map = nullorJSON.stringify = null,click()throws aTypeError. - Every other global, such as
MouseEvent,KeyboardEvent,InputEvent,DataTransferorgetComputedStyle, is read from the page when used: withMouseEventdeleted,click()throws aTypeError. setTimeoutis read from the page too, since Playwright's fallback does not keep it either: with it deleted,click(),fill()andwaitForTimeout()throw aTypeError.
- A failing
expectassertion builds its message, andtoMatchAriaSnapshot()reads its template, with the page's current globals, so after the page deletesObject,ArrayorMaththey can throw thatReferenceErrorinstead.
Hidden tabs
Browsers stop animation frames and slow timers down in a background tab. Playwright's browser runs with that turned off, and playwright-lite keeps actions, waits and assertions at their foreground pace while the document is hidden.
- When the page's Content Security Policy blocks
blob:workers, each timed wait, such as adelayoption,waitForTimeout(), a polling interval or the stability check before an action, takes about a second while the tab is hidden. - When other programs keep the CPU busy, the operating system can give a hidden tab almost no CPU time, as macOS does, so an action, a wait or its timeout can run tens of seconds late until the tab gets time again. Playwright's browser keeps a hidden tab at foreground priority, so it competes for the CPU like a visible one.
- While the tab is hidden,
toBeInViewport()measures the element against the viewport and the ancestors that clip its overflow, and ignores clipping by CSS transforms,clip-pathandcontain: paint, or inside a closed shadow root the element is slotted into.
Navigation timeouts
waitForNavigation(), waitForURL(), waitForLoadState(), goBack(), goForward() and reload() time out with only page.<method>: Timeout <n>ms exceeded., where Playwright appends the navigation steps it logged, such as waiting for navigation until "load".
Compatibility
Targets Playwright 1.62.1. Statuses describe API compatibility within the runtime boundaries above. API links open Playwright's current documentation.
✅ Compatible · ⚠️ Partially compatible · ❌ Not implemented · 🚫 Intentionally excluded
Page
| API | Status | Notes |
| --------------------------------------------------------------------------------------------------------------- | :----: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| $ | ⚠️ | Returned ElementHandle methods and options differ; see ElementHandle and JSHandle. |
| $$ | ⚠️ | Returned ElementHandle methods and options differ; see ElementHandle and JSHandle. |
| $$eval | ✅ | |
| $eval | ✅ | |
| [Symbol.asyncDispose]() | ⚠️ | Closes as close() does, disposing only the Page object; see Closing a page. |
| addInitScript | 🚫 | Registers a script to run before the document's own scripts, which have already run by the time this adapter attaches. |
| addListener | ⚠️ | Fires only the supported events; other names never fire. |
| addLocatorHandler | ⚠️ | A handler that throws or rejects is logged instead of raised; see Events. |
| addScriptTag | ⚠️ | Rejects path, which reads the script from disk. Returned ElementHandle methods and options differ; see ElementHandle and JSHandle. |
| addStyleTag | ⚠️ | Rejects path, which reads the stylesheet from disk. Returned ElementHandle methods and options differ; see ElementHandle and JSHandle. |
| ariaSnapshot | ✅ | |
| bringToFront | 🚫 | Browser tab focus control is excluded. |
| cancelPickLocator | ❌ | |
| check | ✅ | |
| clearConsoleMessages | ✅ | |
| clearPageErrors | ✅ | |
| click | ⚠️ | The action does not wait for navigation. |
| clock | ❌ | |
| close | ⚠️ | Disposes the Page object, not the document, and rejects runBeforeUnload: true; see Closing a page. |
| consoleMessages | ⚠️ | filter: "all" and the default "since-navigation" return the same messages; see ConsoleMessage. |
| content | ✅ | |
| context | 🚫 | Refers to the owning browser context, which does not exist in this adapter. |
| coverage | 🚫 | Collecting code coverage requires the browser process. |
| dblclick | ✅ | |
| dispatchEvent | ✅ | |
| dragAndDrop | ⚠️ | Emulates HTML drag and drop in the document, without the browser's default drop handling; see Drag and drop. |
| emulateMedia | 🚫 | A document cannot change its own media type or prefers-color-scheme. |
| evaluate | ✅ | |
| evaluateHandle | ⚠️ | The returned handle previews differently; see ElementHandle and JSHandle. |
| exposeBinding | ⚠️ | Differs as exposeFunction does. The callback's source is { page, frame: page }, with no context, since this package has no BrowserContext. |
| exposeFunction | ⚠️ | dispose() leaves a value the page itself assigned to the property on window, where Playwright deletes it. Arguments and the result skip JSON.stringify(), so a page overriding Array.prototype.toJSON() does not make the call reject, as it does in Playwright. |
| fill | ✅ | |
| focus | ✅ | |
| frame | 🚫 | Iframe realms are outside the single-document boundary. |
| frameLocator | 🚫 | Iframe realms are outside the single-document boundary. |
| frames | 🚫 | Iframe realms are outside the single-document boundary. |
| getAttribute | ✅ | |
| getByAltText | ✅ | |
| getByLabel | ✅ | |
| getByPlaceholder | ✅ | |
| getByRole | ✅ | |
| getByTestId | ✅ | |
| getByText | ✅ | |
| getByTitle | ✅ | |
| goBack | ⚠️ | Needs the browser's Navigation API and rejects without it; returns no Response; resolves to null without navigating when the Navigation API does not list the adjacent entry (an entry of another origin, an entry beyond one, or any entry in an opaque-origin document such as a sandboxed frame), where Playwright navigates to it; networkidle resolves no sooner than 500 ms after the call, even when already idle. |
| goForward | ⚠️ | Needs the browser's Navigation API and rejects without it; returns no Response; resolves to null without navigating when the Navigation API does not list the adjacent entry (an entry of another origin, an entry beyond one, or any entry in an opaque-origin document such as a sandboxed frame), where Playwright navigates to it; networkidle resolves no sooner than 500 ms after the call, even when already idle. |
| goto | ⚠️ | Returns no Response (null only for same-document navigation, including one the page's own navigate listener intercepts where Playwright loads a new document; a navigation that listener cancels times out instead); relative URLs resolve against document.baseURI, with no baseURL; rejects referer and signal; networkidle resolves no sooner than 500 ms after the call, even when already idle. |
| hideHighlight | ✅ | |
| hover | ✅ | |
| innerHTML | ✅ | |
| innerText | ✅ | |
| inputValue | ✅ | |
| isChecked | ✅ | |
| isClosed | ⚠️ | Reports whether this Page object was closed; the document stays open; see Closing a page. |
| isDisabled | ✅ | |
| isEditable | ✅ | |
| isEnabled | ✅ | |
| isHidden | ✅ | |
| isVisible | ✅ | |
| keyboard | ✅ | |
| localStorage | ✅ | |
| locator | ✅ | |
| mainFrame | ⚠️ | Returns the same Page object, not a Frame. |
| mouse | ⚠️ | No text selection or context menu starts, and HTML drags are emulated; see Mouse. |
| off | ⚠️ | Only the supported events ever fire; other names are accepted. |
| on | ⚠️ | Fires only the supported events; other names never fire. |
| once | ⚠️ | Fires only the supported events; other names never fire. |
| opener | 🚫 | Refers to another page, outside the single-document boundary. |
| pageErrors | ⚠️ | filter: "all" and the default "since-navigation" return the same errors: this single-document adapter never crosses documents within one page's lifetime, so nothing ever marks the buffer at a navigation. |
| pause | 🚫 | Pausing for the Playwright Inspector requires the browser process. |
| pdf | 🚫 | Generating a PDF requires the browser process. |
| pickLocator | ❌ | |
| prependListener | ⚠️ | Fires only the supported events; other names never fire. |
| press | ✅ | |
| reload | ⚠️ | Does not resolve in the old document, which the reload destroys, and returns no Response, since the reload ends execution; rejects with a timeout only if the document is not replaced. |
| removeAllListeners | ⚠️ | Only the supported events ever fire; other names are accepted. |
| removeListener | ⚠️ | Only the supported events ever fire; other names are accepted. |
| removeLocatorHandler | ✅ | |
| request | 🚫 | Returns Playwright's Node-side API request context, which has no in-document counterpart. |
| requestGC | 🚫 | Forcing garbage collection requires the browser process. |
| requests | ⚠️ | fetch() and XMLHttpRequest calls of the current document only; see Request and Response. |
| route | 🚫 | Browser-level network interception is excluded. |
| routeFromHAR | 🚫 | Browser-level network interception is excluded. |
| routeWebSocket | 🚫 | Browser-level network interception is excluded. |
| screencast | 🚫 | Capturing a screencast requires the browser process. |
| screenshot | ⚠️ | Returns a Uint8Array rendered from the DOM, and rejects path and animations: "disabled"; see Screenshots. |
| selectOption | ✅ | |
| sessionStorage | ✅ | |
| setChecked | ✅ | |
| setContent | 🚫 | Document replacement is excluded.
