feather-testing-core 0.2.0 → 0.4.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.
Files changed (43) hide show
  1. package/README.md +177 -4
  2. package/dist/errors.d.ts +8 -0
  3. package/dist/errors.d.ts.map +1 -1
  4. package/dist/errors.js +12 -0
  5. package/dist/eslint-plugin/index.d.ts +18 -0
  6. package/dist/eslint-plugin/index.d.ts.map +1 -0
  7. package/dist/eslint-plugin/index.js +37 -0
  8. package/dist/eslint-plugin/rules/no-conditional-skip.d.ts +4 -0
  9. package/dist/eslint-plugin/rules/no-conditional-skip.d.ts.map +1 -0
  10. package/dist/eslint-plugin/rules/no-conditional-skip.js +56 -0
  11. package/dist/eslint-plugin/rules/no-swallowed-cleanup-catch.d.ts +4 -0
  12. package/dist/eslint-plugin/rules/no-swallowed-cleanup-catch.d.ts.map +1 -0
  13. package/dist/eslint-plugin/rules/no-swallowed-cleanup-catch.js +63 -0
  14. package/dist/eslint-plugin/rules/no-wait-for-timeout.d.ts +4 -0
  15. package/dist/eslint-plugin/rules/no-wait-for-timeout.d.ts.map +1 -0
  16. package/dist/eslint-plugin/rules/no-wait-for-timeout.js +69 -0
  17. package/dist/eslint-plugin/rules/no-weak-assertions.d.ts +4 -0
  18. package/dist/eslint-plugin/rules/no-weak-assertions.d.ts.map +1 -0
  19. package/dist/eslint-plugin/rules/no-weak-assertions.js +73 -0
  20. package/dist/eslint-plugin/rules/warn-serial-mode.d.ts +4 -0
  21. package/dist/eslint-plugin/rules/warn-serial-mode.d.ts.map +1 -0
  22. package/dist/eslint-plugin/rules/warn-serial-mode.js +59 -0
  23. package/dist/index.d.ts +2 -2
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +1 -1
  26. package/dist/playwright/driver.d.ts +18 -3
  27. package/dist/playwright/driver.d.ts.map +1 -1
  28. package/dist/playwright/driver.js +82 -18
  29. package/dist/playwright/index.d.ts +4 -4
  30. package/dist/playwright/index.d.ts.map +1 -1
  31. package/dist/playwright/index.js +1 -1
  32. package/dist/rtl/driver.d.ts +55 -9
  33. package/dist/rtl/driver.d.ts.map +1 -1
  34. package/dist/rtl/driver.js +135 -35
  35. package/dist/rtl/index.d.ts +5 -4
  36. package/dist/rtl/index.d.ts.map +1 -1
  37. package/dist/rtl/index.js +2 -2
  38. package/dist/session.d.ts +41 -4
  39. package/dist/session.d.ts.map +1 -1
  40. package/dist/session.js +72 -1
  41. package/dist/types.d.ts +35 -3
  42. package/dist/types.d.ts.map +1 -1
  43. package/package.json +10 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"warn-serial-mode.d.ts","sourceRoot":"","sources":["../../../src/eslint-plugin/rules/warn-serial-mode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAgBnC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UAkDhB,CAAC;AAEF,eAAe,IAAI,CAAC"}
@@ -0,0 +1,59 @@
1
+ function memberPath(node) {
2
+ if (node.type === "Identifier")
3
+ return node.name;
4
+ if (node.type === "MemberExpression" &&
5
+ !node.computed &&
6
+ node.property.type === "Identifier") {
7
+ const prefix = memberPath(node.object);
8
+ return prefix === null ? null : `${prefix}.${node.property.name}`;
9
+ }
10
+ return null;
11
+ }
12
+ const rule = {
13
+ meta: {
14
+ type: "suggestion",
15
+ docs: {
16
+ description: "Flag serial mode, which makes each test depend on the ones before it",
17
+ recommended: true,
18
+ },
19
+ schema: [],
20
+ messages: {
21
+ serial: "Serial mode couples these tests: one failure cascades into the rest, " +
22
+ "so the report stops naming the real cause and a flake becomes a wall " +
23
+ "of red. Make them independent — share setup, not state — or keep " +
24
+ "serial with an eslint-disable-next-line comment saying why it is " +
25
+ "required.",
26
+ },
27
+ },
28
+ create(context) {
29
+ return {
30
+ CallExpression(node) {
31
+ const path = memberPath(node.callee);
32
+ if (path === null)
33
+ return;
34
+ const segments = path.split(".");
35
+ const isSerialRunner = segments.includes("serial") &&
36
+ ["test", "it", "describe", "suite"].includes(segments[0] ?? "");
37
+ if (isSerialRunner) {
38
+ context.report({ node, messageId: "serial" });
39
+ return;
40
+ }
41
+ // test.describe.configure({ mode: "serial" })
42
+ if (!path.endsWith(".configure"))
43
+ return;
44
+ const [options] = node.arguments;
45
+ if (!options || options.type !== "ObjectExpression")
46
+ return;
47
+ const declaresSerial = options.properties.some((property) => property.type === "Property" &&
48
+ !property.computed &&
49
+ property.key.type === "Identifier" &&
50
+ property.key.name === "mode" &&
51
+ property.value.type === "Literal" &&
52
+ property.value.value === "serial");
53
+ if (declaresSerial)
54
+ context.report({ node, messageId: "serial" });
55
+ },
56
+ };
57
+ },
58
+ };
59
+ export default rule;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { Session } from "./session.js";
2
- export { StepError } from "./errors.js";
3
- export type { AssertHasOptions, AssertPathOptions, QueuedStep, TestDriver, } from "./types.js";
2
+ export { StepError, BrowserOnlyVerbError } from "./errors.js";
3
+ export type { AssertHasOptions, AssertPathOptions, DownloadOptions, QueuedStep, TestDriver, UntilOptions, UntilPredicate, } from "./types.js";
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,EACV,UAAU,GACX,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC9D,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,UAAU,EACV,UAAU,EACV,YAAY,EACZ,cAAc,GACf,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  export { Session } from "./session.js";
2
- export { StepError } from "./errors.js";
2
+ export { StepError, BrowserOnlyVerbError } from "./errors.js";
@@ -1,12 +1,12 @@
1
1
  import { type Page, type Locator } from "@playwright/test";
2
- import type { AssertHasOptions, AssertPathOptions, TestDriver } from "../types.js";
2
+ import type { AssertHasOptions, AssertPathOptions, DownloadOptions, TestDriver, UntilOptions, UntilPredicate } from "../types.js";
3
3
  /** Context handed to custom step() callbacks in the Playwright adapter. */
4
4
  export interface PlaywrightStepContext {
5
5
  page: Page;
6
6
  /** Current scope: the page, or the container locator inside within(). */
7
7
  scope: Page | Locator;
8
8
  }
9
- export declare class PlaywrightDriver implements TestDriver<PlaywrightStepContext> {
9
+ export declare class PlaywrightDriver implements TestDriver<PlaywrightStepContext, Page> {
10
10
  private page;
11
11
  private scope;
12
12
  private lastFormLocator;
@@ -15,6 +15,8 @@ export declare class PlaywrightDriver implements TestDriver<PlaywrightStepContex
15
15
  click(text: string): Promise<void>;
16
16
  clickLink(text: string): Promise<void>;
17
17
  clickButton(text: string): Promise<void>;
18
+ /** The single label-addressed lookup: every labelled verb goes through it. */
19
+ private labelled;
18
20
  private fieldByLabelOrPlaceholder;
19
21
  fillIn(label: string, value: string): Promise<void>;
20
22
  selectOption(label: string, option: string): Promise<void>;
@@ -22,7 +24,11 @@ export declare class PlaywrightDriver implements TestDriver<PlaywrightStepContex
22
24
  uncheck(label: string): Promise<void>;
23
25
  choose(label: string): Promise<void>;
24
26
  submit(): Promise<void>;
27
+ attachFile(label: string, path: string): Promise<void>;
28
+ /** @deprecated Older name for {@link PlaywrightDriver.attachFile}. */
25
29
  upload(label: string, path: string): Promise<void>;
30
+ pressKey(key: string): Promise<void>;
31
+ hover(text: string): Promise<void>;
26
32
  dropFile(selector: string, path: string): Promise<void>;
27
33
  assertHas(selector: string, opts?: AssertHasOptions): Promise<void>;
28
34
  refuteHas(selector: string, opts?: AssertHasOptions): Promise<void>;
@@ -35,8 +41,17 @@ export declare class PlaywrightDriver implements TestDriver<PlaywrightStepContex
35
41
  assertOptions(label: string, optionLabels: string[]): Promise<void>;
36
42
  assertPath(path: string, opts?: AssertPathOptions): Promise<void>;
37
43
  refutePath(path: string): Promise<void>;
44
+ assertDownload(expected: string | RegExp, trigger: () => Promise<void>, opts?: DownloadOptions): Promise<void>;
45
+ until(description: string, predicate: UntilPredicate<PlaywrightStepContext>, opts?: UntilOptions): Promise<void>;
38
46
  step(fn: (context: PlaywrightStepContext) => Promise<unknown>): Promise<void>;
39
- within(selector: string): Promise<TestDriver<PlaywrightStepContext>>;
47
+ /**
48
+ * raw() hands over the page itself, not the within() scope: it exists for
49
+ * the cases the DSL does not model, where re-scoping is the caller's job.
50
+ */
51
+ raw(fn: (page: Page) => unknown | Promise<unknown>): Promise<void>;
52
+ /** The adapter context handed to step(), until(), and friends. */
53
+ protected context(): PlaywrightStepContext;
54
+ within(selector: string): Promise<TestDriver<PlaywrightStepContext, Page>>;
40
55
  debug(): Promise<void>;
41
56
  wrapStep(name: string, fn: () => Promise<void>): Promise<void>;
42
57
  }
@@ -1 +1 @@
1
- {"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../../src/playwright/driver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../../src/playwright/driver.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,21 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { basename } from "node:path";
3
3
  import { expect, test } from "@playwright/test";
4
+ /**
5
+ * Playwright matches a bare string name/label/text as a case-insensitive
6
+ * SUBSTRING. That makes every text-addressed verb ambient: `clickButton('Check')`
7
+ * also matches an unrelated "Checklist Run — checklist" control that happens to
8
+ * be on the page, and the run dies on a strict-mode violation whose appearance
9
+ * depends on what else rendered. When a spec names a control it means *that*
10
+ * control, so every addressing matcher here passes `exact: true`. Playwright
11
+ * still normalizes whitespace under exact matching, so multi-line markup and
12
+ * padded labels keep working.
13
+ *
14
+ * Assertions (assertText/refuteText/assertHas) deliberately stay substring:
15
+ * they ask "does this text appear", and an exact `refuteText` would pass while
16
+ * the text is plainly on the page inside a longer string.
17
+ */
18
+ const EXACT = { exact: true };
4
19
  export class PlaywrightDriver {
5
20
  page;
6
21
  scope;
@@ -13,23 +28,25 @@ export class PlaywrightDriver {
13
28
  await this.page.goto(path);
14
29
  }
15
30
  async click(text) {
16
- await this.scope.getByText(text).click();
31
+ await this.scope.getByText(text, EXACT).click();
17
32
  }
18
33
  async clickLink(text) {
19
- await this.scope.getByRole("link", { name: text }).click();
34
+ await this.scope.getByRole("link", { name: text, ...EXACT }).click();
20
35
  }
21
36
  async clickButton(text) {
22
- await this.scope.getByRole("button", { name: text }).click();
37
+ await this.scope.getByRole("button", { name: text, ...EXACT }).click();
38
+ }
39
+ /** The single label-addressed lookup: every labelled verb goes through it. */
40
+ labelled(label) {
41
+ return this.scope.getByLabel(label, EXACT);
23
42
  }
24
43
  fieldByLabelOrPlaceholder(label) {
25
44
  // .or() lets Playwright auto-wait on whichever appears, so
26
45
  // async-rendered labeled fields don't fall through to the
27
- // placeholder branch. Placeholder matching is exact — substring
46
+ // placeholder branch. Both branches match exactly — substring
28
47
  // matching would collide with labels ("Name" vs placeholder
29
48
  // "Nickname") and trip strict mode.
30
- return this.scope
31
- .getByLabel(label)
32
- .or(this.scope.getByPlaceholder(label, { exact: true }));
49
+ return this.labelled(label).or(this.scope.getByPlaceholder(label, EXACT));
33
50
  }
34
51
  async fillIn(label, value) {
35
52
  const field = this.fieldByLabelOrPlaceholder(label);
@@ -37,22 +54,22 @@ export class PlaywrightDriver {
37
54
  this.lastFormLocator = this.scope.locator("form", { has: field });
38
55
  }
39
56
  async selectOption(label, option) {
40
- const select = this.scope.getByLabel(label);
57
+ const select = this.labelled(label);
41
58
  await select.selectOption({ label: option });
42
59
  this.lastFormLocator = this.scope.locator("form", { has: select });
43
60
  }
44
61
  async check(label) {
45
- const checkbox = this.scope.getByLabel(label);
62
+ const checkbox = this.labelled(label);
46
63
  await checkbox.check();
47
64
  this.lastFormLocator = this.scope.locator("form", { has: checkbox });
48
65
  }
49
66
  async uncheck(label) {
50
- const checkbox = this.scope.getByLabel(label);
67
+ const checkbox = this.labelled(label);
51
68
  await checkbox.uncheck();
52
69
  this.lastFormLocator = this.scope.locator("form", { has: checkbox });
53
70
  }
54
71
  async choose(label) {
55
- const radio = this.scope.getByRole("radio", { name: label });
72
+ const radio = this.scope.getByRole("radio", { name: label, ...EXACT });
56
73
  await radio.check();
57
74
  this.lastFormLocator = this.scope.locator("form", { has: radio });
58
75
  }
@@ -82,11 +99,21 @@ export class PlaywrightDriver {
82
99
  .press("Enter");
83
100
  }
84
101
  }
85
- async upload(label, path) {
86
- const input = this.scope.getByLabel(label);
102
+ async attachFile(label, path) {
103
+ const input = this.labelled(label);
87
104
  await input.setInputFiles(path);
88
105
  this.lastFormLocator = this.scope.locator("form", { has: input });
89
106
  }
107
+ /** @deprecated Older name for {@link PlaywrightDriver.attachFile}. */
108
+ async upload(label, path) {
109
+ await this.attachFile(label, path);
110
+ }
111
+ async pressKey(key) {
112
+ await this.page.keyboard.press(key);
113
+ }
114
+ async hover(text) {
115
+ await this.scope.getByText(text, EXACT).hover();
116
+ }
90
117
  async dropFile(selector, path) {
91
118
  const content = await readFile(path);
92
119
  const name = basename(path);
@@ -133,17 +160,17 @@ export class PlaywrightDriver {
133
160
  await expect(this.fieldByLabelOrPlaceholder(label)).toHaveValue(value);
134
161
  }
135
162
  async assertChecked(label) {
136
- await expect(this.scope.getByLabel(label)).toBeChecked();
163
+ await expect(this.labelled(label)).toBeChecked();
137
164
  }
138
165
  async refuteChecked(label) {
139
- await expect(this.scope.getByLabel(label)).not.toBeChecked();
166
+ await expect(this.labelled(label)).not.toBeChecked();
140
167
  }
141
168
  async assertSelected(label, optionLabel) {
142
- const select = this.scope.getByLabel(label);
169
+ const select = this.labelled(label);
143
170
  await expect(select.locator("option:checked")).toHaveText(optionLabel);
144
171
  }
145
172
  async assertOptions(label, optionLabels) {
146
- const select = this.scope.getByLabel(label);
173
+ const select = this.labelled(label);
147
174
  await expect(select.locator("option")).toHaveText(optionLabels);
148
175
  }
149
176
  async assertPath(path, opts) {
@@ -166,8 +193,45 @@ export class PlaywrightDriver {
166
193
  })
167
194
  .not.toBe(path);
168
195
  }
196
+ async assertDownload(expected, trigger, opts) {
197
+ // The wait has to be armed before the click that starts the download,
198
+ // which is why the trigger arrives as a callback rather than as an
199
+ // earlier step in the chain.
200
+ const [download] = await Promise.all([
201
+ this.page.waitForEvent("download", { timeout: opts?.timeout }),
202
+ trigger(),
203
+ ]);
204
+ const filename = download.suggestedFilename();
205
+ const matched = typeof expected === "string"
206
+ ? filename === expected
207
+ : expected.test(filename);
208
+ if (!matched) {
209
+ throw new Error(`assertDownload(${typeof expected === "string" ? `'${expected}'` : String(expected)}): ` +
210
+ `the browser offered a download named '${filename}' instead.`);
211
+ }
212
+ }
213
+ async until(description, predicate, opts) {
214
+ await expect
215
+ .poll(async () => Boolean(await predicate(this.context())), {
216
+ message: `until: ${description}`,
217
+ timeout: opts?.timeout,
218
+ intervals: opts?.interval === undefined ? undefined : [opts.interval],
219
+ })
220
+ .toBe(true);
221
+ }
169
222
  async step(fn) {
170
- await fn({ page: this.page, scope: this.scope });
223
+ await fn(this.context());
224
+ }
225
+ /**
226
+ * raw() hands over the page itself, not the within() scope: it exists for
227
+ * the cases the DSL does not model, where re-scoping is the caller's job.
228
+ */
229
+ async raw(fn) {
230
+ await fn(this.page);
231
+ }
232
+ /** The adapter context handed to step(), until(), and friends. */
233
+ context() {
234
+ return { page: this.page, scope: this.scope };
171
235
  }
172
236
  async within(selector) {
173
237
  const scopedLocator = this.scope.locator(selector);
@@ -2,12 +2,12 @@ import { type Page } from "@playwright/test";
2
2
  import { Session } from "../session.js";
3
3
  import { type PlaywrightStepContext } from "./driver.js";
4
4
  export { Session } from "../session.js";
5
- export { StepError } from "../errors.js";
5
+ export { StepError, BrowserOnlyVerbError } from "../errors.js";
6
6
  export { PlaywrightDriver, type PlaywrightStepContext } from "./driver.js";
7
- export type { AssertHasOptions, AssertPathOptions, TestDriver, } from "../types.js";
8
- export declare function createSession(page: Page): Session<PlaywrightStepContext>;
7
+ export type { AssertHasOptions, AssertPathOptions, DownloadOptions, TestDriver, UntilOptions, UntilPredicate, } from "../types.js";
8
+ export declare function createSession(page: Page): Session<PlaywrightStepContext, Page>;
9
9
  export declare const test: import("playwright/test").TestType<import("playwright/test").PlaywrightTestArgs & import("playwright/test").PlaywrightTestOptions & {
10
- session: Session<PlaywrightStepContext>;
10
+ session: Session<PlaywrightStepContext, Page>;
11
11
  }, import("playwright/test").PlaywrightWorkerArgs & import("playwright/test").PlaywrightWorkerOptions>;
12
12
  export { expect } from "@playwright/test";
13
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/playwright/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAoB,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE3E,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAC3E,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,GACX,MAAM,aAAa,CAAC;AAErB,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAExE;AAED,eAAO,MAAM,IAAI;aAA0B,OAAO,CAAC,qBAAqB,CAAC;sGAIvE,CAAC;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/playwright/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAoB,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE3E,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAC3E,YAAY,EACV,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,UAAU,EACV,YAAY,EACZ,cAAc,GACf,MAAM,aAAa,CAAC;AAErB,wBAAgB,aAAa,CAC3B,IAAI,EAAE,IAAI,GACT,OAAO,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAEtC;AAED,eAAO,MAAM,IAAI;aACN,OAAO,CAAC,qBAAqB,EAAE,IAAI,CAAC;sGAK7C,CAAC;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC"}
@@ -2,7 +2,7 @@ import { test as base } from "@playwright/test";
2
2
  import { Session } from "../session.js";
3
3
  import { PlaywrightDriver } from "./driver.js";
4
4
  export { Session } from "../session.js";
5
- export { StepError } from "../errors.js";
5
+ export { StepError, BrowserOnlyVerbError } from "../errors.js";
6
6
  export { PlaywrightDriver } from "./driver.js";
7
7
  export function createSession(page) {
8
8
  return new Session(new PlaywrightDriver(page));
@@ -1,6 +1,8 @@
1
1
  import { screen, within as rtlWithin } from "@testing-library/react";
2
2
  import { type UserEvent } from "@testing-library/user-event";
3
- import type { AssertHasOptions, TestDriver } from "../types.js";
3
+ import type { AssertHasOptions, DownloadOptions, TestDriver, UntilOptions, UntilPredicate } from "../types.js";
4
+ /** The scoped query object raw() callbacks receive in the RTL adapter. */
5
+ export type RTLQueries = ReturnType<typeof rtlWithin>;
4
6
  /** Context handed to custom step() callbacks in the RTL adapter. */
5
7
  export interface RTLStepContext {
6
8
  user: UserEvent;
@@ -10,14 +12,36 @@ export interface RTLStepContext {
10
12
  /**
11
13
  * RTL adapter implementing the subset of TestDriver that applies in JSDOM.
12
14
  * Navigation methods (visit, assertPath, refutePath) are not supported.
15
+ *
16
+ * Everything a host adapter is likely to specialize is `protected`: the
17
+ * label-to-control lookup (`findField`), the scoped-driver factory
18
+ * (`scoped`), and the `user` / `root` / `lastFormElement` state the verbs
19
+ * share. Subclass it rather than reimplementing the DSL when an app's markup
20
+ * needs a different lookup — that is how feather-testing-postgres binds this
21
+ * driver to markup whose labels are wrapper siblings rather than `htmlFor`
22
+ * targets.
13
23
  */
14
- export declare class RTLDriver implements TestDriver<RTLStepContext> {
15
- private user;
16
- private container;
17
- private lastFormElement;
18
- constructor(user?: UserEvent, container?: HTMLElement);
19
- private rootElement;
20
- private findFieldByLabelOrPlaceholder;
24
+ export declare class RTLDriver implements TestDriver<RTLStepContext, RTLQueries> {
25
+ protected user: UserEvent;
26
+ /** The element every query and selector in this driver resolves against. */
27
+ protected root: HTMLElement;
28
+ protected container: ReturnType<typeof rtlWithin>;
29
+ protected lastFormElement: HTMLFormElement | null;
30
+ /** Per-lookup timeout in ms; undefined leaves RTL's own default in place. */
31
+ protected timeout: number | undefined;
32
+ constructor(user?: UserEvent, container?: HTMLElement, timeout?: number);
33
+ protected rootElement(): HTMLElement;
34
+ /** Options forwarded to every async query and waitFor call. */
35
+ protected waitOpts(): {
36
+ timeout?: number;
37
+ };
38
+ /**
39
+ * The single label-addressed lookup: every labelled verb goes through it,
40
+ * so overriding this one method retargets them all.
41
+ */
42
+ protected findField(label: string): Promise<HTMLElement>;
43
+ /** Subclasses override this so within() yields a driver of their own type. */
44
+ protected scoped(element: HTMLElement): TestDriver<RTLStepContext, RTLQueries>;
21
45
  visit(): Promise<void>;
22
46
  click(text: string): Promise<void>;
23
47
  clickLink(text: string): Promise<void>;
@@ -28,7 +52,16 @@ export declare class RTLDriver implements TestDriver<RTLStepContext> {
28
52
  uncheck(label: string): Promise<void>;
29
53
  choose(label: string): Promise<void>;
30
54
  submit(): Promise<void>;
55
+ attachFile(label: string, path: string): Promise<void>;
56
+ /** @deprecated Older name for {@link RTLDriver.attachFile}. */
31
57
  upload(label: string, path: string): Promise<void>;
58
+ /**
59
+ * Keys are named the Playwright way ('Enter', 'Control+A') and translated
60
+ * to user-event's keyboard syntax, so one spec reads the same on both
61
+ * adapters.
62
+ */
63
+ pressKey(key: string): Promise<void>;
64
+ hover(text: string): Promise<void>;
32
65
  dropFile(selector: string, path: string): Promise<void>;
33
66
  assertHas(_selector: string, _opts?: AssertHasOptions): Promise<void>;
34
67
  refuteHas(_selector: string, _opts?: AssertHasOptions): Promise<void>;
@@ -41,8 +74,21 @@ export declare class RTLDriver implements TestDriver<RTLStepContext> {
41
74
  assertOptions(label: string, optionLabels: string[]): Promise<void>;
42
75
  assertPath(): Promise<void>;
43
76
  refutePath(): Promise<void>;
77
+ assertDownload(_expected: string | RegExp, _trigger: () => Promise<void>, _opts?: DownloadOptions): Promise<void>;
78
+ until(description: string, predicate: UntilPredicate<RTLStepContext>, opts?: UntilOptions): Promise<void>;
44
79
  step(fn: (context: RTLStepContext) => Promise<unknown>): Promise<void>;
45
- within(selector: string): Promise<TestDriver<RTLStepContext>>;
80
+ /** raw() hands over the scoped query object — `within(root)`, i.e. screen
81
+ * when the driver is unscoped. */
82
+ raw(fn: (queries: RTLQueries) => unknown | Promise<unknown>): Promise<void>;
83
+ /** The adapter context handed to step(), until(), and friends. */
84
+ protected context(): RTLStepContext;
85
+ within(selector: string): Promise<TestDriver<RTLStepContext, RTLQueries>>;
46
86
  debug(): Promise<void>;
47
87
  }
88
+ /**
89
+ * 'Enter' -> '{Enter}', 'a' -> 'a', 'Control+A' -> '{Control>}A{/Control}'.
90
+ * Printable characters are typed literally with user-event's own `{` and `[`
91
+ * escapes applied, so a key name never turns into a descriptor by accident.
92
+ */
93
+ export declare function toKeyboardSyntax(key: string): string;
48
94
  //# sourceMappingURL=driver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../../src/rtl/driver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../../src/rtl/driver.ts"],"names":[],"mappings":"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"}