@shivaedev/test-story 0.15.0 → 0.16.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.16.0 - 2026-10-19
4
+
5
+ ### Added
6
+
7
+ - `screenDriver.find(target, { timeout }?)` waits for a unique element by selector or role and accessible name, narrates the lookup, and returns the element with the actions' candidate diagnostics when no unique target appears.
8
+
3
9
  ## 0.15.0 - 2026-10-19
4
10
 
5
11
  ### Added
package/README.md CHANGED
@@ -604,6 +604,8 @@ verbs: ({ view, requests }, story) => screenDriver({
604
604
 
605
605
  `click`, `enter`, `submit`, `text` and `value` also accept a `ScreenTarget` object such as `{ role: "button", name: "Save" }` or `{ role: "textbox", name: /email/i }`. The role and computed accessible name must match exactly when strings are used; regular expressions match the name. Like Testing Library's role queries, these targets exclude hidden elements, including `aria-hidden` ancestors. Missing or ambiguous targets fail with the requested role, name and the visible candidates on screen. Narration names the role and name instead of a selector.
606
606
 
607
+ `find(target, { timeout }?)` returns an Effect that waits for exactly one matching rendered element and returns that `HTMLElement`. It accepts the same selectors and role/name targets as the actions, narrates the lookup, and uses the driver's live-time `timeoutMs` limit unless `timeout` overrides it in milliseconds. If no unique element appears before the limit, it fails with the same target diagnostics as the actions, including visible candidates for role/name targets.
608
+
607
609
  `accessible(selector)` returns `{ name, role }` computed by `dom-accessibility-api`, including native roles and label references that take precedence over `aria-label`. It narrates the readout and uses the same single rendered target checks as other readouts. Assert on this result when checking a control's accessible name; reading its raw attributes does not compute that name. This DOM computation does not inspect a browser's accessibility tree or prove speech from a screen reader.
608
610
 
609
611
  `accessible(element)` uses the same computation on an `Element` the kit already holds and tells no story line.
@@ -18,6 +18,9 @@ export declare function screenDriver({ root, pending, tell, act, timeoutMs }: Sc
18
18
  };
19
19
  click: (selector: ScreenTarget) => Effect.Effect<void, never, never>;
20
20
  enter: (selector: ScreenTarget, value: string) => Effect.Effect<void, never, never>;
21
+ find: (target: ScreenTarget, options?: {
22
+ readonly timeout?: number;
23
+ }) => Effect.Effect<HTMLElement, import("effect/Cause").UnknownError, never>;
21
24
  settle: (options?: {
22
25
  readonly timeoutMs?: number;
23
26
  }) => Effect.Effect<void, never, never>;
@@ -1 +1 @@
1
- {"version":3,"file":"screenDriver.d.ts","sourceRoot":"","sources":["../../src/server/screenDriver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAKhC,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AAE9F,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,WAAW,CAAC,IAAI,CAAC,CAAC;IAClE,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,CAAC,CAAC;IACjD,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AAE3D,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAA0B,EAAE,SAAgB,EAAE,EAAE,mBAAmB;2BAgC9F,MAAM,GAAG,OAAO;;;;sBASrB,YAAY;sBAOZ,YAAY,SAAS,MAAM;uBAvCrB;QAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE;wBAyCpC,YAAY;sBACb,YAAY;sBAKZ,YAAY;EAM/B"}
1
+ {"version":3,"file":"screenDriver.d.ts","sourceRoot":"","sources":["../../src/server/screenDriver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAY,MAAM,QAAQ,CAAC;AAK1C,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AAE9F,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,WAAW,CAAC,IAAI,CAAC,CAAC;IAClE,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,CAAC,CAAC;IACjD,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AAE3D,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAA0B,EAAE,SAAgB,EAAE,EAAE,mBAAmB;2BAiD9F,MAAM,GAAG,OAAO;;;;sBASrB,YAAY;sBAOZ,YAAY,SAAS,MAAM;mBAlCxB,YAAY,YAAW;QAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE;uBAtBjD;QAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE;wBA2DpC,YAAY;sBACb,YAAY;sBAKZ,YAAY;EAM/B"}
@@ -1,4 +1,4 @@
1
- import { Effect } from "effect";
1
+ import { Effect, Schedule } from "effect";
2
2
  import { withLive } from "@shivaedev/effect-test/withLive.ts";
3
3
  import { accessibility, targetName } from "#internal/screenAccessibility.ts";
4
4
  import { screenElements } from "#internal/screenElements.ts";
@@ -29,6 +29,18 @@ export function screenDriver({ root, pending, tell, act = (action) => action(),
29
29
  }
30
30
  return withLive(wait.pipe(Effect.timeoutOrElse({ duration: limit, orElse: timedOut })));
31
31
  }
32
+ function find(target, options = {}) {
33
+ return withLive(Effect.gen(function* () {
34
+ tell(`find ${targetName(target)}`);
35
+ return yield* Effect.tryPromise(async () => {
36
+ await act(async () => undefined);
37
+ return elements.element(target);
38
+ }).pipe(Effect.retry(Schedule.spaced(10)), Effect.timeoutOrElse({
39
+ duration: options.timeout ?? timeoutMs,
40
+ orElse: () => Effect.sync(() => elements.element(target)),
41
+ }));
42
+ }));
43
+ }
32
44
  return {
33
45
  accessible: (selector) => {
34
46
  if (typeof selector !== "string") {
@@ -46,6 +58,7 @@ export function screenDriver({ root, pending, tell, act = (action) => action(),
46
58
  element.click();
47
59
  }),
48
60
  enter: (selector, value) => perform(`enter a value in ${targetName(selector)}`, () => elements.enter(selector, value)),
61
+ find,
49
62
  settle,
50
63
  submit: (selector = "form") => perform(`submit ${targetName(selector)}`, () => elements.form(selector).requestSubmit()),
51
64
  text: (selector) => {
@@ -1 +1 @@
1
- {"version":3,"file":"screenDriver.js","sourceRoot":"","sources":["../../src/server/screenDriver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"screenDriver.js","sourceRoot":"","sources":["../../src/server/screenDriver.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shivaedev/test-story",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Tests that read like stories over the real engine: a story kit per engine with its own it, traits per feature, and a failure that prints the story, where it stopped and the engine's state",
5
5
  "homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/test-story#readme",
6
6
  "bugs": {
@@ -46,7 +46,7 @@
46
46
  },
47
47
  "devDependencies": {
48
48
  "@effect/vitest": "4.0.4",
49
- "@shivaedev/quality": "0.29.0",
49
+ "@shivaedev/quality": "0.29.1",
50
50
  "@types/node": "24.19.2",
51
51
  "effect": "4.0.4",
52
52
  "happy-dom": "20.14.6",
@@ -1,4 +1,4 @@
1
- import { Effect } from "effect";
1
+ import { Effect, Schedule } from "effect";
2
2
  import { withLive } from "@shivaedev/effect-test/withLive.ts";
3
3
  import { accessibility, targetName } from "#internal/screenAccessibility.ts";
4
4
  import { screenElements } from "#internal/screenElements.ts";
@@ -46,6 +46,23 @@ export function screenDriver({ root, pending, tell, act = (action) => action(),
46
46
  }
47
47
  return withLive(wait.pipe(Effect.timeoutOrElse({ duration: limit, orElse: timedOut })));
48
48
  }
49
+ function find(target: ScreenTarget, options: { readonly timeout?: number } = {}) {
50
+ return withLive(
51
+ Effect.gen(function* () {
52
+ tell(`find ${targetName(target)}`);
53
+ return yield* Effect.tryPromise(async () => {
54
+ await act(async () => undefined);
55
+ return elements.element(target);
56
+ }).pipe(
57
+ Effect.retry(Schedule.spaced(10)),
58
+ Effect.timeoutOrElse({
59
+ duration: options.timeout ?? timeoutMs,
60
+ orElse: () => Effect.sync(() => elements.element(target)),
61
+ }),
62
+ );
63
+ }),
64
+ );
65
+ }
49
66
  return {
50
67
  accessible: (selector: string | Element) => {
51
68
  if (typeof selector !== "string") {
@@ -64,6 +81,7 @@ export function screenDriver({ root, pending, tell, act = (action) => action(),
64
81
  element.click();
65
82
  }),
66
83
  enter: (selector: ScreenTarget, value: string) => perform(`enter a value in ${targetName(selector)}`, () => elements.enter(selector, value)),
84
+ find,
67
85
  settle,
68
86
  submit: (selector: ScreenTarget = "form") => perform(`submit ${targetName(selector)}`, () => elements.form(selector).requestSubmit()),
69
87
  text: (selector?: ScreenTarget) => {