feather-testing-core 0.3.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 +133 -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 +16 -3
  27. package/dist/playwright/driver.d.ts.map +1 -1
  28. package/dist/playwright/driver.js +49 -2
  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 +28 -4
  33. package/dist/rtl/driver.d.ts.map +1 -1
  34. package/dist/rtl/driver.js +78 -2
  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 +36 -4
  39. package/dist/session.d.ts.map +1 -1
  40. package/dist/session.js +67 -1
  41. package/dist/types.d.ts +35 -3
  42. package/dist/types.d.ts.map +1 -1
  43. package/package.json +10 -2
package/dist/session.js CHANGED
@@ -1,4 +1,19 @@
1
1
  import { StepError } from "./errors.js";
2
+ /**
3
+ * Verbs that take a human description exist so a failure names intent rather
4
+ * than mechanics. An empty description defeats that, so it is rejected where
5
+ * the mistake is — at the call site, before the chain runs.
6
+ */
7
+ function requireDescription(verb, value) {
8
+ if (typeof value !== "string" || value.trim() === "") {
9
+ throw new Error(`feather-testing-core: ${verb}() requires a non-empty description as its ` +
10
+ "first argument — it is what the chain trace prints when the step fails.");
11
+ }
12
+ }
13
+ /** How a string-or-regex expectation reads in the chain trace. */
14
+ function describe(expected) {
15
+ return typeof expected === "string" ? expected : String(expected);
16
+ }
2
17
  export class Session {
3
18
  driver;
4
19
  steps = [];
@@ -62,12 +77,27 @@ export class Session {
62
77
  submit() {
63
78
  return this.enqueue("submit()", () => this.driver.submit());
64
79
  }
80
+ /** Set a file input, found by its label, to the file at `path`. */
81
+ attachFile(label, path) {
82
+ return this.enqueue(`attachFile('${label}', '${path}')`, () => this.driver.attachFile(label, path));
83
+ }
84
+ /** @deprecated Older name for {@link Session.attachFile}. */
65
85
  upload(label, path) {
66
- return this.enqueue(`upload('${label}', '${path}')`, () => this.driver.upload(label, path));
86
+ return this.enqueue(`upload('${label}', '${path}')`, () => this.driver.upload
87
+ ? this.driver.upload(label, path)
88
+ : this.driver.attachFile(label, path));
67
89
  }
68
90
  dropFile(selector, path) {
69
91
  return this.enqueue(`dropFile('${selector}', '${path}')`, () => this.driver.dropFile(selector, path));
70
92
  }
93
+ /** Press a key on the focused element, e.g. 'Enter' or 'Control+A'. */
94
+ pressKey(key) {
95
+ return this.enqueue(`pressKey('${key}')`, () => this.driver.pressKey(key));
96
+ }
97
+ /** Hover the element with this text. */
98
+ hover(text) {
99
+ return this.enqueue(`hover('${text}')`, () => this.driver.hover(text));
100
+ }
71
101
  // --- Assertions ---
72
102
  assertText(text) {
73
103
  return this.enqueue(`assertText('${text}')`, () => this.driver.assertText(text));
@@ -109,6 +139,32 @@ export class Session {
109
139
  refutePath(path) {
110
140
  return this.enqueue(`refutePath('${path}')`, () => this.driver.refutePath(path));
111
141
  }
142
+ /**
143
+ * Assert that running `trigger` makes the browser offer a download whose
144
+ * suggested filename matches `expected`. The trigger is a callback because
145
+ * the wait has to be armed before the click that starts the download.
146
+ *
147
+ * Browser-only: the RTL adapter throws a BrowserOnlyVerbError.
148
+ */
149
+ assertDownload(expected, trigger, opts) {
150
+ return this.enqueue(`assertDownload('${describe(expected)}')`, () => this.driver.assertDownload(expected, async () => {
151
+ await trigger(new Session(this.driver));
152
+ }, opts));
153
+ }
154
+ // --- Waiting ---
155
+ /**
156
+ * Wait for a condition instead of sleeping. `description` is mandatory: it
157
+ * is what the chain trace prints, so a timeout reads
158
+ * `[FAILED] until: the export finishes` rather than naming a mechanism.
159
+ *
160
+ * The predicate receives the adapter's context ({ page, scope } for
161
+ * Playwright, { user, container } for RTL) and may be sync or async; it
162
+ * is polled until it returns something truthy or the budget is spent.
163
+ */
164
+ until(description, predicate, opts) {
165
+ requireDescription("until", description);
166
+ return this.enqueue(`until: ${description}`, () => this.driver.until(description, predicate, opts));
167
+ }
112
168
  // --- Escape hatch ---
113
169
  /**
114
170
  * Queue a named custom step. `fn` receives the adapter's context
@@ -119,6 +175,16 @@ export class Session {
119
175
  step(name, fn) {
120
176
  return this.enqueue(`step('${name}')`, () => this.driver.step(fn));
121
177
  }
178
+ /**
179
+ * Drop to the driver itself — Playwright's `page`, RTL's scoped queries —
180
+ * without leaving the chain. `label` is mandatory and registers as a named
181
+ * step, so an escape hatch still names intent in the trace instead of
182
+ * ending it: `[FAILED] raw('drag the card to Done')`.
183
+ */
184
+ raw(label, fn) {
185
+ requireDescription("raw", label);
186
+ return this.enqueue(`raw('${label}')`, () => this.driver.raw(fn));
187
+ }
122
188
  // --- Scoping ---
123
189
  /**
124
190
  * `fn` must either return the scoped session — so its queued steps run — or
package/dist/types.d.ts CHANGED
@@ -7,16 +7,31 @@ export interface AssertHasOptions {
7
7
  export interface AssertPathOptions {
8
8
  queryParams?: Record<string, string>;
9
9
  }
10
+ export interface UntilOptions {
11
+ /** Overall budget in ms. Defaults to the adapter's own wait timeout. */
12
+ timeout?: number;
13
+ /** Gap between polls in ms. Defaults to the adapter's own cadence. */
14
+ interval?: number;
15
+ }
16
+ export interface DownloadOptions {
17
+ /** How long to wait for the download to start, in ms. */
18
+ timeout?: number;
19
+ }
10
20
  export interface QueuedStep {
11
21
  name: string;
12
22
  action: () => Promise<void>;
13
23
  index: number;
14
24
  }
25
+ /** A condition polled by `until()`; may be sync or async. */
26
+ export type UntilPredicate<TContext> = (context: TContext) => unknown | Promise<unknown>;
15
27
  /**
16
28
  * TContext is the adapter-specific context handed to custom step() callbacks
17
29
  * (e.g. { page, scope } for Playwright, { user, container } for RTL).
30
+ *
31
+ * TNative is the adapter's own driving handle, handed to raw() callbacks:
32
+ * Playwright's `Page`, RTL's scoped query object.
18
33
  */
19
- export interface TestDriver<TContext = unknown> {
34
+ export interface TestDriver<TContext = unknown, TNative = unknown> {
20
35
  visit(path: string): Promise<void>;
21
36
  click(text: string): Promise<void>;
22
37
  clickLink(text: string): Promise<void>;
@@ -27,8 +42,12 @@ export interface TestDriver<TContext = unknown> {
27
42
  uncheck(label: string): Promise<void>;
28
43
  choose(label: string): Promise<void>;
29
44
  submit(): Promise<void>;
30
- upload(label: string, path: string): Promise<void>;
45
+ attachFile(label: string, path: string): Promise<void>;
46
+ /** @deprecated The older name for {@link TestDriver.attachFile}. */
47
+ upload?(label: string, path: string): Promise<void>;
31
48
  dropFile(selector: string, path: string): Promise<void>;
49
+ pressKey(key: string): Promise<void>;
50
+ hover(text: string): Promise<void>;
32
51
  assertHas(selector: string, opts?: AssertHasOptions): Promise<void>;
33
52
  refuteHas(selector: string, opts?: AssertHasOptions): Promise<void>;
34
53
  assertText(text: string): Promise<void>;
@@ -40,8 +59,21 @@ export interface TestDriver<TContext = unknown> {
40
59
  assertOptions(label: string, optionLabels: string[]): Promise<void>;
41
60
  assertPath(path: string, opts?: AssertPathOptions): Promise<void>;
42
61
  refutePath(path: string): Promise<void>;
62
+ /**
63
+ * Run `trigger` and assert the browser offers a download whose suggested
64
+ * filename matches `expected`. Browser-only: the RTL adapter throws.
65
+ */
66
+ assertDownload(expected: string | RegExp, trigger: () => Promise<void>, opts?: DownloadOptions): Promise<void>;
67
+ /**
68
+ * Poll `predicate` until it returns something truthy, or fail once the
69
+ * timeout is spent. `description` is what the chain trace prints, so it
70
+ * has to say what is being awaited in plain language.
71
+ */
72
+ until(description: string, predicate: UntilPredicate<TContext>, opts?: UntilOptions): Promise<void>;
43
73
  step(fn: (context: TContext) => Promise<unknown>): Promise<void>;
44
- within(selector: string): Promise<TestDriver<TContext>>;
74
+ /** Hand the caller the adapter's own driving handle, untyped by the DSL. */
75
+ raw(fn: (native: TNative) => unknown | Promise<unknown>): Promise<void>;
76
+ within(selector: string): Promise<TestDriver<TContext, TNative>>;
45
77
  debug(): Promise<void>;
46
78
  /**
47
79
  * Optional hook: wrap a queued step's execution (e.g. in Playwright's
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feather-testing-core",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Phoenix Test-inspired fluent testing DSL for Playwright and React Testing Library",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -32,6 +32,11 @@
32
32
  "types": "./dist/rtl/index.d.ts",
33
33
  "import": "./dist/rtl/index.js",
34
34
  "default": "./dist/rtl/index.js"
35
+ },
36
+ "./eslint-plugin": {
37
+ "types": "./dist/eslint-plugin/index.d.ts",
38
+ "import": "./dist/eslint-plugin/index.js",
39
+ "default": "./dist/eslint-plugin/index.js"
35
40
  }
36
41
  },
37
42
  "files": [
@@ -57,7 +62,8 @@
57
62
  "build": "tsc",
58
63
  "test": "vitest run",
59
64
  "test:pw": "playwright test",
60
- "test:all": "vitest run && playwright test"
65
+ "test:all": "vitest run && playwright test",
66
+ "lint": "npm run build && eslint ."
61
67
  },
62
68
  "devDependencies": {
63
69
  "@playwright/test": "^1.58.0",
@@ -67,6 +73,8 @@
67
73
  "@types/node": "^26.1.2",
68
74
  "@types/react": "^19.2.14",
69
75
  "@types/react-dom": "^19.2.3",
76
+ "@typescript-eslint/parser": "^8.68.0",
77
+ "eslint": "^9.39.5",
70
78
  "jsdom": "^28.1.0",
71
79
  "react": "^19.2.4",
72
80
  "react-dom": "^19.2.4",