@ralphralphai/playwright 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +217 -250
- package/dist/index.cjs +54 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +18 -17
- package/dist/index.mjs +53 -53
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,134 +1,211 @@
|
|
|
1
1
|
# @ralphralphai/playwright
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
other states at the same URL. Each capture contains a screenshot, the recorded
|
|
6
|
-
and actual URLs, and `data-track-id` element positions.
|
|
3
|
+
Turn your Playwright tests into a map of your app for
|
|
4
|
+
[Ralph](https://ralphralph.ai).
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
Tag a test with `@ralph` and every page it visits is recorded: a full-page
|
|
7
|
+
screenshot, the URL, and the position of every element carrying a
|
|
8
|
+
`data-track-id`. Ralph builds a navigation graph from those recordings and
|
|
9
|
+
overlays real user interactions on the screenshots as heatmaps.
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
Your tests keep driving the app exactly as they do today. Untagged tests are
|
|
12
|
+
untouched, and recording is off unless you turn it on.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
Requires Node.js 20.19+ and Playwright Test 1.62.1+ (major version 1).
|
|
15
17
|
|
|
16
18
|
```sh
|
|
17
19
|
pnpm add -D @ralphralphai/playwright @playwright/test
|
|
18
20
|
pnpm exec playwright install chromium
|
|
19
21
|
```
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
**1. Add a `ralph.jsonc`** next to `playwright.config.ts`, listing the screen
|
|
26
|
+
sizes to record at:
|
|
22
27
|
|
|
23
|
-
```
|
|
28
|
+
```jsonc
|
|
24
29
|
{
|
|
25
|
-
"
|
|
30
|
+
"$schema": "./node_modules/@ralphralphai/config/schema/ralph.schema.json",
|
|
31
|
+
"screenSizes": [{ "width": 1280, "height": 720 }]
|
|
26
32
|
}
|
|
27
33
|
```
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
fixture preserves the Ralph config; it does not run its simple URL crawls.
|
|
35
|
+
See [`@ralphralphai/config`](https://www.npmjs.com/package/@ralphralphai/config)
|
|
36
|
+
for everything the file can declare.
|
|
32
37
|
|
|
33
|
-
|
|
38
|
+
**2. Import `test` from this package and tag the tests to record:**
|
|
34
39
|
|
|
35
40
|
```ts
|
|
36
41
|
import { test, expect } from '@ralphralphai/playwright';
|
|
37
42
|
|
|
38
|
-
test('checkout', { tag: '@ralph' }, async ({ page
|
|
43
|
+
test('checkout', { tag: '@ralph' }, async ({ page }) => {
|
|
39
44
|
await page.goto('https://example.com/cart');
|
|
40
|
-
await
|
|
45
|
+
await page.getByRole('link', { name: 'Checkout' }).click();
|
|
46
|
+
await expect(page).toHaveURL(/checkout/);
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Every URL the test reaches is recorded automatically. You don't need to call
|
|
51
|
+
anything.
|
|
52
|
+
|
|
53
|
+
**3. Run with recording on:**
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
RALPH_MODE=local pnpm exec playwright test
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Recordings are saved into Playwright's test output directory. To send them to
|
|
60
|
+
Ralph instead, see [Uploading to Ralph](#uploading-to-ralph).
|
|
61
|
+
|
|
62
|
+
## Tag your elements
|
|
63
|
+
|
|
64
|
+
Ralph records the position of every visible element with a `data-track-id`, so
|
|
65
|
+
it can place heatmap data on the screenshot. Use the same ids your
|
|
66
|
+
[`@ralphralphai/tracker`](https://www.npmjs.com/package/@ralphralphai/tracker)
|
|
67
|
+
setup uses:
|
|
68
|
+
|
|
69
|
+
```html
|
|
70
|
+
<button data-track-id="add-to-cart">Add to cart</button>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Recording pages
|
|
74
|
+
|
|
75
|
+
### Automatically, on every URL change
|
|
41
76
|
|
|
77
|
+
In a `@ralph` test, Ralph records a page whenever the URL changes in any tab of
|
|
78
|
+
the test's browser context:
|
|
79
|
+
|
|
80
|
+
- full page loads, redirects (the final page), and SPA route changes
|
|
81
|
+
- query-string and hash changes
|
|
82
|
+
- new tabs and popups
|
|
83
|
+
|
|
84
|
+
It does not record `about:blank`, iframes, reloads of the same URL, or changes
|
|
85
|
+
to the page that don't change the URL.
|
|
86
|
+
|
|
87
|
+
Each recording waits for the DOM to load, then another 150 ms (`settleMs`) for
|
|
88
|
+
the page to render. If your app needs longer, use the `ready` option to say when
|
|
89
|
+
a page is ready:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
ralphOptions: {
|
|
93
|
+
ready: async (page, signal) => {
|
|
94
|
+
await page.getByTestId('app-ready').waitFor({ timeout: 3000 });
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Explicitly, for states at the same URL
|
|
100
|
+
|
|
101
|
+
Dialogs, menus, and tabs often change the page without changing the URL. Record
|
|
102
|
+
them with `ralph.recordNode`:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
test('shipping options', { tag: '@ralph' }, async ({ page, ralph }) => {
|
|
106
|
+
await page.goto('https://example.com/cart');
|
|
42
107
|
await page.getByRole('button', { name: 'Shipping options' }).click();
|
|
43
108
|
await expect(page.getByRole('dialog')).toBeVisible();
|
|
44
109
|
|
|
45
|
-
await ralph.
|
|
46
|
-
state: 'shipping-options-open',
|
|
47
|
-
url: '/checkout/shipping-options',
|
|
110
|
+
await ralph.recordNode(page, {
|
|
111
|
+
state: 'shipping-options-open', // a label for this state
|
|
112
|
+
url: '/checkout/shipping-options', // optional: the URL to record it under
|
|
48
113
|
});
|
|
49
114
|
});
|
|
115
|
+
```
|
|
50
116
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
117
|
+
`recordNode` records the page as it is right now, without waiting for it to
|
|
118
|
+
settle, so call it after your assertions. The `url` option only changes what is
|
|
119
|
+
recorded; the browser doesn't navigate. Relative URLs resolve against the
|
|
120
|
+
current page.
|
|
121
|
+
|
|
122
|
+
### Waiting for recordings
|
|
123
|
+
|
|
124
|
+
Recording runs in the background, so your test isn't slowed down. If the test
|
|
125
|
+
moves on before a screenshot finishes, that recording is marked `superseded`
|
|
126
|
+
rather than attributed to the wrong page. When you need a page recorded before
|
|
127
|
+
leaving it, wait for pending recordings first:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
await page.goto('https://example.com/pricing');
|
|
131
|
+
await ralph.flush(); // make sure /pricing is recorded
|
|
132
|
+
await page.goto('https://example.com/signup');
|
|
55
133
|
```
|
|
56
134
|
|
|
57
|
-
|
|
58
|
-
|
|
135
|
+
### Browser contexts you create yourself
|
|
136
|
+
|
|
137
|
+
The standard `context` and `page` fixtures are recorded for you. For a context
|
|
138
|
+
you create yourself, enroll it, and flush before closing it:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
const context = await browser.newContext();
|
|
142
|
+
ralph.observe(context);
|
|
143
|
+
// ...
|
|
144
|
+
await ralph.flush();
|
|
145
|
+
await context.close();
|
|
59
146
|
```
|
|
60
147
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
148
|
+
### When recording fails
|
|
149
|
+
|
|
150
|
+
- An explicit `recordNode` call that fails rejects, like any other failing
|
|
151
|
+
step.
|
|
152
|
+
- A failed automatic recording fails an otherwise passing test at the end, so
|
|
153
|
+
missing pages don't go unnoticed. The reason is recorded with the results.
|
|
154
|
+
- A superseded recording, or a page whose layout moved during its screenshot,
|
|
155
|
+
doesn't fail the test.
|
|
156
|
+
|
|
157
|
+
## Modes
|
|
65
158
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
159
|
+
Set the mode with `RALPH_MODE` or the `mode` option:
|
|
160
|
+
|
|
161
|
+
| Mode | What happens |
|
|
162
|
+
| -------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
163
|
+
| `off` | Default. Nothing is recorded, no Ralph config or credentials are needed, and `ralph.recordNode` does nothing. |
|
|
164
|
+
| `local` | `@ralph` tests are recorded into Playwright's output directory. |
|
|
165
|
+
| `upload` | As `local`, and each test's recordings are uploaded to Ralph when it finishes. Chosen automatically when `RALPH_UPLOAD_KEY` is set and no mode is. |
|
|
166
|
+
|
|
167
|
+
Tests without the `@ralph` tag are never recorded, in any mode. In `local` or
|
|
168
|
+
`upload` mode, calling `ralph.recordNode` in an untagged test throws a reminder
|
|
169
|
+
to add the tag.
|
|
170
|
+
|
|
171
|
+
Tests that import from `@playwright/test` directly keep working alongside Ralph
|
|
69
172
|
tests.
|
|
70
173
|
|
|
71
|
-
|
|
72
|
-
enables uploading. Set `RALPH_API_URL` to your Ralph server origin (or set
|
|
73
|
-
`ralphOptions.apiUrl`). The origin must use HTTPS; plain HTTP is accepted only
|
|
74
|
-
for loopback hosts (`localhost`, `127.x.x.x`, `[::1]`) during local development.
|
|
75
|
-
Issue a private upload key for your Ralph project with the authenticated
|
|
76
|
-
`/api/project/playwright/key/create` endpoint and keep it in CI secrets. A key belongs to
|
|
77
|
-
the project, so one key serves every app in it. Set `RALPH_APP_ID` (or
|
|
78
|
-
`ralphOptions.appId`) to the app the captures belong to.
|
|
174
|
+
## Uploading to Ralph
|
|
79
175
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
176
|
+
1. Create an upload key for your Ralph project with the authenticated
|
|
177
|
+
`/api/project/key/create` endpoint. One key covers every app in the project.
|
|
178
|
+
Keep it in your CI secrets.
|
|
179
|
+
2. Set these, as environment variables or options:
|
|
180
|
+
|
|
181
|
+
| Variable | Option | Value |
|
|
182
|
+
| ------------------ | -------- | -------------------------------------- |
|
|
183
|
+
| `RALPH_UPLOAD_KEY` | | The upload key. Environment only. |
|
|
184
|
+
| `RALPH_API_URL` | `apiUrl` | Your Ralph server, e.g. `https://…` |
|
|
185
|
+
| `RALPH_APP_ID` | `appId` | The app these recordings belong to |
|
|
186
|
+
|
|
187
|
+
3. Run:
|
|
188
|
+
|
|
189
|
+
```sh
|
|
190
|
+
RALPH_MODE=upload RALPH_API_URL=https://your-ralph-server.example RALPH_APP_ID=<app id> \
|
|
191
|
+
pnpm exec playwright test
|
|
192
|
+
```
|
|
83
193
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
Automatic capture watches main-frame HTTP(S) URLs in the standard `context`
|
|
95
|
-
fixture, including new tabs and popups. It includes the first application URL,
|
|
96
|
-
full-document navigation, SPA history changes, query changes, and hash changes.
|
|
97
|
-
It skips `about:blank`, subframe navigation, same-URL reloads, and DOM-only changes.
|
|
98
|
-
|
|
99
|
-
Automatic captures wait for DOM content loaded and then settle for 150 ms by
|
|
100
|
-
default. A URL change does not prove application readiness. Supply `ready` for
|
|
101
|
-
an application-specific condition, or explicitly capture after your assertions.
|
|
102
|
-
|
|
103
|
-
Capture runs in the background and is serialized per browser page. A test can
|
|
104
|
-
navigate again before a screenshot completes. Such nodes are retained as
|
|
105
|
-
`superseded`, without a screenshot attributed to the wrong URL. Use
|
|
106
|
-
`await ralph.flush()` to wait for currently queued captures before deliberately
|
|
107
|
-
leaving a state whose screenshot you need. This is also useful before manually
|
|
108
|
-
closing a page or context.
|
|
109
|
-
|
|
110
|
-
The standard context fixture drains captures before closing its pages. Manually
|
|
111
|
-
created contexts can be enrolled with `ralph.observe(context)`; their owner must
|
|
112
|
-
flush before closing them. An explicit capture enrolls its page's context too.
|
|
113
|
-
|
|
114
|
-
`await ralph.captureNode(page, { state?, url? })` always requests a node,
|
|
115
|
-
even at the same URL. It uses the current state without the automatic settling
|
|
116
|
-
delay or readiness hook. A relative URL override resolves against the actual
|
|
117
|
-
browser URL. It does not navigate, change history, or affect subsequent automatic
|
|
118
|
-
captures. Only HTTP(S) recorded URLs are accepted.
|
|
119
|
-
|
|
120
|
-
Explicit capture errors reject the call. Automatic capture errors are written
|
|
121
|
-
to the manifest and fail an otherwise passing opted-in test at fixture teardown.
|
|
122
|
-
Superseded captures and detected geometry changes mark the manifest incomplete,
|
|
123
|
-
without independently failing the test.
|
|
194
|
+
Each attempt of a `@ralph` test uploads once when it finishes. The upload
|
|
195
|
+
receipt is attached to the test as `ralph-upload-receipt`, and its `resultId`
|
|
196
|
+
opens the upload in the Ralph dashboard at `/uploads/<resultId>`. The upload key
|
|
197
|
+
is never written to the results or the receipt.
|
|
198
|
+
|
|
199
|
+
`RALPH_API_URL` must be HTTPS. Plain HTTP is accepted only for `localhost`,
|
|
200
|
+
`127.x.x.x`, and `[::1]`, for local development.
|
|
201
|
+
|
|
202
|
+
If an upload fails, the test fails.
|
|
124
203
|
|
|
125
204
|
## Screen sizes
|
|
126
205
|
|
|
127
|
-
A viewport is fixed
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
and expands a base project into one project per size. Each project's viewport
|
|
131
|
-
is that size:
|
|
206
|
+
A browser context's viewport is fixed, so each entry in `screenSizes` becomes
|
|
207
|
+
its own Playwright project. `ralphProjects` expands one project into one per
|
|
208
|
+
size:
|
|
132
209
|
|
|
133
210
|
```ts
|
|
134
211
|
import { defineConfig, devices } from '@playwright/test';
|
|
@@ -138,8 +215,8 @@ export default defineConfig<RalphFixtures>({
|
|
|
138
215
|
projects: [
|
|
139
216
|
// Ordinary tests keep their single project.
|
|
140
217
|
{ name: 'chromium', grepInvert: /@ralph\b/, use: devices['Desktop Chrome'] },
|
|
141
|
-
// With screenSizes 375x812
|
|
142
|
-
// "chromium 375x812"
|
|
218
|
+
// With screenSizes 375x812 and 1280x720, this creates the projects
|
|
219
|
+
// "chromium 375x812" and "chromium 1280x720".
|
|
143
220
|
...ralphProjects({
|
|
144
221
|
name: 'chromium',
|
|
145
222
|
grep: /@ralph\b/,
|
|
@@ -149,15 +226,14 @@ export default defineConfig<RalphFixtures>({
|
|
|
149
226
|
});
|
|
150
227
|
```
|
|
151
228
|
|
|
152
|
-
|
|
153
|
-
`{ root: import.meta.dirname }` (or `__dirname`) or
|
|
154
|
-
|
|
155
|
-
|
|
229
|
+
`ralphProjects` reads `ralph.jsonc` from the current directory. If Playwright
|
|
230
|
+
runs from elsewhere, pass `{ root: import.meta.dirname }` (or `__dirname`), or
|
|
231
|
+
an inline `config`, as the second argument. Keep it pointing at the same config
|
|
232
|
+
as `ralphOptions`.
|
|
156
233
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
screen projects skip it:
|
|
234
|
+
A test runs at every size by default. A mobile flow is usually a different test
|
|
235
|
+
from its desktop flow, so tag a test or `describe` block with
|
|
236
|
+
`@ralph-screen:WIDTHxHEIGHT`, once per size, to run it only at those sizes:
|
|
161
237
|
|
|
162
238
|
```ts
|
|
163
239
|
test(
|
|
@@ -168,36 +244,22 @@ test(
|
|
|
168
244
|
},
|
|
169
245
|
);
|
|
170
246
|
|
|
171
|
-
// One tag per size: runs at 768x1024 and 1280x720, skipped at 375x812.
|
|
172
|
-
test(
|
|
173
|
-
'data table',
|
|
174
|
-
{ tag: ['@ralph', '@ralph-screen:768x1024', '@ralph-screen:1280x720'] },
|
|
175
|
-
async ({ page }) => {},
|
|
176
|
-
);
|
|
177
|
-
|
|
178
247
|
test.describe('desktop layouts', { tag: '@ralph-screen:1280x720' }, () => {
|
|
179
248
|
test('sidebar', { tag: '@ralph' }, async ({ page }) => {});
|
|
180
249
|
});
|
|
181
250
|
```
|
|
182
251
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
## Options
|
|
190
|
-
|
|
191
|
-
`WebRalphConfig` is re-exported as a type from this package for inline config
|
|
192
|
-
annotations. Its definition remains in `@ralphralphai/config`:
|
|
252
|
+
- Tags on a `describe` block and its tests combine.
|
|
253
|
+
- Screen tags apply in every mode, because they choose which tests run.
|
|
254
|
+
- A size missing from `screenSizes` fails the test, so a typo can't silently
|
|
255
|
+
skip it.
|
|
256
|
+
- Projects not created by `ralphProjects` ignore screen tags.
|
|
193
257
|
|
|
194
|
-
|
|
195
|
-
import type { WebRalphConfig } from '@ralphralphai/playwright';
|
|
258
|
+
To run one size: `playwright test --project "chromium 375x812"`.
|
|
196
259
|
|
|
197
|
-
|
|
198
|
-
```
|
|
260
|
+
## Options
|
|
199
261
|
|
|
200
|
-
Set `ralphOptions`
|
|
262
|
+
Set `ralphOptions` in `playwright.config.ts`, or per file with `test.use`:
|
|
201
263
|
|
|
202
264
|
```ts
|
|
203
265
|
import { defineConfig } from '@playwright/test';
|
|
@@ -205,146 +267,51 @@ import type { RalphFixtures } from '@ralphralphai/playwright';
|
|
|
205
267
|
|
|
206
268
|
export default defineConfig<RalphFixtures>({
|
|
207
269
|
use: {
|
|
208
|
-
viewport: { width: 1280, height: 720 },
|
|
209
270
|
ralphOptions: {
|
|
210
271
|
mode: 'local',
|
|
211
272
|
configPath: './ralph.jsonc',
|
|
212
|
-
buildId:
|
|
213
|
-
|
|
214
|
-
settleMs: 150,
|
|
215
|
-
captureTimeoutMs: 5000,
|
|
216
|
-
ready: async (page, signal) => {
|
|
217
|
-
signal.throwIfAborted();
|
|
218
|
-
await page.getByTestId('app-ready').waitFor({ timeout: 3000 });
|
|
219
|
-
},
|
|
273
|
+
buildId: process.env.GIT_SHA,
|
|
274
|
+
settleMs: 300,
|
|
220
275
|
},
|
|
221
276
|
},
|
|
222
277
|
});
|
|
223
278
|
```
|
|
224
279
|
|
|
225
|
-
| Option |
|
|
226
|
-
|
|
|
227
|
-
| `mode` | `off`, `local`, or `upload
|
|
228
|
-
| `
|
|
229
|
-
| `
|
|
230
|
-
| `
|
|
231
|
-
| `
|
|
232
|
-
| `
|
|
233
|
-
| `
|
|
234
|
-
| `
|
|
235
|
-
| `
|
|
236
|
-
| `
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
attempts. Run/build IDs are optional for local exploration, and unique
|
|
248
|
-
attempt IDs are always generated.
|
|
249
|
-
|
|
250
|
-
## Existing fixtures
|
|
251
|
-
|
|
252
|
-
Compose Ralph with the test object your project already uses:
|
|
280
|
+
| Option | Default | What it does |
|
|
281
|
+
| ----------------- | --------------------- | ------------------------------------------------------------------------------------------------ |
|
|
282
|
+
| `mode` | `RALPH_MODE`, else `off` | `off`, `local`, or `upload`. See [Modes](#modes). |
|
|
283
|
+
| `configPath` | `ralph.jsonc` | Path to the config, relative to the Playwright config's directory. |
|
|
284
|
+
| `config` | | An inline config instead of a file. |
|
|
285
|
+
| `apiUrl` | `RALPH_API_URL` | Your Ralph server, for uploads. |
|
|
286
|
+
| `appId` | `RALPH_APP_ID` | The app recordings belong to, for uploads. |
|
|
287
|
+
| `uploadTimeoutMs` | 30000 | How long an upload may take. |
|
|
288
|
+
| `buildId` | `RALPH_BUILD_ID` | Your app's build, so recordings line up with the analytics from that build. |
|
|
289
|
+
| `runId` | `RALPH_RUN_ID` | Groups the tests of one CI run. |
|
|
290
|
+
| `settleMs` | 150 | How long to wait after the DOM loads before an automatic recording. |
|
|
291
|
+
| `recordTimeoutMs` | 5000 | How long one recording may take, including settling and `ready`. Must be more than `settleMs`. |
|
|
292
|
+
| `ready` | | `(page, signal) => Promise<void>`. Waits for your app to be ready before an automatic recording. |
|
|
293
|
+
|
|
294
|
+
`ready` is bounded by `recordTimeoutMs`. Ralph stops waiting when time runs out
|
|
295
|
+
but can't cancel your code, so honor `signal` or use Playwright operations with
|
|
296
|
+
their own timeouts.
|
|
297
|
+
|
|
298
|
+
Config files accept comments and trailing commas, and are validated when the
|
|
299
|
+
first `@ralph` test in a worker starts. Keep the config unchanged during a run.
|
|
300
|
+
|
|
301
|
+
For an inline config, the type is re-exported:
|
|
253
302
|
|
|
254
303
|
```ts
|
|
255
|
-
import {
|
|
256
|
-
import { test as ralphTest } from '@ralphralphai/playwright';
|
|
257
|
-
import { test as applicationTest } from './application-fixtures';
|
|
304
|
+
import type { WebRalphConfig } from '@ralphralphai/playwright';
|
|
258
305
|
|
|
259
|
-
|
|
306
|
+
const config: WebRalphConfig = { screenSizes: [{ width: 1280, height: 720 }] };
|
|
260
307
|
```
|
|
261
308
|
|
|
262
|
-
|
|
263
|
-
using `ralph.observe(context)`. Capture it while it is still alive.
|
|
264
|
-
|
|
265
|
-
## Artifacts
|
|
266
|
-
|
|
267
|
-
Each opted-in attempt writes `ralph/captures.json` and full-page screenshots within
|
|
268
|
-
Playwright's per-test output directory. The JSON is attached as
|
|
269
|
-
`ralph-captures`; screenshots are attached with unique names referenced by the
|
|
270
|
-
manifest. Copy the full test output or retain the Playwright attachments.
|
|
271
|
-
Configure Playwright to preserve successful test output when collecting captures.
|
|
272
|
-
|
|
273
|
-
The exported `CaptureManifest` and `RawNode` types describe format
|
|
274
|
-
version 1. The manifest includes the config snapshot, producer versions, optional run and
|
|
275
|
-
build IDs, test/project/retry/repeat/shard identity, separate browser-page streams,
|
|
276
|
-
opener relationships, and the `nodes` the test produced.
|
|
277
|
-
|
|
278
|
-
Each entry in `nodes` is a raw node: one page state as the test saw it, with a
|
|
279
|
-
sequence within its page. The same page visited twice produces two raw nodes,
|
|
280
|
-
and superseded or failed captures are kept too. The server merges raw nodes
|
|
281
|
-
into the graph's nodes; this package does no normalization or merging.
|
|
282
|
-
|
|
283
|
-
Each node records `previousNodeId`, the node its page state was reached from:
|
|
284
|
-
the previous one on the same page, or for a popup's first node, its opener's
|
|
285
|
-
latest. Superseded nodes are skipped. No code is injected into
|
|
286
|
-
the page; the flow comes from the order of URL changes.
|
|
287
|
-
|
|
288
|
-
Screenshots are WebP at quality 80, falling back to PNG for pages over 16,383
|
|
289
|
-
pixels in either dimension, which WebP cannot encode. They use one image pixel
|
|
290
|
-
per CSS pixel, including on high-DPI devices.
|
|
291
|
-
Tracked-element boxes use document CSS coordinates and preserve duplicate track
|
|
292
|
-
IDs as separate boxes. Collection includes visible elements in the main frame,
|
|
293
|
-
including open shadow roots; it does not collect child-frame geometry. Elements
|
|
294
|
-
with zero width or height, or entirely off-screen horizontally, are dropped;
|
|
295
|
-
elements below the fold are kept.
|
|
296
|
-
The manifest records viewport, document extent, scroll position, screenshot size,
|
|
297
|
-
and device pixel ratio.
|
|
298
|
-
|
|
299
|
-
Geometry is measured before and after the screenshot. Differences set
|
|
300
|
-
`geometryStable: false`. This detects layout drift, not every visual change;
|
|
301
|
-
animations, sticky elements, and virtualized content can still need explicit
|
|
302
|
-
application preparation. The package does not freeze or alter the application.
|
|
303
|
-
|
|
304
|
-
An attempt is `complete` only when the test has passed at fixture finalization,
|
|
305
|
-
there is at least one node, and every node has a screenshot with
|
|
306
|
-
stable measured geometry. A future reporter must reconcile this with the final
|
|
307
|
-
Playwright result if another fixture fails later. Retries have distinct manifests
|
|
308
|
-
and attempt IDs; partial nodes are preserved.
|
|
309
|
-
|
|
310
|
-
## Upload format and retries
|
|
311
|
-
|
|
312
|
-
The HTTP body is one `application/gzip` tar archive. `capture.json` holds UTF-8
|
|
313
|
-
JSON `{ manifest, screenshots: [{ nodeId, path }] }`, and each `path` points to
|
|
314
|
-
a raw WebP (or PNG fallback) under `assets/images/`. The manifest includes the exact config
|
|
315
|
-
snapshot.
|
|
316
|
-
The package emits only its current `formatVersion: 1`; the server independently
|
|
317
|
-
keeps versioned Zod schemas and selects one by `formatVersion`. No crawler API
|
|
318
|
-
package or server schema is imported by this integration.
|
|
319
|
-
|
|
320
|
-
The limits per attempt are 64 MiB compressed, 2 MiB manifest JSON, 64 MiB total
|
|
321
|
-
screenshot bytes, and 500 screenshots. Server validation
|
|
322
|
-
also limits nodes to 5,000. Split larger scenarios into smaller tests.
|
|
323
|
-
Screenshots are already compressed; gzip mainly compresses the manifest.
|
|
324
|
-
|
|
325
|
-
`uploadCapture(manifest, screenshots, { apiUrl, uploadKey, timeoutMs? })` can retry
|
|
326
|
-
saved artifacts, where each screenshot is `{ nodeId, contentType, bytes: Uint8Array }`.
|
|
327
|
-
`createCaptureBundle(manifest, screenshots)` returns the gzip bytes when a caller
|
|
328
|
-
needs to handle transport itself. An identical retry preserves `attemptId` and
|
|
329
|
-
returns `replayed: true`; changed contents under the same ID are rejected. There
|
|
330
|
-
are no automatic HTTP retries. Playwright test retries create new attempt IDs.
|
|
331
|
-
The manifest's status reflects fixture finalization; a future reporter can
|
|
332
|
-
reconcile failures in fixtures that finish later.
|
|
333
|
-
|
|
334
|
-
## Development
|
|
335
|
-
|
|
336
|
-
From the repository root:
|
|
309
|
+
## Limits
|
|
337
310
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
pnpm --filter @ralphralphai/playwright test
|
|
344
|
-
pnpm --filter @ralphralphai/playwright exec playwright install chromium
|
|
345
|
-
pnpm --filter @ralphralphai/playwright test:browser
|
|
346
|
-
```
|
|
311
|
+
Per test attempt: 500 screenshots, 64 MiB of screenshots, 2 MiB of recorded
|
|
312
|
+
data besides the screenshots, and 64 MiB compressed. The server accepts up to
|
|
313
|
+
5,000 recorded pages. Split larger flows into several tests.
|
|
314
|
+
|
|
315
|
+
## License
|
|
347
316
|
|
|
348
|
-
|
|
349
|
-
then checks the emitted manifests and screenshot attachments. It includes an
|
|
350
|
-
intentional failed first attempt to verify retry isolation.
|
|
317
|
+
Apache-2.0
|