@ripplo/testing 0.3.8 → 0.3.10

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/dist/actions.d.ts CHANGED
@@ -191,6 +191,11 @@ interface HandleDialogOptions {
191
191
  readonly promptText: string | undefined;
192
192
  readonly uiOnly?: boolean;
193
193
  }
194
+ /**
195
+ * Respond to a native browser dialog (`alert`, `confirm`, `prompt`). Set
196
+ * `uiOnly: true` only when the dialog has no backend side effect — if it
197
+ * triggers a mutation, wire an observer and keep `uiOnly` off.
198
+ */
194
199
  declare function handleDialog({ action, promptText, uiOnly }: HandleDialogOptions): UnlabeledStep<{
195
200
  action: "accept" | "dismiss";
196
201
  promptText: string | undefined;
@@ -202,6 +207,12 @@ interface ClipboardOptions {
202
207
  readonly target: Variable<string> | undefined;
203
208
  readonly value: StringOrVariable | undefined;
204
209
  }
210
+ /**
211
+ * Read from or write to the browser clipboard. Use when a flow depends on
212
+ * copy/paste (share links, invite tokens) — pair `write` to seed the
213
+ * clipboard before a paste action, or `read` into a `variable()` to assert
214
+ * what the app copied.
215
+ */
205
216
  declare function clipboard({ action, target, value }: ClipboardOptions): UnlabeledStep<{
206
217
  action: "read" | "write";
207
218
  type: "clipboard";
@@ -212,6 +223,10 @@ interface SetPermissionOptions {
212
223
  readonly permission: string;
213
224
  readonly state: "granted" | "prompt";
214
225
  }
226
+ /**
227
+ * Pre-answer a browser permission prompt (geolocation, camera, notifications,
228
+ * clipboard-read, etc.) so the test doesn't stall on the native dialog.
229
+ */
215
230
  declare function setPermission({ permission, state }: SetPermissionOptions): UnlabeledStep<{
216
231
  permission: string;
217
232
  state: "granted" | "prompt";
@@ -221,6 +236,11 @@ interface SetViewportOptions {
221
236
  readonly height: number;
222
237
  readonly width: number;
223
238
  }
239
+ /**
240
+ * Resize the browser viewport mid-test. Use to cover responsive breakpoints
241
+ * (mobile nav, collapsed sidebars) within the same flow rather than
242
+ * duplicating the test per size.
243
+ */
224
244
  declare function setViewport({ height, width }: SetViewportOptions): UnlabeledStep<{
225
245
  height: number;
226
246
  type: "setViewport";
package/dist/assert.d.ts CHANGED
@@ -67,6 +67,12 @@ declare const assert: {
67
67
  operator: "equals";
68
68
  type: "assertAttribute";
69
69
  }>;
70
+ /**
71
+ * Verify a backend side effect by polling an observer. The server-side
72
+ * observer impl returns `ctx.retry(reason)` for transient states (keep
73
+ * polling) and `ctx.fail(reason)` only for invariant violations (stop
74
+ * early). The observer's `.budget()` governs poll interval and timeout.
75
+ */
70
76
  backend<THandle extends ObserverHandle>(observer: THandle, params: ObserverInput<THandle> & Record<string, string>): UnlabeledStep<{
71
77
  budget: ObserverBudgetTier;
72
78
  observer: string;
package/dist/assert.js CHANGED
@@ -37,6 +37,13 @@ var assert = {
37
37
  type: "assertAttribute"
38
38
  });
39
39
  },
40
+ // eslint-disable-next-line no-comments/disallowComments
41
+ /**
42
+ * Verify a backend side effect by polling an observer. The server-side
43
+ * observer impl returns `ctx.retry(reason)` for transient states (keep
44
+ * polling) and `ctx.fail(reason)` only for invariant violations (stop
45
+ * early). The observer's `.budget()` governs poll interval and timeout.
46
+ */
40
47
  backend(observer, params) {
41
48
  return createStep({
42
49
  budget: readObserverBudget(observer),
@@ -6,6 +6,12 @@ interface ObserverNeedsDescription {
6
6
  readonly description: (text: string) => ObserverNeedsBudget;
7
7
  }
8
8
  interface ObserverNeedsBudget {
9
+ /**
10
+ * Polling envelope: interval + total timeout the runner uses when calling
11
+ * `assert.backend()`. Pick by where the side effect lands — fast for
12
+ * same-request DB reads, slow for job-queue settle, async for
13
+ * webhook/email delivery. Too tight = flaky; too loose = slow suite.
14
+ */
9
15
  readonly budget: (tier: ObserverBudget) => ObserverNeedsInput;
10
16
  }
11
17
  interface ObserverNeedsInput {
@@ -15,6 +21,11 @@ interface ObserverReady<TInput extends Record<string, string>> {
15
21
  readonly contract: () => ObserverHandle<TInput>;
16
22
  }
17
23
 
24
+ interface CoverageRegistry {
25
+ }
26
+ type RegistryKeys = keyof CoverageRegistry;
27
+ type CoverageStatementId = [RegistryKeys] extends [never] ? string : Extract<RegistryKeys, string>;
28
+
18
29
  type PreconditionRecord = Record<string, Precondition>;
19
30
  type ResolveDeps<TDeps extends PreconditionRecord> = {
20
31
  readonly [K in keyof TDeps]: PreconditionData<TDeps[K]>;
@@ -32,6 +43,13 @@ interface TestNeedsName {
32
43
  }
33
44
  interface TestNeedsRequires {
34
45
  readonly description: (text: string) => TestNeedsRequires;
46
+ /**
47
+ * Declare the preconditions this test needs. The object keys become the
48
+ * destructured parameter in `.startsAt` / `.steps`, and each precondition's
49
+ * return data flows through as a typed proxy — interpolate proxy fields
50
+ * directly (e.g. `` `/workspaces/${workspace.id}` ``); never hand-write
51
+ * `{{namespace.key}}` template strings (lint blocks this).
52
+ */
35
53
  readonly requires: <TReqs extends PreconditionRecord>(reqs: TReqs) => TestNeedsOutcome<ResolveDeps<TReqs>>;
36
54
  }
37
55
  interface TestNeedsOutcome<TVars extends Record<string, Record<string, string>>> {
@@ -39,14 +57,34 @@ interface TestNeedsOutcome<TVars extends Record<string, Record<string, string>>>
39
57
  }
40
58
 
41
59
  interface TestNeedsStartsAt<TVars extends Record<string, Record<string, string>>> {
60
+ /**
61
+ * Skip implementation — compiles as a planning stub and is reported in
62
+ * `getUnimplemented()`. The test appears in the lockfile but is not run.
63
+ */
42
64
  readonly notImplemented: () => TestDefinition;
65
+ /**
66
+ * Starting URL for the test, derived from precondition data. The function
67
+ * receives the `.requires()` object destructured as typed proxies; it runs
68
+ * at compile time, not at test-author time.
69
+ */
43
70
  readonly startsAt: (fn: (vars: TVars) => string) => TestNeedsSteps<TVars>;
44
71
  }
45
72
  interface TestNeedsSteps<TVars extends Record<string, Record<string, string>>> {
73
+ /**
74
+ * Declare the ordered step list. Receives destructured precondition data as
75
+ * typed proxies (same as `.startsAt`). Every step must be labeled with
76
+ * `.as("…")` — lint enforces it.
77
+ */
46
78
  readonly steps: (fn: (vars: TVars) => ReadonlyArray<Step>) => TestNeedsCoverage;
47
79
  }
48
80
  interface TestNeedsCoverage {
49
- readonly coverage: (...ids: ReadonlyArray<string>) => TestDefinition;
81
+ /**
82
+ * Finalize the test and declare which mutation/AST branches it covers.
83
+ * Coverage IDs feed LLM-driven test scoping on diffs so only relevant tests
84
+ * run for a change. `.coverage()` (or `.notImplemented()`) is the only way
85
+ * to terminate the chain into a runnable `TestDefinition`.
86
+ */
87
+ readonly coverage: (...ids: ReadonlyArray<CoverageStatementId>) => TestDefinition;
50
88
  }
51
89
 
52
90
  type PreconditionSetupResult<TData extends Record<string, string>> = {
@@ -79,9 +117,35 @@ interface RipploInstance<P extends PreconditionRegistry = PreconditionRegistry,
79
117
  readonly getUnimplemented: () => UnimplementedItems;
80
118
  }
81
119
  type RipploBuilder = RipploInstance;
120
+ /**
121
+ * Declare a precondition — a named, reusable piece of test data setup. Returns
122
+ * a pure handle (no side effects at definition time); the setup/teardown impl
123
+ * is wired once in `createEngine`. Depend on other preconditions via
124
+ * `.requires()` so the DAG can resolve independents in parallel, giving every
125
+ * test a clean, isolated slate.
126
+ */
82
127
  declare function precondition(name: string): PreconditionNeedsSetup;
128
+ /**
129
+ * Declare a backend assertion — checked from tests via `assert.backend()`.
130
+ * The impl runs on your server (where DB/queue access lives) and is polled
131
+ * until pass/fail; `.budget()` picks the polling envelope. Use this instead
132
+ * of sleeps when verifying async side effects (jobs, webhooks, emails, DB rows).
133
+ */
83
134
  declare function observer(name: string): ObserverNeedsDescription;
135
+ /**
136
+ * Start a test definition. `id` is a stable kebab-case key persisted in the
137
+ * lockfile and used for run history — changing it renames the test and loses
138
+ * history. Set `options.uiOnly` only for genuinely read-only flows (no
139
+ * mutations); do not use it to silence observer lint on flows that mutate
140
+ * state — wire the observer instead.
141
+ */
84
142
  declare function test(id: string, options?: TestOptions): TestNeedsName;
143
+ /**
144
+ * The single registration point for the DSL graph. Collects the three
145
+ * registries into an instance consumed both by `ripplo compile` (to write
146
+ * `.ripplo/ripplo.lock`) and by `createEngine` on the server, where
147
+ * TypeScript exhaustiveness-checks that every handle has exactly one impl.
148
+ */
85
149
  declare function createRipplo<P extends PreconditionRegistry, O extends ObserverRegistry>(rawConfig: UserDslConfig, registries: RipploRegistries<P, O>): RipploInstance<P, O>;
86
150
 
87
- export { type ObserverImplFn as O, type PreconditionImpl as P, type ResolveDeps as R, type ObserverRegistry as a, type PreconditionRecord as b, type PreconditionRegistry as c, type RipploBuilder as d, type RipploInstance as e, type RipploRegistries as f, createRipplo as g, observer as o, precondition as p, test as t };
151
+ export { type CoverageRegistry as C, type ObserverImplFn as O, type PreconditionImpl as P, type ResolveDeps as R, type ObserverRegistry as a, type PreconditionRecord as b, type PreconditionRegistry as c, type RipploBuilder as d, type RipploInstance as e, type RipploRegistries as f, createRipplo as g, observer as o, precondition as p, test as t };
@@ -1,5 +1,5 @@
1
1
  import { Observer, Precondition, WorkflowSpec } from '@ripplo/spec';
2
- import { d as RipploBuilder } from './builder-D-i_ueN3.js';
2
+ import { d as RipploBuilder } from './builder-CkyzxH7O.js';
3
3
  import { e as DslConfig } from './types-Do4o4Y_c.js';
4
4
  import './step-De52hTLd.js';
5
5
  import 'zod';
package/dist/control.d.ts CHANGED
@@ -6,6 +6,12 @@ declare const VARIABLE_INTERNAL: unique symbol;
6
6
  interface Variable<_TName extends string> {
7
7
  readonly [VARIABLE_INTERNAL]: _TName;
8
8
  }
9
+ /**
10
+ * Declare a runtime placeholder, bound later by `extract()` and consumed by
11
+ * actions/assertions. Use for values produced mid-test (e.g. an ID rendered
12
+ * into the DOM). Precondition data is already typed and injected — reach for
13
+ * `variable` only when the value isn't known until the test runs.
14
+ */
9
15
  declare function variable<TName extends string>(name: TName): Variable<TName>;
10
16
  declare function readVariable(v: Variable<string>): string;
11
17
  declare function isVariable(v: unknown): v is Variable<string>;
@@ -18,6 +24,11 @@ interface VariableRef {
18
24
  readonly type: "variable";
19
25
  }
20
26
  declare function toStringValueRef(value: string | Variable<string>): StaticStringRef | VariableRef;
27
+ /**
28
+ * Capture the text of the matched element into `target` (a `variable()`).
29
+ * Pairs by name — downstream steps referencing the same `variable` see the
30
+ * extracted value.
31
+ */
21
32
  declare function extract(locator: AnyLocator, target: Variable<string>): UnlabeledStep<{
22
33
  locator: {
23
34
  by: "testId";
package/dist/elysia.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Elysia } from 'elysia';
2
- import { R as RipploEngine } from './engine-BMfNkIKC.js';
3
- import './builder-D-i_ueN3.js';
2
+ import { R as RipploEngine } from './engine-CRq3Az6b.js';
3
+ import './builder-CkyzxH7O.js';
4
4
  import './types-Do4o4Y_c.js';
5
5
  import 'zod';
6
6
  import './step-De52hTLd.js';
@@ -1,4 +1,4 @@
1
- import { c as PreconditionRegistry, a as ObserverRegistry, O as ObserverImplFn, P as PreconditionImpl, e as RipploInstance } from './builder-D-i_ueN3.js';
1
+ import { c as PreconditionRegistry, a as ObserverRegistry, O as ObserverImplFn, P as PreconditionImpl, e as RipploInstance } from './builder-CkyzxH7O.js';
2
2
  import { h as ObserverOutcome, C as CookieEntry, e as DslConfig, g as ObserverDefinition, k as PreconditionDefinition, U as UnimplementedItems, O as ObserverHandle, P as Precondition } from './types-Do4o4Y_c.js';
3
3
 
4
4
  interface EngineResult {
package/dist/express.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Router } from 'express';
2
- import { R as RipploEngine } from './engine-BMfNkIKC.js';
3
- import './builder-D-i_ueN3.js';
2
+ import { R as RipploEngine } from './engine-CRq3Az6b.js';
3
+ import './builder-CkyzxH7O.js';
4
4
  import './types-Do4o4Y_c.js';
5
5
  import 'zod';
6
6
  import './step-De52hTLd.js';
package/dist/fastify.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { FastifyInstance } from 'fastify';
2
- import { R as RipploEngine } from './engine-BMfNkIKC.js';
3
- import './builder-D-i_ueN3.js';
2
+ import { R as RipploEngine } from './engine-CRq3Az6b.js';
3
+ import './builder-CkyzxH7O.js';
4
4
  import './types-Do4o4Y_c.js';
5
5
  import 'zod';
6
6
  import './step-De52hTLd.js';
package/dist/hono.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Hono } from 'hono';
2
- import { R as RipploEngine } from './engine-BMfNkIKC.js';
3
- import './builder-D-i_ueN3.js';
2
+ import { R as RipploEngine } from './engine-CRq3Az6b.js';
3
+ import './builder-CkyzxH7O.js';
4
4
  import './types-Do4o4Y_c.js';
5
5
  import 'zod';
6
6
  import './step-De52hTLd.js';
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- export { O as ObserverImplFn, a as ObserverRegistry, P as PreconditionImpl, b as PreconditionRecord, c as PreconditionRegistry, R as ResolveDeps, d as RipploBuilder, e as RipploInstance, f as RipploRegistries, g as createRipplo, o as observer, p as precondition, t as test } from './builder-D-i_ueN3.js';
1
+ export { C as CoverageRegistry, O as ObserverImplFn, a as ObserverRegistry, P as PreconditionImpl, b as PreconditionRecord, c as PreconditionRegistry, R as ResolveDeps, d as RipploBuilder, e as RipploInstance, f as RipploRegistries, g as createRipplo, o as observer, p as precondition, t as test } from './builder-CkyzxH7O.js';
2
2
  import { CompileResult } from './compiler.js';
3
3
  export { CompiledTest, compile } from './compiler.js';
4
- export { E as EngineImpls, a as EngineResult, b as ExecuteBatchOptions, N as NotImplemented, O as ObserverImplFnFor, P as PreconditionImplFor, R as RipploEngine, c as createEngine, n as notImplemented } from './engine-BMfNkIKC.js';
4
+ export { E as EngineImpls, a as EngineResult, b as ExecuteBatchOptions, N as NotImplemented, O as ObserverImplFnFor, P as PreconditionImplFor, R as RipploEngine, c as createEngine, n as notImplemented } from './engine-CRq3Az6b.js';
5
5
  import { C as CookieEntry } from './types-Do4o4Y_c.js';
6
6
  export { c as CookieOptions, D as DEFAULT_IGNORE_PATHS, d as DEFAULT_WATCH_PATHS, e as DslConfig, f as ObserverContext, g as ObserverDefinition, O as ObserverHandle, a as ObserverInput, h as ObserverOutcome, P as Precondition, i as PreconditionDeps, S as SetupContext, T as TeardownContext, j as TestDefinition } from './types-Do4o4Y_c.js';
7
7
  export { D as DslNodeInput } from './step-De52hTLd.js';
package/dist/koa.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Middleware } from 'koa';
2
- import { R as RipploEngine } from './engine-BMfNkIKC.js';
3
- import './builder-D-i_ueN3.js';
2
+ import { R as RipploEngine } from './engine-CRq3Az6b.js';
3
+ import './builder-CkyzxH7O.js';
4
4
  import './types-Do4o4Y_c.js';
5
5
  import 'zod';
6
6
  import './step-De52hTLd.js';
@@ -24,7 +24,17 @@ type AnyLocator = Locator<"role" | "testId">;
24
24
  type InputLocator = Locator<"testId"> | RoleLocator<"combobox" | "searchbox" | "spinbutton" | "textbox">;
25
25
  type SelectLocator = Locator<"testId"> | RoleLocator<"combobox" | "listbox">;
26
26
  type CheckLocator = Locator<"testId"> | RoleLocator<"checkbox" | "switch">;
27
+ /**
28
+ * Preferred locator: targets an element by its ARIA role and accessible name.
29
+ * Survives DOM churn and doubles as an accessibility check — if a role query
30
+ * can't find your element, real users likely can't either.
31
+ */
27
32
  declare function role<TRole extends AriaRole>(ariaRole: TRole, name?: string): RoleLocator<TRole>;
33
+ /**
34
+ * Escape hatch for elements without a semantic role (decorative wrappers,
35
+ * non-interactive panels). Prefer `role()` when the element has one; a
36
+ * test-id locator has no accessibility signal.
37
+ */
28
38
  declare function testId(id: string): Locator<"testId">;
29
39
 
30
40
  export { type AnyLocator, type AriaRole, type CheckLocator, type InputLocator, type Locator, type LocatorSpec, type RoleLocator, type SelectLocator, readLocator, role, testId };
@@ -1,7 +1,7 @@
1
1
  import { Codec } from '@ripplo/spec';
2
2
  import { z } from 'zod';
3
3
  import { CompileResult } from './compiler.js';
4
- import './builder-D-i_ueN3.js';
4
+ import './builder-CkyzxH7O.js';
5
5
  import './types-Do4o4Y_c.js';
6
6
  import './step-De52hTLd.js';
7
7
 
package/dist/nestjs.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { DynamicModule } from '@nestjs/common';
2
- import { R as RipploEngine } from './engine-BMfNkIKC.js';
3
- import './builder-D-i_ueN3.js';
2
+ import { R as RipploEngine } from './engine-CRq3Az6b.js';
3
+ import './builder-CkyzxH7O.js';
4
4
  import './types-Do4o4Y_c.js';
5
5
  import 'zod';
6
6
  import './step-De52hTLd.js';
package/dist/nextjs.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as RipploEngine } from './engine-BMfNkIKC.js';
2
- import './builder-D-i_ueN3.js';
1
+ import { R as RipploEngine } from './engine-CRq3Az6b.js';
2
+ import './builder-CkyzxH7O.js';
3
3
  import './types-Do4o4Y_c.js';
4
4
  import 'zod';
5
5
  import './step-De52hTLd.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ripplo/testing",
3
3
  "description": "TypeScript DSL for defining and running Ripplo e2e workflow tests",
4
- "version": "0.3.8",
4
+ "version": "0.3.10",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist"