@ralphralphai/playwright 0.2.0 → 1.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/LICENSE +201 -0
- package/README.md +126 -147
- package/dist/index.cjs +23 -584
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +93 -45
- package/dist/index.mjs +16 -554
- package/dist/index.mjs.map +1 -1
- package/dist/reporter.cjs +185 -0
- package/dist/reporter.cjs.map +1 -0
- package/dist/reporter.d.cts +1 -0
- package/dist/reporter.d.mts +53 -0
- package/dist/reporter.mjs +183 -0
- package/dist/reporter.mjs.map +1 -0
- package/dist/upload-DPYMUP9Y.mjs +702 -0
- package/dist/upload-DPYMUP9Y.mjs.map +1 -0
- package/dist/upload-DxF1r2db.cjs +816 -0
- package/dist/upload-DxF1r2db.cjs.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,25 @@ 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 with your upload key** (see [Uploading to Ralph](#uploading-to-ralph)):
|
|
54
54
|
|
|
55
55
|
```sh
|
|
56
|
-
|
|
56
|
+
RALPH_UPLOAD_KEY=… RALPH_APP_ID=… pnpm exec playwright test
|
|
57
57
|
```
|
|
58
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
59
|
## Tag your elements
|
|
63
60
|
|
|
64
|
-
|
|
65
|
-
it can place heatmap data on the screenshot. Use the same ids your
|
|
61
|
+
Add `data-track-id` to elements, using the same ids as your
|
|
66
62
|
[`@ralphralphai/tracker`](https://www.npmjs.com/package/@ralphralphai/tracker)
|
|
67
|
-
setup
|
|
63
|
+
setup:
|
|
68
64
|
|
|
69
65
|
```html
|
|
70
66
|
<button data-track-id="add-to-cart">Add to cart</button>
|
|
@@ -72,21 +68,15 @@ setup uses:
|
|
|
72
68
|
|
|
73
69
|
## Recording pages
|
|
74
70
|
|
|
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:
|
|
79
|
-
|
|
80
|
-
- full page loads, redirects (the final page), and SPA route changes
|
|
81
|
-
- query-string and hash changes
|
|
82
|
-
- new tabs and popups
|
|
71
|
+
### Automatically
|
|
83
72
|
|
|
84
|
-
|
|
85
|
-
|
|
73
|
+
A page is recorded whenever the URL changes in any tab of the test: page loads,
|
|
74
|
+
redirects (the final page), SPA route changes, query and hash changes, and new
|
|
75
|
+
tabs and popups. Reloads, iframes, `about:blank`, and changes that keep the URL
|
|
76
|
+
are not recorded.
|
|
86
77
|
|
|
87
|
-
Each recording waits for the DOM to load
|
|
88
|
-
|
|
89
|
-
a page is ready:
|
|
78
|
+
Each recording waits for the DOM to load plus `settleMs` (150 ms). If your app
|
|
79
|
+
needs longer, use `ready`:
|
|
90
80
|
|
|
91
81
|
```ts
|
|
92
82
|
ralphOptions: {
|
|
@@ -96,10 +86,13 @@ ralphOptions: {
|
|
|
96
86
|
},
|
|
97
87
|
```
|
|
98
88
|
|
|
89
|
+
CSS animations and transitions are frozen for the screenshot. For animations
|
|
90
|
+
driven from JavaScript, set `reduceMotion` if your app honors
|
|
91
|
+
`prefers-reduced-motion`.
|
|
92
|
+
|
|
99
93
|
### Explicitly, for states at the same URL
|
|
100
94
|
|
|
101
|
-
|
|
102
|
-
them with `ralph.recordNode`:
|
|
95
|
+
For dialogs, menus, and tabs, call `ralph.recordNode` after your assertions:
|
|
103
96
|
|
|
104
97
|
```ts
|
|
105
98
|
test('shipping options', { tag: '@ralph' }, async ({ page, ralph }) => {
|
|
@@ -114,98 +107,107 @@ test('shipping options', { tag: '@ralph' }, async ({ page, ralph }) => {
|
|
|
114
107
|
});
|
|
115
108
|
```
|
|
116
109
|
|
|
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.
|
|
110
|
+
`url` only changes what is recorded; the browser doesn't navigate.
|
|
121
111
|
|
|
122
|
-
### Waiting for
|
|
112
|
+
### Waiting for capture
|
|
123
113
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
leaving it, wait for pending recordings first:
|
|
114
|
+
Recordings run in the background. If the test leaves a page before its
|
|
115
|
+
screenshot is done, the page is `uncaptured`: it's in the graph without a
|
|
116
|
+
screenshot. To make sure a page is captured before leaving it:
|
|
128
117
|
|
|
129
118
|
```ts
|
|
130
119
|
await page.goto('https://example.com/pricing');
|
|
131
|
-
await ralph.
|
|
120
|
+
await ralph.waitForCapture();
|
|
132
121
|
await page.goto('https://example.com/signup');
|
|
133
122
|
```
|
|
134
123
|
|
|
135
|
-
###
|
|
124
|
+
### Pausing capture
|
|
125
|
+
|
|
126
|
+
To leave steps out of the graph:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
await page.goto('https://example.com/a');
|
|
130
|
+
ralph.pause();
|
|
131
|
+
await page.goto('https://example.com/b'); // not recorded
|
|
132
|
+
await page.goto('https://example.com/c'); // not recorded
|
|
133
|
+
ralph.resume();
|
|
134
|
+
await page.goto('https://example.com/d'); // recorded, linked from /a
|
|
135
|
+
```
|
|
136
136
|
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
This works across tabs. While paused, `recordNode` does nothing. `resume` does
|
|
138
|
+
not record the current page.
|
|
139
|
+
|
|
140
|
+
### Browser contexts you create yourself
|
|
139
141
|
|
|
140
142
|
```ts
|
|
141
143
|
const context = await browser.newContext();
|
|
142
144
|
ralph.observe(context);
|
|
143
145
|
// ...
|
|
144
|
-
await ralph.
|
|
146
|
+
await ralph.waitForCapture();
|
|
145
147
|
await context.close();
|
|
146
148
|
```
|
|
147
149
|
|
|
148
150
|
### When recording fails
|
|
149
151
|
|
|
150
|
-
-
|
|
151
|
-
|
|
152
|
-
-
|
|
153
|
-
|
|
154
|
-
- A superseded recording, or a page whose layout moved during its screenshot,
|
|
155
|
-
doesn't fail the test.
|
|
152
|
+
- A failed `recordNode` call rejects.
|
|
153
|
+
- A failed automatic recording fails the test at the end.
|
|
154
|
+
- Pages left before capture, and pages whose layout moved during the
|
|
155
|
+
screenshot, don't fail the test.
|
|
156
156
|
|
|
157
157
|
## Modes
|
|
158
158
|
|
|
159
|
-
Set
|
|
159
|
+
Set with `RALPH_MODE` or the `mode` option:
|
|
160
160
|
|
|
161
|
-
| Mode | What happens
|
|
162
|
-
| -------- |
|
|
163
|
-
| `off` | Default. Nothing is recorded, no
|
|
164
|
-
| `
|
|
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. |
|
|
161
|
+
| Mode | What happens |
|
|
162
|
+
| -------- | ----------------------------------------------------------------------------------------------- |
|
|
163
|
+
| `off` | Default. Nothing is recorded, and no config or credentials are needed. |
|
|
164
|
+
| `upload` | `@ralph` tests are recorded, and the reporter uploads the run. Default when `RALPH_UPLOAD_KEY` is set. |
|
|
166
165
|
|
|
167
|
-
|
|
168
|
-
`
|
|
169
|
-
to add the tag.
|
|
170
|
-
|
|
171
|
-
Tests that import from `@playwright/test` directly keep working alongside Ralph
|
|
172
|
-
tests.
|
|
166
|
+
Untagged tests are never recorded. In `upload` mode, calling
|
|
167
|
+
`ralph.recordNode` in an untagged test throws.
|
|
173
168
|
|
|
174
169
|
## Uploading to Ralph
|
|
175
170
|
|
|
176
|
-
1. Create an upload key for your
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
2. Set these, as environment variables or options:
|
|
171
|
+
1. Create an upload key for your project in the project dashboard.
|
|
172
|
+
2. Set these as environment variables, or `appId` as
|
|
173
|
+
[reporter options](#reporter-options):
|
|
180
174
|
|
|
181
|
-
| Variable |
|
|
182
|
-
| ------------------ |
|
|
183
|
-
| `RALPH_UPLOAD_KEY` |
|
|
184
|
-
| `
|
|
185
|
-
| `RALPH_APP_ID` | `appId` | The app these recordings belong to |
|
|
175
|
+
| Variable | Value |
|
|
176
|
+
| ------------------ | -------------------------------------------------- |
|
|
177
|
+
| `RALPH_UPLOAD_KEY` | The upload key. Environment only. |
|
|
178
|
+
| `RALPH_APP_ID` | The app the recordings belong to. |
|
|
186
179
|
|
|
187
180
|
3. Run:
|
|
188
181
|
|
|
189
182
|
```sh
|
|
190
|
-
RALPH_MODE=upload
|
|
191
|
-
pnpm exec playwright test
|
|
183
|
+
RALPH_MODE=upload pnpm exec playwright test
|
|
192
184
|
```
|
|
193
185
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
186
|
+
The reporter uploads the whole run once. It prints the result link before the
|
|
187
|
+
upload starts, so you can open it straight away and watch the run upload and
|
|
188
|
+
then be processed:
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
Ralph: uploading the run. Follow the upload and processing at https://dash.ralphralph.ai/uploads/0f6c6a2e-…
|
|
192
|
+
Ralph: uploaded the run. Ralph is processing it at https://dash.ralphralph.ai/uploads/0f6c6a2e-…
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Sharded runs
|
|
196
|
+
|
|
197
|
+
Use the `blob` reporter on each shard, then merge with the Ralph reporter:
|
|
198
198
|
|
|
199
|
-
|
|
200
|
-
|
|
199
|
+
```sh
|
|
200
|
+
# Each shard (no upload credentials needed):
|
|
201
|
+
RALPH_MODE=upload pnpm exec playwright test --shard=1/4 --reporter blob
|
|
201
202
|
|
|
202
|
-
|
|
203
|
+
# Once, after collecting every shard's blob-report/ into all-blob-reports/:
|
|
204
|
+
RALPH_MODE=upload RALPH_UPLOAD_KEY=… RALPH_APP_ID=… \
|
|
205
|
+
pnpm exec playwright merge-reports --reporter @ralphralphai/playwright/reporter ./all-blob-reports
|
|
206
|
+
```
|
|
203
207
|
|
|
204
208
|
## Screen sizes
|
|
205
209
|
|
|
206
|
-
|
|
207
|
-
its own Playwright project. `ralphProjects` expands one project into one per
|
|
208
|
-
size:
|
|
210
|
+
Use `ralphProjects` to create one project per entry in `screenSizes`:
|
|
209
211
|
|
|
210
212
|
```ts
|
|
211
213
|
import { defineConfig, devices } from '@playwright/test';
|
|
@@ -213,10 +215,8 @@ import { ralphProjects, type RalphFixtures } from '@ralphralphai/playwright';
|
|
|
213
215
|
|
|
214
216
|
export default defineConfig<RalphFixtures>({
|
|
215
217
|
projects: [
|
|
216
|
-
// Ordinary tests keep their single project.
|
|
217
218
|
{ name: 'chromium', grepInvert: /@ralph\b/, use: devices['Desktop Chrome'] },
|
|
218
|
-
//
|
|
219
|
-
// "chromium 375x812" and "chromium 1280x720".
|
|
219
|
+
// Creates "chromium 375x812", "chromium 1280x720", …
|
|
220
220
|
...ralphProjects({
|
|
221
221
|
name: 'chromium',
|
|
222
222
|
grep: /@ralph\b/,
|
|
@@ -226,34 +226,23 @@ export default defineConfig<RalphFixtures>({
|
|
|
226
226
|
});
|
|
227
227
|
```
|
|
228
228
|
|
|
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`.
|
|
229
|
+
`ralphProjects` reads `ralph.jsonc` from the current directory. Otherwise pass
|
|
230
|
+
`{ root: import.meta.dirname }` or an inline `config` as the second argument.
|
|
233
231
|
|
|
234
|
-
A test runs at every size
|
|
235
|
-
|
|
236
|
-
`@ralph-screen:WIDTHxHEIGHT`, once per size, to run it only at those sizes:
|
|
232
|
+
A test runs at every size. To run it only at some sizes, tag it or its
|
|
233
|
+
`describe` block with `@ralph-screen:WIDTHxHEIGHT`, once per size:
|
|
237
234
|
|
|
238
235
|
```ts
|
|
239
|
-
test(
|
|
240
|
-
'
|
|
241
|
-
|
|
242
|
-
async ({ page }) => {
|
|
243
|
-
await page.getByRole('button', { name: 'Menu' }).click();
|
|
244
|
-
},
|
|
245
|
-
);
|
|
236
|
+
test('mobile navigation', { tag: ['@ralph', '@ralph-screen:375x812'] }, async ({ page }) => {
|
|
237
|
+
await page.getByRole('button', { name: 'Menu' }).click();
|
|
238
|
+
});
|
|
246
239
|
|
|
247
240
|
test.describe('desktop layouts', { tag: '@ralph-screen:1280x720' }, () => {
|
|
248
241
|
test('sidebar', { tag: '@ralph' }, async ({ page }) => {});
|
|
249
242
|
});
|
|
250
243
|
```
|
|
251
244
|
|
|
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.
|
|
245
|
+
A size missing from `screenSizes` fails the test.
|
|
257
246
|
|
|
258
247
|
To run one size: `playwright test --project "chromium 375x812"`.
|
|
259
248
|
|
|
@@ -262,55 +251,45 @@ To run one size: `playwright test --project "chromium 375x812"`.
|
|
|
262
251
|
Set `ralphOptions` in `playwright.config.ts`, or per file with `test.use`:
|
|
263
252
|
|
|
264
253
|
```ts
|
|
265
|
-
import { defineConfig } from '@playwright/test';
|
|
266
|
-
import type { RalphFixtures } from '@ralphralphai/playwright';
|
|
267
|
-
|
|
268
254
|
export default defineConfig<RalphFixtures>({
|
|
269
255
|
use: {
|
|
270
|
-
ralphOptions: {
|
|
271
|
-
mode: 'local',
|
|
272
|
-
configPath: './ralph.jsonc',
|
|
273
|
-
buildId: process.env.GIT_SHA,
|
|
274
|
-
settleMs: 300,
|
|
275
|
-
},
|
|
256
|
+
ralphOptions: { buildId: process.env.GIT_SHA, settleMs: 300 },
|
|
276
257
|
},
|
|
277
258
|
});
|
|
278
259
|
```
|
|
279
260
|
|
|
280
|
-
| Option | Default
|
|
281
|
-
| ----------------- |
|
|
282
|
-
| `mode` | `RALPH_MODE`, else `off` | `off
|
|
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:
|
|
261
|
+
| Option | Default | What it does |
|
|
262
|
+
| ----------------- | ------------------------ | ----------------------------------------------------------------------------------------------- |
|
|
263
|
+
| `mode` | `RALPH_MODE`, else `off` | `off` or `upload`. See [Modes](#modes). |
|
|
264
|
+
| `configPath` | `ralph.jsonc` | Path to the config, relative to the Playwright config. |
|
|
265
|
+
| `config` | | An inline config instead of a file (type: `WebRalphConfig`). |
|
|
266
|
+
| `buildId` | `RALPH_BUILD_ID` | Your app's build, to line up recordings with its analytics. |
|
|
267
|
+
| `runId` | `RALPH_RUN_ID` | Identifies the CI run. |
|
|
268
|
+
| `settleMs` | 150 | Wait after the DOM loads before an automatic recording. |
|
|
269
|
+
| `recordTimeoutMs` | 5000 | Budget for one recording, including `settleMs` and `ready`. Must exceed `settleMs`. |
|
|
270
|
+
| `ready` | | `(page, signal) => Promise<void>`. Waits for your app before an automatic recording. Honor `signal`. |
|
|
271
|
+
| `reduceMotion` | `false` | Emulates `prefers-reduced-motion: reduce` and waits for finite animations before recording. |
|
|
302
272
|
|
|
303
|
-
|
|
304
|
-
import type { WebRalphConfig } from '@ralphralphai/playwright';
|
|
273
|
+
## Reporter options
|
|
305
274
|
|
|
306
|
-
|
|
275
|
+
```ts
|
|
276
|
+
reporter: [['list'], ['@ralphralphai/playwright/reporter', { appId: 'my-app' }]],
|
|
307
277
|
```
|
|
308
278
|
|
|
279
|
+
| Option | Default | What it does |
|
|
280
|
+
| ----------------- | ------------------------------------------------- | ----------------------------------------------- |
|
|
281
|
+
| `mode` | `RALPH_MODE`, else `upload` if `RALPH_UPLOAD_KEY` | `upload` uploads the run. |
|
|
282
|
+
| `appId` | `RALPH_APP_ID` | The app the recordings belong to. |
|
|
283
|
+
| `uploadTimeoutMs` | 120000 | Upload timeout. |
|
|
284
|
+
| `outputDir` | `ralph/` in the first project's output directory | Where the merged graph is written. |
|
|
285
|
+
|
|
286
|
+
The run is also uploaded when any test ran in `upload` mode.
|
|
287
|
+
|
|
309
288
|
## Limits
|
|
310
289
|
|
|
311
|
-
Per
|
|
312
|
-
data
|
|
313
|
-
|
|
290
|
+
Per run: 5,000 screenshots, 256 MiB of screenshots, 16 MiB of other recorded
|
|
291
|
+
data, 256 MiB compressed, 20,000 recorded pages, and 5,000 recorded pages per
|
|
292
|
+
test.
|
|
314
293
|
|
|
315
294
|
## License
|
|
316
295
|
|