@ralphralphai/playwright 0.2.0 → 1.1.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/LICENSE +201 -0
- package/README.md +132 -145
- package/dist/index.cjs +22 -584
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +76 -38
- package/dist/index.mjs +15 -553
- package/dist/index.mjs.map +1 -1
- package/dist/reporter.cjs +169 -0
- package/dist/reporter.cjs.map +1 -0
- package/dist/reporter.d.cts +1 -0
- package/dist/reporter.d.mts +54 -0
- package/dist/reporter.mjs +167 -0
- package/dist/reporter.mjs.map +1 -0
- package/dist/upload-QZreR5i6.cjs +694 -0
- package/dist/upload-QZreR5i6.cjs.map +1 -0
- package/dist/upload-giSVxpun.mjs +592 -0
- package/dist/upload-giSVxpun.mjs.map +1 -0
- package/package.json +26 -16
package/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -4,12 +4,8 @@ Turn your Playwright tests into a map of your app for
|
|
|
4
4
|
[Ralph](https://ralphralph.ai).
|
|
5
5
|
|
|
6
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
|
|
8
|
-
|
|
9
|
-
overlays real user interactions on the screenshots as heatmaps.
|
|
10
|
-
|
|
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.
|
|
7
|
+
screenshot, the URL, and the position of every element with a `data-track-id`.
|
|
8
|
+
Untagged tests are untouched, and recording is off unless you turn it on.
|
|
13
9
|
|
|
14
10
|
## Install
|
|
15
11
|
|
|
@@ -22,8 +18,7 @@ pnpm exec playwright install chromium
|
|
|
22
18
|
|
|
23
19
|
## Quick start
|
|
24
20
|
|
|
25
|
-
**1. Add a `ralph.jsonc`** next to `playwright.config.ts
|
|
26
|
-
sizes to record at:
|
|
21
|
+
**1. Add a `ralph.jsonc`** next to `playwright.config.ts`:
|
|
27
22
|
|
|
28
23
|
```jsonc
|
|
29
24
|
{
|
|
@@ -33,9 +28,9 @@ sizes to record at:
|
|
|
33
28
|
```
|
|
34
29
|
|
|
35
30
|
See [`@ralphralphai/config`](https://www.npmjs.com/package/@ralphralphai/config)
|
|
36
|
-
for
|
|
31
|
+
for all fields.
|
|
37
32
|
|
|
38
|
-
**2. Import `test` from this package and tag
|
|
33
|
+
**2. Import `test` from this package and tag tests with `@ralph`:**
|
|
39
34
|
|
|
40
35
|
```ts
|
|
41
36
|
import { test, expect } from '@ralphralphai/playwright';
|
|
@@ -47,24 +42,28 @@ test('checkout', { tag: '@ralph' }, async ({ page }) => {
|
|
|
47
42
|
});
|
|
48
43
|
```
|
|
49
44
|
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
**3. Add the Ralph reporter** in `playwright.config.ts`:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
export default defineConfig({
|
|
49
|
+
reporter: [['list'], ['@ralphralphai/playwright/reporter']],
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
52
|
|
|
53
|
-
**
|
|
53
|
+
**4. Run:**
|
|
54
54
|
|
|
55
55
|
```sh
|
|
56
56
|
RALPH_MODE=local pnpm exec playwright test
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
Ralph
|
|
59
|
+
The merged graph for the run is written to `test-results/ralph/`. To send it to
|
|
60
|
+
Ralph, see [Uploading to Ralph](#uploading-to-ralph).
|
|
61
61
|
|
|
62
62
|
## Tag your elements
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
it can place heatmap data on the screenshot. Use the same ids your
|
|
64
|
+
Add `data-track-id` to elements, using the same ids as your
|
|
66
65
|
[`@ralphralphai/tracker`](https://www.npmjs.com/package/@ralphralphai/tracker)
|
|
67
|
-
setup
|
|
66
|
+
setup:
|
|
68
67
|
|
|
69
68
|
```html
|
|
70
69
|
<button data-track-id="add-to-cart">Add to cart</button>
|
|
@@ -72,21 +71,15 @@ setup uses:
|
|
|
72
71
|
|
|
73
72
|
## Recording pages
|
|
74
73
|
|
|
75
|
-
### Automatically
|
|
76
|
-
|
|
77
|
-
In a `@ralph` test, Ralph records a page whenever the URL changes in any tab of
|
|
78
|
-
the test's browser context:
|
|
74
|
+
### Automatically
|
|
79
75
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
A page is recorded whenever the URL changes in any tab of the test: page loads,
|
|
77
|
+
redirects (the final page), SPA route changes, query and hash changes, and new
|
|
78
|
+
tabs and popups. Reloads, iframes, `about:blank`, and changes that keep the URL
|
|
79
|
+
are not recorded.
|
|
83
80
|
|
|
84
|
-
|
|
85
|
-
|
|
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:
|
|
81
|
+
Each recording waits for the DOM to load plus `settleMs` (150 ms). If your app
|
|
82
|
+
needs longer, use `ready`:
|
|
90
83
|
|
|
91
84
|
```ts
|
|
92
85
|
ralphOptions: {
|
|
@@ -96,10 +89,13 @@ ralphOptions: {
|
|
|
96
89
|
},
|
|
97
90
|
```
|
|
98
91
|
|
|
92
|
+
CSS animations and transitions are frozen for the screenshot. For animations
|
|
93
|
+
driven from JavaScript, set `reduceMotion` if your app honors
|
|
94
|
+
`prefers-reduced-motion`.
|
|
95
|
+
|
|
99
96
|
### Explicitly, for states at the same URL
|
|
100
97
|
|
|
101
|
-
|
|
102
|
-
them with `ralph.recordNode`:
|
|
98
|
+
For dialogs, menus, and tabs, call `ralph.recordNode` after your assertions:
|
|
103
99
|
|
|
104
100
|
```ts
|
|
105
101
|
test('shipping options', { tag: '@ralph' }, async ({ page, ralph }) => {
|
|
@@ -114,98 +110,111 @@ test('shipping options', { tag: '@ralph' }, async ({ page, ralph }) => {
|
|
|
114
110
|
});
|
|
115
111
|
```
|
|
116
112
|
|
|
117
|
-
`
|
|
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.
|
|
113
|
+
`url` only changes what is recorded; the browser doesn't navigate.
|
|
121
114
|
|
|
122
|
-
### Waiting for
|
|
115
|
+
### Waiting for capture
|
|
123
116
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
leaving it, wait for pending recordings first:
|
|
117
|
+
Recordings run in the background. If the test leaves a page before its
|
|
118
|
+
screenshot is done, the page is `uncaptured`: it's in the graph without a
|
|
119
|
+
screenshot. To make sure a page is captured before leaving it:
|
|
128
120
|
|
|
129
121
|
```ts
|
|
130
122
|
await page.goto('https://example.com/pricing');
|
|
131
|
-
await ralph.
|
|
123
|
+
await ralph.waitForCapture();
|
|
132
124
|
await page.goto('https://example.com/signup');
|
|
133
125
|
```
|
|
134
126
|
|
|
135
|
-
###
|
|
127
|
+
### Pausing capture
|
|
128
|
+
|
|
129
|
+
To leave steps out of the graph:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
await page.goto('https://example.com/a');
|
|
133
|
+
ralph.pause();
|
|
134
|
+
await page.goto('https://example.com/b'); // not recorded
|
|
135
|
+
await page.goto('https://example.com/c'); // not recorded
|
|
136
|
+
ralph.resume();
|
|
137
|
+
await page.goto('https://example.com/d'); // recorded, linked from /a
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
This works across tabs. While paused, `recordNode` does nothing. `resume` does
|
|
141
|
+
not record the current page.
|
|
136
142
|
|
|
137
|
-
|
|
138
|
-
you create yourself, enroll it, and flush before closing it:
|
|
143
|
+
### Browser contexts you create yourself
|
|
139
144
|
|
|
140
145
|
```ts
|
|
141
146
|
const context = await browser.newContext();
|
|
142
147
|
ralph.observe(context);
|
|
143
148
|
// ...
|
|
144
|
-
await ralph.
|
|
149
|
+
await ralph.waitForCapture();
|
|
145
150
|
await context.close();
|
|
146
151
|
```
|
|
147
152
|
|
|
148
153
|
### When recording fails
|
|
149
154
|
|
|
150
|
-
-
|
|
151
|
-
|
|
152
|
-
-
|
|
153
|
-
|
|
154
|
-
- A superseded recording, or a page whose layout moved during its screenshot,
|
|
155
|
-
doesn't fail the test.
|
|
155
|
+
- A failed `recordNode` call rejects.
|
|
156
|
+
- A failed automatic recording fails the test at the end.
|
|
157
|
+
- Pages left before capture, and pages whose layout moved during the
|
|
158
|
+
screenshot, don't fail the test.
|
|
156
159
|
|
|
157
160
|
## Modes
|
|
158
161
|
|
|
159
|
-
Set
|
|
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. |
|
|
162
|
+
Set with `RALPH_MODE` or the `mode` option:
|
|
166
163
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
164
|
+
| Mode | What happens |
|
|
165
|
+
| -------- | --------------------------------------------------------------------------------------------------- |
|
|
166
|
+
| `off` | Default. Nothing is recorded, and no config or credentials are needed. |
|
|
167
|
+
| `local` | `@ralph` tests are recorded, and the reporter writes the merged graph locally. |
|
|
168
|
+
| `upload` | As `local`, and the reporter uploads the graph once the run ends. Default when `RALPH_UPLOAD_KEY` is set. |
|
|
170
169
|
|
|
171
|
-
|
|
172
|
-
|
|
170
|
+
Untagged tests are never recorded. In `local` or `upload` mode, calling
|
|
171
|
+
`ralph.recordNode` in an untagged test throws.
|
|
173
172
|
|
|
174
173
|
## Uploading to Ralph
|
|
175
174
|
|
|
176
|
-
1. Create an upload key for your
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
175
|
+
1. Create an upload key for your project with `/api/project/key/create`, and
|
|
176
|
+
keep it in your CI secrets.
|
|
177
|
+
2. Set these as environment variables, or `apiUrl` and `appId` as
|
|
178
|
+
[reporter options](#reporter-options):
|
|
180
179
|
|
|
181
|
-
| Variable |
|
|
182
|
-
| ------------------ |
|
|
183
|
-
| `RALPH_UPLOAD_KEY` |
|
|
184
|
-
| `RALPH_API_URL` |
|
|
185
|
-
| `RALPH_APP_ID` |
|
|
180
|
+
| Variable | Value |
|
|
181
|
+
| ------------------ | ----------------------------------------------- |
|
|
182
|
+
| `RALPH_UPLOAD_KEY` | The upload key. Environment only. |
|
|
183
|
+
| `RALPH_API_URL` | Your Ralph server. HTTPS, or HTTP on localhost. |
|
|
184
|
+
| `RALPH_APP_ID` | The app the recordings belong to. |
|
|
186
185
|
|
|
187
186
|
3. Run:
|
|
188
187
|
|
|
189
188
|
```sh
|
|
190
|
-
RALPH_MODE=upload
|
|
191
|
-
pnpm exec playwright test
|
|
189
|
+
RALPH_MODE=upload pnpm exec playwright test
|
|
192
190
|
```
|
|
193
191
|
|
|
194
|
-
|
|
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.
|
|
192
|
+
The reporter uploads the whole run once and prints the link:
|
|
198
193
|
|
|
199
|
-
|
|
200
|
-
|
|
194
|
+
```
|
|
195
|
+
Ralph: uploaded the run's raw graph. https://dash.ralphralph.ai/uploads/0f6c6a2e-…
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
- Retried tests contribute only their final attempt.
|
|
199
|
+
- Every test in a run must use the same Ralph config, `runId`, and `buildId`.
|
|
200
|
+
- If the upload fails, the run fails.
|
|
201
|
+
|
|
202
|
+
### Sharded runs
|
|
201
203
|
|
|
202
|
-
|
|
204
|
+
Use the `blob` reporter on each shard, then merge with the Ralph reporter:
|
|
205
|
+
|
|
206
|
+
```sh
|
|
207
|
+
# Each shard (no upload credentials needed):
|
|
208
|
+
RALPH_MODE=upload pnpm exec playwright test --shard=1/4 --reporter blob
|
|
209
|
+
|
|
210
|
+
# Once, after collecting every shard's blob-report/ into all-blob-reports/:
|
|
211
|
+
RALPH_MODE=upload RALPH_UPLOAD_KEY=… RALPH_API_URL=… RALPH_APP_ID=… \
|
|
212
|
+
pnpm exec playwright merge-reports --reporter @ralphralphai/playwright/reporter ./all-blob-reports
|
|
213
|
+
```
|
|
203
214
|
|
|
204
215
|
## Screen sizes
|
|
205
216
|
|
|
206
|
-
|
|
207
|
-
its own Playwright project. `ralphProjects` expands one project into one per
|
|
208
|
-
size:
|
|
217
|
+
Use `ralphProjects` to create one project per entry in `screenSizes`:
|
|
209
218
|
|
|
210
219
|
```ts
|
|
211
220
|
import { defineConfig, devices } from '@playwright/test';
|
|
@@ -213,10 +222,8 @@ import { ralphProjects, type RalphFixtures } from '@ralphralphai/playwright';
|
|
|
213
222
|
|
|
214
223
|
export default defineConfig<RalphFixtures>({
|
|
215
224
|
projects: [
|
|
216
|
-
// Ordinary tests keep their single project.
|
|
217
225
|
{ name: 'chromium', grepInvert: /@ralph\b/, use: devices['Desktop Chrome'] },
|
|
218
|
-
//
|
|
219
|
-
// "chromium 375x812" and "chromium 1280x720".
|
|
226
|
+
// Creates "chromium 375x812", "chromium 1280x720", …
|
|
220
227
|
...ralphProjects({
|
|
221
228
|
name: 'chromium',
|
|
222
229
|
grep: /@ralph\b/,
|
|
@@ -226,34 +233,23 @@ export default defineConfig<RalphFixtures>({
|
|
|
226
233
|
});
|
|
227
234
|
```
|
|
228
235
|
|
|
229
|
-
`ralphProjects` reads `ralph.jsonc` from the current directory.
|
|
230
|
-
|
|
231
|
-
an inline `config`, as the second argument. Keep it pointing at the same config
|
|
232
|
-
as `ralphOptions`.
|
|
236
|
+
`ralphProjects` reads `ralph.jsonc` from the current directory. Otherwise pass
|
|
237
|
+
`{ root: import.meta.dirname }` or an inline `config` as the second argument.
|
|
233
238
|
|
|
234
|
-
A test runs at every size
|
|
235
|
-
|
|
236
|
-
`@ralph-screen:WIDTHxHEIGHT`, once per size, to run it only at those sizes:
|
|
239
|
+
A test runs at every size. To run it only at some sizes, tag it or its
|
|
240
|
+
`describe` block with `@ralph-screen:WIDTHxHEIGHT`, once per size:
|
|
237
241
|
|
|
238
242
|
```ts
|
|
239
|
-
test(
|
|
240
|
-
'
|
|
241
|
-
|
|
242
|
-
async ({ page }) => {
|
|
243
|
-
await page.getByRole('button', { name: 'Menu' }).click();
|
|
244
|
-
},
|
|
245
|
-
);
|
|
243
|
+
test('mobile navigation', { tag: ['@ralph', '@ralph-screen:375x812'] }, async ({ page }) => {
|
|
244
|
+
await page.getByRole('button', { name: 'Menu' }).click();
|
|
245
|
+
});
|
|
246
246
|
|
|
247
247
|
test.describe('desktop layouts', { tag: '@ralph-screen:1280x720' }, () => {
|
|
248
248
|
test('sidebar', { tag: '@ralph' }, async ({ page }) => {});
|
|
249
249
|
});
|
|
250
250
|
```
|
|
251
251
|
|
|
252
|
-
|
|
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.
|
|
252
|
+
A size missing from `screenSizes` fails the test.
|
|
257
253
|
|
|
258
254
|
To run one size: `playwright test --project "chromium 375x812"`.
|
|
259
255
|
|
|
@@ -262,55 +258,46 @@ To run one size: `playwright test --project "chromium 375x812"`.
|
|
|
262
258
|
Set `ralphOptions` in `playwright.config.ts`, or per file with `test.use`:
|
|
263
259
|
|
|
264
260
|
```ts
|
|
265
|
-
import { defineConfig } from '@playwright/test';
|
|
266
|
-
import type { RalphFixtures } from '@ralphralphai/playwright';
|
|
267
|
-
|
|
268
261
|
export default defineConfig<RalphFixtures>({
|
|
269
262
|
use: {
|
|
270
|
-
ralphOptions: {
|
|
271
|
-
mode: 'local',
|
|
272
|
-
configPath: './ralph.jsonc',
|
|
273
|
-
buildId: process.env.GIT_SHA,
|
|
274
|
-
settleMs: 300,
|
|
275
|
-
},
|
|
263
|
+
ralphOptions: { mode: 'local', buildId: process.env.GIT_SHA, settleMs: 300 },
|
|
276
264
|
},
|
|
277
265
|
});
|
|
278
266
|
```
|
|
279
267
|
|
|
280
|
-
| Option | Default
|
|
281
|
-
| ----------------- |
|
|
282
|
-
| `mode` | `RALPH_MODE`, else `off` | `off`, `local`, or `upload`. See [Modes](#modes).
|
|
283
|
-
| `configPath` | `ralph.jsonc`
|
|
284
|
-
| `config` |
|
|
285
|
-
| `
|
|
286
|
-
| `
|
|
287
|
-
| `
|
|
288
|
-
| `
|
|
289
|
-
| `
|
|
290
|
-
| `
|
|
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:
|
|
268
|
+
| Option | Default | What it does |
|
|
269
|
+
| ----------------- | ------------------------ | ----------------------------------------------------------------------------------------------- |
|
|
270
|
+
| `mode` | `RALPH_MODE`, else `off` | `off`, `local`, or `upload`. See [Modes](#modes). |
|
|
271
|
+
| `configPath` | `ralph.jsonc` | Path to the config, relative to the Playwright config. |
|
|
272
|
+
| `config` | | An inline config instead of a file (type: `WebRalphConfig`). |
|
|
273
|
+
| `buildId` | `RALPH_BUILD_ID` | Your app's build, to line up recordings with its analytics. |
|
|
274
|
+
| `runId` | `RALPH_RUN_ID` | Identifies the CI run. |
|
|
275
|
+
| `settleMs` | 150 | Wait after the DOM loads before an automatic recording. |
|
|
276
|
+
| `recordTimeoutMs` | 5000 | Budget for one recording, including `settleMs` and `ready`. Must exceed `settleMs`. |
|
|
277
|
+
| `ready` | | `(page, signal) => Promise<void>`. Waits for your app before an automatic recording. Honor `signal`. |
|
|
278
|
+
| `reduceMotion` | `false` | Emulates `prefers-reduced-motion: reduce` and waits for finite animations before recording. |
|
|
302
279
|
|
|
303
|
-
|
|
304
|
-
import type { WebRalphConfig } from '@ralphralphai/playwright';
|
|
280
|
+
## Reporter options
|
|
305
281
|
|
|
306
|
-
|
|
282
|
+
```ts
|
|
283
|
+
reporter: [['list'], ['@ralphralphai/playwright/reporter', { appId: 'my-app' }]],
|
|
307
284
|
```
|
|
308
285
|
|
|
286
|
+
| Option | Default | What it does |
|
|
287
|
+
| ----------------- | ------------------------------------------------- | ----------------------------------------------- |
|
|
288
|
+
| `mode` | `RALPH_MODE`, else `upload` if `RALPH_UPLOAD_KEY` | `upload` uploads the run. |
|
|
289
|
+
| `apiUrl` | `RALPH_API_URL` | Your Ralph server. |
|
|
290
|
+
| `appId` | `RALPH_APP_ID` | The app the recordings belong to. |
|
|
291
|
+
| `uploadTimeoutMs` | 120000 | Upload timeout. |
|
|
292
|
+
| `outputDir` | `ralph/` in the first project's output directory | Where the merged graph is written. |
|
|
293
|
+
|
|
294
|
+
The run is also uploaded when any test ran in `upload` mode.
|
|
295
|
+
|
|
309
296
|
## Limits
|
|
310
297
|
|
|
311
|
-
Per
|
|
312
|
-
data
|
|
313
|
-
|
|
298
|
+
Per run: 5,000 screenshots, 256 MiB of screenshots, 16 MiB of other recorded
|
|
299
|
+
data, 256 MiB compressed, 20,000 recorded pages, and 5,000 recorded pages per
|
|
300
|
+
test.
|
|
314
301
|
|
|
315
302
|
## License
|
|
316
303
|
|