DIF-015 — Interactive browser recorder for test creation.
Opens a Playwright browser pointed at the project's URL, streams a
live CDP screencast to the frontend via SSE (reusing the existing
emitRunEvent channel), and captures raw user interactions
(clicks, fills, key-presses, navigations) as Playwright actions.
On stop, the captured action list is transformed into a Playwright test body and returned so the caller (routes/tests.js) can persist a Draft test and run it through the rest of the generation pipeline (assertion enhancement, self-healing transform) just like any other AI-generated test.
Exports
startRecording— Launch browser + begin capture.stopRecording— Stop capture; return{ actions, playwrightCode }.getRecording— Inspect an in-flight recording (for abort / status).
Design notes
Capture is done entirely in the page context via a single injected
listener that posts events back to Node through page.exposeBinding.
This is the same approach Playwright's own codegen uses for JavaScript
action recording, minus the DevTools UI — we only need the raw event
stream.
- Source:
Members
(inner, constant) RECORDER_SCRIPT
JS source injected into every page frame. It captures pointer/keyboard
events and relays them to Node via the __sentriRecord binding. We
de-duplicate by dispatch target + event type so that a single click
doesn't emit multiple entries when bubbling through the DOM.
bestSelector intentionally mirrors Playwright's own heuristics loosely:
prefer role-based selectors, then data-testid, then aria-label, then
a short CSS chain.
- Source:
(inner, constant) completedSessions :Map.<string, CompletedRecording>
Short-lived cache of recordings torn down by the MAX_RECORDING_MS safety-net
timeout. Entries live for COMPLETED_TTL_MS so a user who clicks
"Stop & Save" moments after the timeout fires can still recover their
captured actions instead of losing them to a 500 error. Scoped to the in-
process recorder so no external store is needed.
Type:
- Map.<string, CompletedRecording>
- Source:
(inner, constant) sessions :Map.<string, RecordingSession>
Type:
- Map.<string, RecordingSession>
- Source:
Methods
(static) actionsToPlaywrightCode(testName, startUrl, actions) → {string}
Convert a list of captured actions into a Playwright test body. The output
is wrapped in the repo-standard test(...) shape so the existing runner
(codeExecutor, codeParsing) treats it like any AI-generated test.
Parameters:
| Name | Type | Description |
|---|---|---|
testName |
string | |
startUrl |
string | |
actions |
Array.<RecordedAction> |
- Source:
Returns:
Playwright source code.
- Type
- string
(static) getRecording(sessionId) → {RecordingSession|null}
Look up an in-flight recording session.
Parameters:
| Name | Type | Description |
|---|---|---|
sessionId |
string |
- Source:
Returns:
- Type
- RecordingSession | null
(static) startRecording(args) → {Promise.<RecordingSession>}
Start a new interactive recording session. Opens a Playwright browser,
navigates to startUrl, installs the capture script, and begins a CDP
screencast on the given session ID (reused as the SSE run ID).
Parameters:
| Name | Type | Description | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
args |
Object |
Properties
|
- Source:
Returns:
- Type
- Promise.<RecordingSession>
(static) stopRecording(sessionId, optsopt) → {Promise.<{actions: Array.<RecordedAction>, playwrightCode: string, url: string}>}
Stop an in-flight recording. Tears down the browser and returns the captured actions plus a ready-to-persist Playwright test body.
Parameters:
| Name | Type | Attributes | Description | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
sessionId |
string | ||||||||||
opts |
Object |
<optional> |
Properties
|
- Source:
Throws:
-
When the session does not exist.
- Type
- Error
Returns:
- Type
- Promise.<{actions: Array.<RecordedAction>, playwrightCode: string, url: string}>
(static) takeCompletedRecording(sessionId) → {CompletedRecording|null}
Look up and remove a recording that was auto-torn-down by the safety-net
timeout. Returns null if no such recording is cached (either never timed
out, or the TTL has expired). The entry is removed on read so callers get
at-most-once delivery of the captured actions.
Parameters:
| Name | Type | Description |
|---|---|---|
sessionId |
string |
- Source:
Returns:
- Type
- CompletedRecording | null
(inner) escapeJsSingleQuote(str) → {string}
Escape a user-controlled string so it can be safely interpolated into a
JavaScript single-quoted string literal in generated source code. Handles
backslash (\), single quote ('), newline (\n), carriage return (\r),
line/paragraph separators (U+2028 / U+2029 — these break literals in most
engines), and other C0 control characters via \xHH escapes.
Order matters: backslash must be escaped first, otherwise subsequent replacements would double-escape their own inserted backslashes.
Parameters:
| Name | Type | Description |
|---|---|---|
str |
string |
- Source:
Returns:
- Type
- string
Type Definitions
CompletedRecording
Type:
- Object
Properties:
| Name | Type | Description |
|---|---|---|
projectId |
string | |
actions |
Array.<RecordedAction> | |
playwrightCode |
string | |
url |
string | |
completedAt |
number | |
reason |
"auto_timeout" | "manual" |
- Source:
RecordedAction
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
kind |
"goto" | "click" | "fill" | "press" | "select" | "check" | "uncheck" | ||
selector |
string |
<optional> |
Best-effort role/label/text/css selector. |
value |
string |
<optional> |
For |
url |
string |
<optional> |
For |
key |
string |
<optional> |
For |
ts |
number | Epoch ms when the action was captured. |
- Source:
RecordingSession
Type:
- Object
Properties:
| Name | Type | Attributes | Description |
|---|---|---|---|
id |
string | ||
projectId |
string | ||
url |
string | Starting URL. |
|
status |
"recording" | "stopping" | "stopped" | ||
actions |
Array.<RecordedAction> | ||
startedAt |
number | ||
browser |
Object |
<optional> |
Playwright Browser (internal). |
context |
Object |
<optional> |
Playwright BrowserContext (internal). |
page |
Object |
<optional> |
Playwright Page (internal). |
stopScreencast |
function |
<optional> |
Cleanup fn returned by startScreencast. |
- Source: