@ripplo/testing 0.0.1
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 +386 -0
- package/dist/actions.d.ts +233 -0
- package/dist/actions.js +116 -0
- package/dist/assert.d.ts +182 -0
- package/dist/assert.js +83 -0
- package/dist/builder-DTWMrbuv.d.ts +133 -0
- package/dist/chunk-2VUWFRR5.js +20 -0
- package/dist/chunk-DCJBLS2U.js +26 -0
- package/dist/chunk-KWUKVAGI.js +227 -0
- package/dist/chunk-MGATMMCZ.js +16 -0
- package/dist/chunk-X2FROZPN.js +149 -0
- package/dist/compiler.d.ts +22 -0
- package/dist/compiler.js +7 -0
- package/dist/control.d.ts +24 -0
- package/dist/control.js +27 -0
- package/dist/express.d.ts +12 -0
- package/dist/express.js +102 -0
- package/dist/fastify.d.ts +12 -0
- package/dist/fastify.js +72 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.js +464 -0
- package/dist/locators.d.ts +30 -0
- package/dist/locators.js +10 -0
- package/dist/nextjs.d.ts +12 -0
- package/dist/nextjs.js +105 -0
- package/dist/step-DLfkKI3V.d.ts +19 -0
- package/package.json +94 -0
package/README.md
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
# @ripplo/testing
|
|
2
|
+
|
|
3
|
+
Typed TypeScript DSL for defining end-to-end tests with [Ripplo](https://ripplo.ai).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install @ripplo/testing
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
1. Run `npx ripplo` to authenticate and scaffold a `.ripplo/` directory in your project
|
|
14
|
+
2. Define preconditions in `.ripplo/preconditions.ts` — these set up test data (users, projects, etc.)
|
|
15
|
+
3. Write tests in `.ripplo/tests/` — each file defines one user flow to test
|
|
16
|
+
4. Run `npx ripplo lint` to validate, `npx ripplo run` to execute
|
|
17
|
+
|
|
18
|
+
Every test gets a clean slate via preconditions — no shared state, no ordering dependencies, fully parallelizable.
|
|
19
|
+
|
|
20
|
+
## DSL API
|
|
21
|
+
|
|
22
|
+
### Test Builder
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import ripplo from "../ripplo.js";
|
|
26
|
+
import { dataProject } from "../preconditions.js";
|
|
27
|
+
|
|
28
|
+
ripplo
|
|
29
|
+
.test("delete-project")
|
|
30
|
+
.name("Delete a project")
|
|
31
|
+
.requires({ project: dataProject })
|
|
32
|
+
.expectedOutcome("Project deleted and user redirected to connect page")
|
|
33
|
+
.startsAt(({ project }) => `/projects/${project.projectId}/settings`)
|
|
34
|
+
.steps(({ project }) => [
|
|
35
|
+
// steps here
|
|
36
|
+
]);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Chain:** `.test(id)` → `.name(display)` → `.requires(preconditions)` → `.expectedOutcome(text)` → `.startsAt(urlFn)` → `.steps(stepsFn)`
|
|
40
|
+
|
|
41
|
+
Use `.notImplemented()` instead of `.startsAt()` + `.steps()` to stub a test during planning.
|
|
42
|
+
|
|
43
|
+
### Locators
|
|
44
|
+
|
|
45
|
+
Only two locator types are available. ARIA roles are strongly preferred.
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { role, testId } from "@ripplo/testing/locators";
|
|
49
|
+
|
|
50
|
+
role("button", "Save"); // ARIA role + accessible name (preferred)
|
|
51
|
+
role("heading", "Settings"); // role without interaction
|
|
52
|
+
role("textbox", "Email"); // input by role
|
|
53
|
+
role("combobox", "Country"); // select/dropdown by role
|
|
54
|
+
testId("workflow-checkbox"); // data-testid (fallback only)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Available ARIA roles:** `alert`, `alertdialog`, `button`, `checkbox`, `combobox`, `dialog`, `form`, `grid`, `heading`, `img`, `link`, `list`, `listbox`, `listitem`, `menu`, `menuitem`, `navigation`, `option`, `progressbar`, `radio`, `region`, `row`, `searchbox`, `separator`, `slider`, `spinbutton`, `status`, `switch`, `tab`, `tabpanel`, `textbox`, `toolbar`, `tooltip`, `tree`, `treeitem`
|
|
58
|
+
|
|
59
|
+
**Type-safe constraints:**
|
|
60
|
+
|
|
61
|
+
- `InputLocator` accepts: `role("textbox")`, `role("searchbox")`, `role("combobox")`, `role("spinbutton")`, `testId()`
|
|
62
|
+
- `SelectLocator` accepts: `role("combobox")`, `role("listbox")`, `testId()`
|
|
63
|
+
- `CheckLocator` accepts: `role("checkbox")`, `role("switch")`, `testId()`
|
|
64
|
+
|
|
65
|
+
### Actions
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import {
|
|
69
|
+
click,
|
|
70
|
+
fill,
|
|
71
|
+
select,
|
|
72
|
+
check,
|
|
73
|
+
uncheck,
|
|
74
|
+
hover,
|
|
75
|
+
press,
|
|
76
|
+
navigate,
|
|
77
|
+
dblclick,
|
|
78
|
+
focus,
|
|
79
|
+
clear,
|
|
80
|
+
typeText,
|
|
81
|
+
rightClick,
|
|
82
|
+
scrollIntoView,
|
|
83
|
+
drag,
|
|
84
|
+
upload,
|
|
85
|
+
handleDialog,
|
|
86
|
+
clipboard,
|
|
87
|
+
setPermission,
|
|
88
|
+
setViewport,
|
|
89
|
+
} from "@ripplo/testing/actions";
|
|
90
|
+
|
|
91
|
+
navigate("/settings"); // Go to URL
|
|
92
|
+
click(role("button", "Save")); // Click element
|
|
93
|
+
fill(role("textbox", "Email"), "test@x.com"); // Clear + type into input
|
|
94
|
+
select(role("combobox", "Role"), "admin"); // Select option
|
|
95
|
+
check(role("checkbox", "Terms")); // Check checkbox/switch
|
|
96
|
+
uncheck(role("switch", "Notifications")); // Uncheck
|
|
97
|
+
hover(role("button", "Info")); // Hover
|
|
98
|
+
press("Enter"); // Press key
|
|
99
|
+
focus(role("searchbox", "Search")); // Focus element
|
|
100
|
+
dblclick(role("button", "Edit")); // Double click
|
|
101
|
+
clear(role("textbox", "Search")); // Clear input
|
|
102
|
+
upload(testId("file-input"), "./test.png"); // Upload file
|
|
103
|
+
drag(role("row", "Item 1"), role("row", "Item 2")); // Drag and drop
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Assertions
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { assert } from "@ripplo/testing/assert";
|
|
110
|
+
|
|
111
|
+
assert.visible(role("heading", "Settings")); // Element visible
|
|
112
|
+
assert.not.visible(role("dialog")); // Element not visible
|
|
113
|
+
assert.text(role("status"), "3 / 5 runs"); // Exact text match
|
|
114
|
+
assert.url("/projects/abc/settings"); // Exact URL match
|
|
115
|
+
assert.enabled(role("button", "Submit")); // Element enabled
|
|
116
|
+
assert.disabled(role("button", "Submit")); // Element disabled
|
|
117
|
+
assert.checked(role("checkbox", "Terms")); // Checked
|
|
118
|
+
assert.not.checked(role("switch", "Dark mode")); // Not checked
|
|
119
|
+
assert.focused(role("textbox", "Search")); // Has focus
|
|
120
|
+
assert.not.focused(role("textbox", "Search")); // No focus
|
|
121
|
+
assert.count(testId("row"), 5); // Element count
|
|
122
|
+
assert.attribute(role("link", "Docs"), "href", "/docs"); // Attribute value
|
|
123
|
+
assert.value(role("textbox", "Email"), "test@x.com"); // Input value
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
All text/URL assertions use **exact matching only** (`equals` operator). No `contains`, `startsWith`, or regex.
|
|
127
|
+
|
|
128
|
+
### Variables & Extraction
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { extract, variable } from "@ripplo/testing/control";
|
|
132
|
+
|
|
133
|
+
const token = variable("token");
|
|
134
|
+
extract(testId("token-value"), token).as("capture token");
|
|
135
|
+
// token can be referenced in subsequent steps
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Step Labels
|
|
139
|
+
|
|
140
|
+
Every step **must** have a `.as("description")` label:
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
click(role("button", "Save")).as("save the form");
|
|
144
|
+
assert.visible(role("status", "Saved")).as("verify save confirmation");
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Precondition System
|
|
148
|
+
|
|
149
|
+
Preconditions declare test data requirements with typed contracts:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import ripplo from "./ripplo.js";
|
|
153
|
+
|
|
154
|
+
export const authLoggedIn = ripplo
|
|
155
|
+
.precondition("auth:logged-in")
|
|
156
|
+
.description("Authenticated test user with a valid session")
|
|
157
|
+
.contract<{ userId: string }>();
|
|
158
|
+
|
|
159
|
+
export const dataProject = ripplo
|
|
160
|
+
.precondition("data:project")
|
|
161
|
+
.description("A project exists and the user is an admin member")
|
|
162
|
+
.requires({ auth: authLoggedIn })
|
|
163
|
+
.contract<{ orgId: string; projectId: string }>();
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Chain:** `.precondition(name)` → `.description(text)` → `.requires(deps)` → `.contract<T>()`
|
|
167
|
+
|
|
168
|
+
Use `.notImplemented()` instead of `.contract<T>()` for stubs.
|
|
169
|
+
|
|
170
|
+
### Data Flow
|
|
171
|
+
|
|
172
|
+
Precondition data flows into tests via destructuring:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
ripplo
|
|
176
|
+
.test("delete-project")
|
|
177
|
+
.requires({ project: dataProject })
|
|
178
|
+
.startsAt(({ project }) => `/projects/${project.projectId}/settings`)
|
|
179
|
+
.steps(({ project }) => [
|
|
180
|
+
navigate(`/projects/${project.projectId}/settings`).as("go to settings"),
|
|
181
|
+
// project.projectId, project.orgId available here
|
|
182
|
+
]);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Always destructure and use precondition data.** Never hardcode values that come from preconditions — if a precondition implementation changes, the test should not break.
|
|
186
|
+
|
|
187
|
+
### Precondition Implementation
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
ripplo.implement(authLoggedIn, {
|
|
191
|
+
setup: async (ctx, deps) => {
|
|
192
|
+
const email = ctx.uniqueEmail();
|
|
193
|
+
// Create user, set cookies via ctx.setCookie()
|
|
194
|
+
return { userId: ctx.fixed("user-123") };
|
|
195
|
+
},
|
|
196
|
+
teardown: async (ctx) => {
|
|
197
|
+
// Clean up using ctx.data
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**SetupContext provides:**
|
|
203
|
+
|
|
204
|
+
- `ctx.runId` — unique UUID for this test run
|
|
205
|
+
- `ctx.fixed(value)` — static test value
|
|
206
|
+
- `ctx.uniqueId(prefix)` — generate unique ID (e.g., `ripplo-test-abc123`)
|
|
207
|
+
- `ctx.uniqueEmail()` — generate unique email
|
|
208
|
+
- `ctx.setCookie(name, value, options?)` — inject auth cookies
|
|
209
|
+
|
|
210
|
+
## Determinism Rules
|
|
211
|
+
|
|
212
|
+
1. **Use `role()` locators exclusively.** Only use `testId()` when no ARIA role is available.
|
|
213
|
+
2. **All text assertions use exact matching.** No `contains`, `startsWith`, or regex.
|
|
214
|
+
3. **Destructure precondition data in `steps()`.** Never hardcode names, IDs, or emails that come from preconditions.
|
|
215
|
+
4. **Every step must have `.as("description")`.** No unlabeled steps.
|
|
216
|
+
5. **No duplicate labels** within a test.
|
|
217
|
+
6. **End with assertions** that verify the `expectedOutcome`.
|
|
218
|
+
7. **After a test passes, run flake detection** to verify determinism across parallel runs.
|
|
219
|
+
|
|
220
|
+
## CLI Commands
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
ripplo # Launch interactive dashboard
|
|
224
|
+
ripplo lint [slugs..] # Compile + lint tests (all or specific slugs)
|
|
225
|
+
ripplo run [slugs..] # Run tests in parallel
|
|
226
|
+
ripplo list # List tests with status
|
|
227
|
+
ripplo flake-detect <slug> --runs=10 # Run N times in parallel to detect flakiness
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Server Setup
|
|
231
|
+
|
|
232
|
+
Your application server must expose the precondition endpoints under a single path prefix (the value you pass to `createRipplo({ preconditionsUrl })`). Pick the adapter that matches your framework — each handles webhook signature verification, cookie forwarding, and request parsing for you. Wrap the mount point behind an env guard (e.g. `ENABLE_RIPPLO_TESTING=true`) so it never ships to production.
|
|
233
|
+
|
|
234
|
+
### Express
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
import express from "express";
|
|
238
|
+
import { createExpressHandler } from "@ripplo/testing/express";
|
|
239
|
+
import ripplo from "../.ripplo/ripplo.js";
|
|
240
|
+
|
|
241
|
+
const app = express();
|
|
242
|
+
app.use("/api/test/preconditions", createExpressHandler({ ripplo }));
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Mounts both `PUT /execute-batch` and `PUT /teardown` under the prefix you choose.
|
|
246
|
+
|
|
247
|
+
### Fastify
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
import Fastify from "fastify";
|
|
251
|
+
import { registerFastifyHandler } from "@ripplo/testing/fastify";
|
|
252
|
+
import ripplo from "../.ripplo/ripplo.js";
|
|
253
|
+
|
|
254
|
+
const app = Fastify();
|
|
255
|
+
await app.register(registerFastifyHandler({ ripplo }), {
|
|
256
|
+
prefix: "/api/test/preconditions",
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Next.js (App Router)
|
|
261
|
+
|
|
262
|
+
The Next.js adapter exports a single catch-all handler. Create one dynamic route file:
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
// app/api/test/preconditions/[action]/route.ts
|
|
266
|
+
import { createNextHandler } from "@ripplo/testing/nextjs";
|
|
267
|
+
import ripplo from "@/.ripplo/ripplo";
|
|
268
|
+
|
|
269
|
+
export const PUT = createNextHandler({ ripplo });
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
The handler dispatches on the last URL segment (`execute-batch` or `teardown`) and returns 404 for anything else. It depends only on the Web `Request` / `Response` types, so it runs on both the Node and Edge runtimes — no `next` import required.
|
|
273
|
+
|
|
274
|
+
### Custom integration (raw engine)
|
|
275
|
+
|
|
276
|
+
If your framework isn't covered above (Hono, Koa, Bun, Deno, Cloudflare Workers, etc.), use the raw engine directly. The adapters are thin wrappers over the same API.
|
|
277
|
+
|
|
278
|
+
```ts
|
|
279
|
+
import {
|
|
280
|
+
buildSetCookieHeader,
|
|
281
|
+
createEngine,
|
|
282
|
+
serializeCookie,
|
|
283
|
+
verifyWebhookSignature,
|
|
284
|
+
} from "@ripplo/testing";
|
|
285
|
+
import ripplo from "../.ripplo/ripplo.js";
|
|
286
|
+
|
|
287
|
+
const engine = createEngine(ripplo);
|
|
288
|
+
const webhookSecret = ripplo.getConfig().webhookSecret;
|
|
289
|
+
|
|
290
|
+
// PUT /api/test/preconditions/execute-batch
|
|
291
|
+
async function executeBatch(req: Request): Promise<Response> {
|
|
292
|
+
const body = await req.text();
|
|
293
|
+
const verified = verifyWebhookSignature(
|
|
294
|
+
body,
|
|
295
|
+
{
|
|
296
|
+
"webhook-id": req.headers.get("webhook-id") ?? undefined,
|
|
297
|
+
"webhook-signature": req.headers.get("webhook-signature") ?? undefined,
|
|
298
|
+
"webhook-timestamp": req.headers.get("webhook-timestamp") ?? undefined,
|
|
299
|
+
},
|
|
300
|
+
webhookSecret,
|
|
301
|
+
);
|
|
302
|
+
if (!verified) {
|
|
303
|
+
return new Response(JSON.stringify({ error: "Invalid signature" }), { status: 401 });
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const { preconditions } = JSON.parse(body);
|
|
307
|
+
const appUrl = `${req.headers.get("x-forwarded-proto") ?? "http"}://${req.headers.get("host")}`;
|
|
308
|
+
const result = await engine.executeBatch(preconditions, { appUrl });
|
|
309
|
+
|
|
310
|
+
const headers = new Headers({ "content-type": "application/json" });
|
|
311
|
+
result.cookies.forEach((c) => {
|
|
312
|
+
headers.append("Set-Cookie", buildSetCookieHeader(serializeCookie(c)));
|
|
313
|
+
});
|
|
314
|
+
return new Response(JSON.stringify(result), { headers });
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// PUT /api/test/preconditions/teardown
|
|
318
|
+
async function teardown(req: Request): Promise<Response> {
|
|
319
|
+
// ... same verify pattern, then:
|
|
320
|
+
// await engine.teardown(parsed.preconditions, parsed.data);
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
**You're responsible for:**
|
|
325
|
+
|
|
326
|
+
- **Webhook verification.** Always call `verifyWebhookSignature` before invoking the engine.
|
|
327
|
+
- **Routing.** Dispatch the two endpoints (`execute-batch`, `teardown`) however your framework handles routes.
|
|
328
|
+
- **Cookie forwarding.** `result.cookies` contains the cookies preconditions set during setup — they must reach the test browser as `Set-Cookie` headers, or login/session preconditions will silently fail.
|
|
329
|
+
- **Body parsing.** Use the raw text body for signature verification, then `JSON.parse` for the engine call.
|
|
330
|
+
|
|
331
|
+
### Config
|
|
332
|
+
|
|
333
|
+
After mounting, point Ripplo at the prefix:
|
|
334
|
+
|
|
335
|
+
```ts
|
|
336
|
+
// .ripplo/ripplo.ts
|
|
337
|
+
import { createRipplo } from "@ripplo/testing";
|
|
338
|
+
|
|
339
|
+
export default createRipplo({
|
|
340
|
+
appUrl: process.env.APP_URL,
|
|
341
|
+
preconditionsUrl: `${process.env.APP_URL}/api/test/preconditions`,
|
|
342
|
+
projectId: "...",
|
|
343
|
+
webhookSecret: process.env.RIPPLO_WEBHOOK_SECRET,
|
|
344
|
+
});
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Precondition API Contract
|
|
348
|
+
|
|
349
|
+
The app server exposes two endpoints for test data management:
|
|
350
|
+
|
|
351
|
+
### Execute (`PUT {preconditionApiPath}/execute`)
|
|
352
|
+
|
|
353
|
+
```json
|
|
354
|
+
// Request
|
|
355
|
+
{ "precondition": "data:project" }
|
|
356
|
+
|
|
357
|
+
// Response
|
|
358
|
+
{ "success": true, "data": { "projectId": "cuid-abc", "orgId": "org-xyz" } }
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Returns data that flows into test variables. Set-Cookie headers are captured automatically.
|
|
362
|
+
|
|
363
|
+
### Teardown (`PUT {preconditionApiPath}/teardown`)
|
|
364
|
+
|
|
365
|
+
```json
|
|
366
|
+
// Request
|
|
367
|
+
{ "preconditions": ["auth:logged-in", "data:project"] }
|
|
368
|
+
|
|
369
|
+
// Response
|
|
370
|
+
{ "success": true }
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Parallel Safety
|
|
374
|
+
|
|
375
|
+
- Generate unique names/emails per run using `crypto.randomUUID()` suffixes
|
|
376
|
+
- Return created entity IDs in the `data` response
|
|
377
|
+
- Teardown only deletes that run's data (use session cookies to identify user)
|
|
378
|
+
- Never hardcode entity names or use bulk deletion
|
|
379
|
+
|
|
380
|
+
### Webhook Signing
|
|
381
|
+
|
|
382
|
+
All requests are signed using Standard Webhooks (HMAC-SHA256). Headers: `webhook-id`, `webhook-timestamp`, `webhook-signature`. Verify before executing.
|
|
383
|
+
|
|
384
|
+
### Environment Guard
|
|
385
|
+
|
|
386
|
+
Wrap all precondition routes behind `ENABLE_RIPPLO_TESTING=true`. Never expose in production.
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { U as UnlabeledStep } from './step-DLfkKI3V.js';
|
|
2
|
+
import { CheckLocator, InputLocator, AnyLocator, SelectLocator } from './locators.js';
|
|
3
|
+
import '@ripplo/spec';
|
|
4
|
+
|
|
5
|
+
declare function navigate(url: string): UnlabeledStep<{
|
|
6
|
+
type: "goto";
|
|
7
|
+
url: {
|
|
8
|
+
type: "static";
|
|
9
|
+
value: string;
|
|
10
|
+
};
|
|
11
|
+
}>;
|
|
12
|
+
declare function click(locator: AnyLocator): UnlabeledStep<{
|
|
13
|
+
locator: {
|
|
14
|
+
by: "testId";
|
|
15
|
+
value: string;
|
|
16
|
+
} | {
|
|
17
|
+
by: "role";
|
|
18
|
+
role: string;
|
|
19
|
+
name?: string | undefined;
|
|
20
|
+
};
|
|
21
|
+
type: "click";
|
|
22
|
+
}>;
|
|
23
|
+
declare function fill(locator: InputLocator, value: string): UnlabeledStep<{
|
|
24
|
+
locator: {
|
|
25
|
+
by: "testId";
|
|
26
|
+
value: string;
|
|
27
|
+
} | {
|
|
28
|
+
by: "role";
|
|
29
|
+
role: string;
|
|
30
|
+
name?: string | undefined;
|
|
31
|
+
};
|
|
32
|
+
type: "fill";
|
|
33
|
+
value: {
|
|
34
|
+
type: "static";
|
|
35
|
+
value: string;
|
|
36
|
+
};
|
|
37
|
+
}>;
|
|
38
|
+
declare function select(locator: SelectLocator, value: string): UnlabeledStep<{
|
|
39
|
+
locator: {
|
|
40
|
+
by: "testId";
|
|
41
|
+
value: string;
|
|
42
|
+
} | {
|
|
43
|
+
by: "role";
|
|
44
|
+
role: string;
|
|
45
|
+
name?: string | undefined;
|
|
46
|
+
};
|
|
47
|
+
type: "select";
|
|
48
|
+
value: {
|
|
49
|
+
type: "static";
|
|
50
|
+
value: string;
|
|
51
|
+
};
|
|
52
|
+
}>;
|
|
53
|
+
declare function check(locator: CheckLocator): UnlabeledStep<{
|
|
54
|
+
locator: {
|
|
55
|
+
by: "testId";
|
|
56
|
+
value: string;
|
|
57
|
+
} | {
|
|
58
|
+
by: "role";
|
|
59
|
+
role: string;
|
|
60
|
+
name?: string | undefined;
|
|
61
|
+
};
|
|
62
|
+
type: "check";
|
|
63
|
+
}>;
|
|
64
|
+
declare function uncheck(locator: CheckLocator): UnlabeledStep<{
|
|
65
|
+
locator: {
|
|
66
|
+
by: "testId";
|
|
67
|
+
value: string;
|
|
68
|
+
} | {
|
|
69
|
+
by: "role";
|
|
70
|
+
role: string;
|
|
71
|
+
name?: string | undefined;
|
|
72
|
+
};
|
|
73
|
+
type: "uncheck";
|
|
74
|
+
}>;
|
|
75
|
+
declare function hover(locator: AnyLocator): UnlabeledStep<{
|
|
76
|
+
locator: {
|
|
77
|
+
by: "testId";
|
|
78
|
+
value: string;
|
|
79
|
+
} | {
|
|
80
|
+
by: "role";
|
|
81
|
+
role: string;
|
|
82
|
+
name?: string | undefined;
|
|
83
|
+
};
|
|
84
|
+
type: "hover";
|
|
85
|
+
}>;
|
|
86
|
+
declare function press(key: string): UnlabeledStep<{
|
|
87
|
+
key: string;
|
|
88
|
+
type: "press";
|
|
89
|
+
}>;
|
|
90
|
+
declare function upload(locator: AnyLocator, path: string): UnlabeledStep<{
|
|
91
|
+
files: string[];
|
|
92
|
+
locator: {
|
|
93
|
+
by: "testId";
|
|
94
|
+
value: string;
|
|
95
|
+
} | {
|
|
96
|
+
by: "role";
|
|
97
|
+
role: string;
|
|
98
|
+
name?: string | undefined;
|
|
99
|
+
};
|
|
100
|
+
type: "upload";
|
|
101
|
+
}>;
|
|
102
|
+
declare function dblclick(locator: AnyLocator): UnlabeledStep<{
|
|
103
|
+
locator: {
|
|
104
|
+
by: "testId";
|
|
105
|
+
value: string;
|
|
106
|
+
} | {
|
|
107
|
+
by: "role";
|
|
108
|
+
role: string;
|
|
109
|
+
name?: string | undefined;
|
|
110
|
+
};
|
|
111
|
+
type: "dblclick";
|
|
112
|
+
}>;
|
|
113
|
+
declare function focus(locator: AnyLocator): UnlabeledStep<{
|
|
114
|
+
locator: {
|
|
115
|
+
by: "testId";
|
|
116
|
+
value: string;
|
|
117
|
+
} | {
|
|
118
|
+
by: "role";
|
|
119
|
+
role: string;
|
|
120
|
+
name?: string | undefined;
|
|
121
|
+
};
|
|
122
|
+
type: "focus";
|
|
123
|
+
}>;
|
|
124
|
+
declare function clear(locator: InputLocator): UnlabeledStep<{
|
|
125
|
+
locator: {
|
|
126
|
+
by: "testId";
|
|
127
|
+
value: string;
|
|
128
|
+
} | {
|
|
129
|
+
by: "role";
|
|
130
|
+
role: string;
|
|
131
|
+
name?: string | undefined;
|
|
132
|
+
};
|
|
133
|
+
type: "clear";
|
|
134
|
+
}>;
|
|
135
|
+
declare function typeText(locator: InputLocator, value: string): UnlabeledStep<{
|
|
136
|
+
locator: {
|
|
137
|
+
by: "testId";
|
|
138
|
+
value: string;
|
|
139
|
+
} | {
|
|
140
|
+
by: "role";
|
|
141
|
+
role: string;
|
|
142
|
+
name?: string | undefined;
|
|
143
|
+
};
|
|
144
|
+
type: "type";
|
|
145
|
+
value: {
|
|
146
|
+
type: "static";
|
|
147
|
+
value: string;
|
|
148
|
+
};
|
|
149
|
+
}>;
|
|
150
|
+
declare function rightClick(locator: AnyLocator): UnlabeledStep<{
|
|
151
|
+
locator: {
|
|
152
|
+
by: "testId";
|
|
153
|
+
value: string;
|
|
154
|
+
} | {
|
|
155
|
+
by: "role";
|
|
156
|
+
role: string;
|
|
157
|
+
name?: string | undefined;
|
|
158
|
+
};
|
|
159
|
+
type: "rightClick";
|
|
160
|
+
}>;
|
|
161
|
+
declare function scrollIntoView(locator: AnyLocator): UnlabeledStep<{
|
|
162
|
+
locator: {
|
|
163
|
+
by: "testId";
|
|
164
|
+
value: string;
|
|
165
|
+
} | {
|
|
166
|
+
by: "role";
|
|
167
|
+
role: string;
|
|
168
|
+
name?: string | undefined;
|
|
169
|
+
};
|
|
170
|
+
type: "scrollIntoView";
|
|
171
|
+
}>;
|
|
172
|
+
declare function drag(source: AnyLocator, target: AnyLocator): UnlabeledStep<{
|
|
173
|
+
source: {
|
|
174
|
+
by: "testId";
|
|
175
|
+
value: string;
|
|
176
|
+
} | {
|
|
177
|
+
by: "role";
|
|
178
|
+
role: string;
|
|
179
|
+
name?: string | undefined;
|
|
180
|
+
};
|
|
181
|
+
target: {
|
|
182
|
+
by: "testId";
|
|
183
|
+
value: string;
|
|
184
|
+
} | {
|
|
185
|
+
by: "role";
|
|
186
|
+
role: string;
|
|
187
|
+
name?: string | undefined;
|
|
188
|
+
};
|
|
189
|
+
type: "drag";
|
|
190
|
+
}>;
|
|
191
|
+
interface HandleDialogOptions {
|
|
192
|
+
readonly action: "accept" | "dismiss";
|
|
193
|
+
readonly promptText: string | undefined;
|
|
194
|
+
}
|
|
195
|
+
declare function handleDialog({ action, promptText }: HandleDialogOptions): UnlabeledStep<{
|
|
196
|
+
action: "accept" | "dismiss";
|
|
197
|
+
promptText: string | undefined;
|
|
198
|
+
type: "handleDialog";
|
|
199
|
+
}>;
|
|
200
|
+
interface ClipboardOptions {
|
|
201
|
+
readonly action: "read" | "write";
|
|
202
|
+
readonly value: string | undefined;
|
|
203
|
+
readonly variable: string | undefined;
|
|
204
|
+
}
|
|
205
|
+
declare function clipboard({ action, value, variable }: ClipboardOptions): UnlabeledStep<{
|
|
206
|
+
action: "read" | "write";
|
|
207
|
+
type: "clipboard";
|
|
208
|
+
value: {
|
|
209
|
+
type: "static";
|
|
210
|
+
value: string;
|
|
211
|
+
} | undefined;
|
|
212
|
+
variable: string | undefined;
|
|
213
|
+
}>;
|
|
214
|
+
interface SetPermissionOptions {
|
|
215
|
+
readonly permission: string;
|
|
216
|
+
readonly state: "granted" | "prompt";
|
|
217
|
+
}
|
|
218
|
+
declare function setPermission({ permission, state }: SetPermissionOptions): UnlabeledStep<{
|
|
219
|
+
permission: string;
|
|
220
|
+
state: "granted" | "prompt";
|
|
221
|
+
type: "setPermission";
|
|
222
|
+
}>;
|
|
223
|
+
interface SetViewportOptions {
|
|
224
|
+
readonly height: number;
|
|
225
|
+
readonly width: number;
|
|
226
|
+
}
|
|
227
|
+
declare function setViewport({ height, width }: SetViewportOptions): UnlabeledStep<{
|
|
228
|
+
height: number;
|
|
229
|
+
type: "setViewport";
|
|
230
|
+
width: number;
|
|
231
|
+
}>;
|
|
232
|
+
|
|
233
|
+
export { check, clear, click, clipboard, dblclick, drag, fill, focus, handleDialog, hover, navigate, press, rightClick, scrollIntoView, select, setPermission, setViewport, typeText, uncheck, upload };
|