@remix-run/test 0.0.0 → 0.2.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 (150) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +430 -2
  3. package/dist/app/client/entry.d.ts +2 -0
  4. package/dist/app/client/entry.d.ts.map +1 -0
  5. package/dist/app/client/entry.js +324 -0
  6. package/dist/app/client/iframe.d.ts +2 -0
  7. package/dist/app/client/iframe.d.ts.map +1 -0
  8. package/dist/app/client/iframe.js +22 -0
  9. package/dist/app/server.d.ts +6 -0
  10. package/dist/app/server.d.ts.map +1 -0
  11. package/dist/app/server.js +303 -0
  12. package/dist/cli-entry.d.ts +3 -0
  13. package/dist/cli-entry.d.ts.map +1 -0
  14. package/dist/cli-entry.js +14 -0
  15. package/dist/cli.d.ts +8 -0
  16. package/dist/cli.d.ts.map +1 -0
  17. package/dist/cli.js +305 -0
  18. package/dist/index.d.ts +6 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +2 -0
  21. package/dist/lib/colors.d.ts +2 -0
  22. package/dist/lib/colors.d.ts.map +1 -0
  23. package/dist/lib/colors.js +2 -0
  24. package/dist/lib/config.d.ts +91 -0
  25. package/dist/lib/config.d.ts.map +1 -0
  26. package/dist/lib/config.js +255 -0
  27. package/dist/lib/context.d.ts +93 -0
  28. package/dist/lib/context.d.ts.map +1 -0
  29. package/dist/lib/context.js +65 -0
  30. package/dist/lib/coverage-loader.d.ts +16 -0
  31. package/dist/lib/coverage-loader.d.ts.map +1 -0
  32. package/dist/lib/coverage-loader.js +20 -0
  33. package/dist/lib/coverage.d.ts +28 -0
  34. package/dist/lib/coverage.d.ts.map +1 -0
  35. package/dist/lib/coverage.js +212 -0
  36. package/dist/lib/executor.d.ts +4 -0
  37. package/dist/lib/executor.d.ts.map +1 -0
  38. package/dist/lib/executor.js +128 -0
  39. package/dist/lib/fake-timers.d.ts +6 -0
  40. package/dist/lib/fake-timers.d.ts.map +1 -0
  41. package/dist/lib/fake-timers.js +45 -0
  42. package/dist/lib/framework.d.ts +107 -0
  43. package/dist/lib/framework.d.ts.map +1 -0
  44. package/dist/lib/framework.js +198 -0
  45. package/dist/lib/import-module.d.ts +2 -0
  46. package/dist/lib/import-module.d.ts.map +1 -0
  47. package/dist/lib/import-module.js +29 -0
  48. package/dist/lib/mock.d.ts +52 -0
  49. package/dist/lib/mock.d.ts.map +1 -0
  50. package/dist/lib/mock.js +61 -0
  51. package/dist/lib/normalize.d.ts +2 -0
  52. package/dist/lib/normalize.d.ts.map +1 -0
  53. package/dist/lib/normalize.js +18 -0
  54. package/dist/lib/playwright.d.ts +15 -0
  55. package/dist/lib/playwright.d.ts.map +1 -0
  56. package/dist/lib/playwright.js +81 -0
  57. package/dist/lib/reporters/dot.d.ts +9 -0
  58. package/dist/lib/reporters/dot.d.ts.map +1 -0
  59. package/dist/lib/reporters/dot.js +56 -0
  60. package/dist/lib/reporters/files.d.ts +9 -0
  61. package/dist/lib/reporters/files.d.ts.map +1 -0
  62. package/dist/lib/reporters/files.js +71 -0
  63. package/dist/lib/reporters/index.d.ts +13 -0
  64. package/dist/lib/reporters/index.d.ts.map +1 -0
  65. package/dist/lib/reporters/index.js +18 -0
  66. package/dist/lib/reporters/results.d.ts +30 -0
  67. package/dist/lib/reporters/results.d.ts.map +1 -0
  68. package/dist/lib/reporters/results.js +1 -0
  69. package/dist/lib/reporters/spec.d.ts +9 -0
  70. package/dist/lib/reporters/spec.d.ts.map +1 -0
  71. package/dist/lib/reporters/spec.js +153 -0
  72. package/dist/lib/reporters/tap.d.ts +9 -0
  73. package/dist/lib/reporters/tap.d.ts.map +1 -0
  74. package/dist/lib/reporters/tap.js +54 -0
  75. package/dist/lib/runner-browser.d.ts +21 -0
  76. package/dist/lib/runner-browser.d.ts.map +1 -0
  77. package/dist/lib/runner-browser.js +117 -0
  78. package/dist/lib/runner.d.ts +14 -0
  79. package/dist/lib/runner.d.ts.map +1 -0
  80. package/dist/lib/runner.js +118 -0
  81. package/dist/lib/runtime.d.ts +2 -0
  82. package/dist/lib/runtime.d.ts.map +1 -0
  83. package/dist/lib/runtime.js +2 -0
  84. package/dist/lib/ts-transform.d.ts +4 -0
  85. package/dist/lib/ts-transform.d.ts.map +1 -0
  86. package/dist/lib/ts-transform.js +29 -0
  87. package/dist/lib/watcher.d.ts +5 -0
  88. package/dist/lib/watcher.d.ts.map +1 -0
  89. package/dist/lib/watcher.js +39 -0
  90. package/dist/lib/worker-e2e.d.ts +2 -0
  91. package/dist/lib/worker-e2e.d.ts.map +1 -0
  92. package/dist/lib/worker-e2e.js +49 -0
  93. package/dist/lib/worker.d.ts +2 -0
  94. package/dist/lib/worker.d.ts.map +1 -0
  95. package/dist/lib/worker.js +57 -0
  96. package/dist/test/coverage/fixture.d.ts +5 -0
  97. package/dist/test/coverage/fixture.d.ts.map +1 -0
  98. package/dist/test/coverage/fixture.js +32 -0
  99. package/dist/test/coverage/test-browser.d.ts +2 -0
  100. package/dist/test/coverage/test-browser.d.ts.map +1 -0
  101. package/dist/test/coverage/test-browser.js +24 -0
  102. package/dist/test/coverage/test-e2e.d.ts +2 -0
  103. package/dist/test/coverage/test-e2e.d.ts.map +1 -0
  104. package/dist/test/coverage/test-e2e.js +60 -0
  105. package/dist/test/coverage/test-unit.d.ts +2 -0
  106. package/dist/test/coverage/test-unit.d.ts.map +1 -0
  107. package/dist/test/coverage/test-unit.js +27 -0
  108. package/dist/test/framework.test.browser.d.ts +2 -0
  109. package/dist/test/framework.test.browser.d.ts.map +1 -0
  110. package/dist/test/framework.test.browser.js +107 -0
  111. package/dist/test/framework.test.e2e.d.ts +2 -0
  112. package/dist/test/framework.test.e2e.d.ts.map +1 -0
  113. package/dist/test/framework.test.e2e.js +34 -0
  114. package/package.json +79 -5
  115. package/src/app/client/entry.ts +353 -0
  116. package/src/app/client/iframe.ts +18 -0
  117. package/src/app/server.ts +336 -0
  118. package/src/cli-entry.ts +15 -0
  119. package/src/cli.ts +384 -0
  120. package/src/index.ts +16 -0
  121. package/src/lib/colors.ts +3 -0
  122. package/src/lib/config.ts +377 -0
  123. package/src/lib/context.ts +168 -0
  124. package/src/lib/coverage-loader.ts +31 -0
  125. package/src/lib/coverage.ts +320 -0
  126. package/src/lib/executor.ts +145 -0
  127. package/src/lib/fake-timers.ts +64 -0
  128. package/src/lib/framework.ts +251 -0
  129. package/src/lib/import-module.ts +29 -0
  130. package/src/lib/mock.ts +89 -0
  131. package/src/lib/normalize.ts +22 -0
  132. package/src/lib/playwright.ts +100 -0
  133. package/src/lib/reporters/dot.ts +58 -0
  134. package/src/lib/reporters/files.ts +77 -0
  135. package/src/lib/reporters/index.ts +27 -0
  136. package/src/lib/reporters/results.ts +29 -0
  137. package/src/lib/reporters/spec.ts +174 -0
  138. package/src/lib/reporters/tap.ts +58 -0
  139. package/src/lib/runner-browser.ts +165 -0
  140. package/src/lib/runner.ts +189 -0
  141. package/src/lib/runtime.ts +2 -0
  142. package/src/lib/ts-transform.ts +36 -0
  143. package/src/lib/watcher.ts +46 -0
  144. package/src/lib/worker-e2e.ts +54 -0
  145. package/src/lib/worker.ts +50 -0
  146. package/src/test/coverage/fixture.ts +34 -0
  147. package/src/test/coverage/test-browser.ts +29 -0
  148. package/src/test/coverage/test-e2e.ts +70 -0
  149. package/src/test/coverage/test-unit.ts +32 -0
  150. package/tsconfig.json +16 -0
@@ -0,0 +1,198 @@
1
+ // Holds lifecycle hooks registered at the top level (outside any describe).
2
+ // Top-level describes inherit these hooks just like nested describes inherit
3
+ // from their parent.
4
+ const rootHooks = {};
5
+ let currentSuite = null;
6
+ const rootSuites = [];
7
+ // Lazily-created suite for top-level it() calls outside any describe().
8
+ // Name '' causes the reporter to display these tests under "Global".
9
+ // We check rootSuites.includes() so the suite is re-created after the executor
10
+ // clears rootSuites between files (suites.length = 0) or after captureRegistration splices it.
11
+ let implicitRootSuite = null;
12
+ function getImplicitRootSuite() {
13
+ if (!implicitRootSuite || !rootSuites.includes(implicitRootSuite)) {
14
+ implicitRootSuite = { name: '', tests: [], ...rootHooks };
15
+ rootSuites.push(implicitRootSuite);
16
+ }
17
+ return implicitRootSuite;
18
+ }
19
+ // Expose for executor.ts which reads this global
20
+ ;
21
+ globalThis.__testSuites = rootSuites;
22
+ function registerDescribe(name, fn, flags) {
23
+ // Nested describes are flattened: "Parent > Child"
24
+ let fullName = currentSuite ? `${currentSuite.name} > ${name}` : name;
25
+ if (rootSuites.some((s) => s.name === fullName)) {
26
+ throw new Error(`Duplicate suite name: "${fullName}"`);
27
+ }
28
+ let suite = { name: fullName, tests: [], ...flags };
29
+ // Inherit lifecycle hooks from parent suite (or root hooks if at top level)
30
+ let parent = currentSuite ?? rootHooks;
31
+ if (parent.beforeEach)
32
+ suite.beforeEach = parent.beforeEach;
33
+ if (parent.afterEach)
34
+ suite.afterEach = parent.afterEach;
35
+ if (parent.beforeAll)
36
+ suite.beforeAll = parent.beforeAll;
37
+ if (parent.afterAll)
38
+ suite.afterAll = parent.afterAll;
39
+ let insertedAt = rootSuites.length;
40
+ rootSuites.push(suite);
41
+ let prevSuite = currentSuite;
42
+ currentSuite = suite;
43
+ try {
44
+ fn();
45
+ }
46
+ catch (error) {
47
+ // Remove this suite and any suites registered during fn() so they don't
48
+ // end up in the executor after a failed registration call
49
+ rootSuites.splice(insertedAt);
50
+ throw error;
51
+ }
52
+ finally {
53
+ currentSuite = prevSuite;
54
+ }
55
+ }
56
+ /**
57
+ * Groups related tests into a named suite. Suites can be nested snd will be displayed
58
+ * as such or joined with ` > ` in reporter output. Lifecycle hooks registered inside
59
+ * a `describe` block apply only to tests within that block.
60
+ *
61
+ * @example
62
+ * describe('auth', () => {
63
+ * it('logs in', async () => { ... })
64
+ * })
65
+ *
66
+ * // Modifiers
67
+ * describe.skip('skipped suite', () => { ... })
68
+ * describe.only('focused suite', () => { ... })
69
+ * describe.todo('planned suite')
70
+ *
71
+ * @param name - The suite name shown in reporter output.
72
+ * @param fn - A function that registers the tests and lifecycle hooks in this suite.
73
+ */
74
+ export const describe = Object.assign((name, metaOrFn, fn) => {
75
+ let meta = typeof metaOrFn === 'function' ? {} : metaOrFn;
76
+ let suiteFn = typeof metaOrFn === 'function' ? metaOrFn : fn;
77
+ registerDescribe(name, suiteFn, meta);
78
+ }, {
79
+ skip: (name, fn) => registerDescribe(name, fn, { skip: true }),
80
+ only: (name, fn) => registerDescribe(name, fn, { only: true }),
81
+ todo: (name) => {
82
+ let fullName = currentSuite ? `${currentSuite.name} > ${name}` : name;
83
+ if (rootSuites.some((s) => s.name === fullName)) {
84
+ throw new Error(`Duplicate suite name: "${fullName}"`);
85
+ }
86
+ rootSuites.push({ name: fullName, tests: [], todo: true });
87
+ },
88
+ });
89
+ function registerIt(name, fn, flags) {
90
+ let suite = currentSuite ?? getImplicitRootSuite();
91
+ if (suite.tests.some((t) => t.name === name)) {
92
+ throw new Error(`Duplicate test name: "${name}" in suite "${suite.name || 'Global'}"`);
93
+ }
94
+ suite.tests.push({ name, fn, suite, ...flags });
95
+ }
96
+ /**
97
+ * Defines a single test case. The optional `TestContext` argument `t` provides
98
+ * mock helpers and per-test cleanup registration.
99
+ *
100
+ * @example
101
+ * it('returns 200 for the home route', async () => {
102
+ * const res = await router.fetch('/')
103
+ * assert.equal(res.status, 200)
104
+ * })
105
+ *
106
+ * // Modifiers
107
+ * it.skip('not ready yet', () => { ... })
108
+ * it.only('focused test', () => { ... })
109
+ * it.todo('coming soon')
110
+ *
111
+ * @param name - The test name shown in reporter output.
112
+ * @param fn - The test body, receiving a {@link TestContext} as its first argument.
113
+ */
114
+ export const it = Object.assign((name, metaOrFn, fn) => {
115
+ let meta = typeof metaOrFn === 'function' ? {} : metaOrFn;
116
+ let testFn = typeof metaOrFn === 'function' ? metaOrFn : fn;
117
+ registerIt(name, testFn, meta);
118
+ }, {
119
+ skip: (name, fn) => registerIt(name, fn ?? (() => { }), { skip: true }),
120
+ only: (name, fn) => registerIt(name, fn, { only: true }),
121
+ todo: (name) => {
122
+ let suite = currentSuite ?? getImplicitRootSuite();
123
+ if (suite.tests.some((t) => t.name === name)) {
124
+ throw new Error(`Duplicate test name: "${name}" in suite "${suite.name || 'Global'}"`);
125
+ }
126
+ suite.tests.push({ name, fn: () => { }, suite, todo: true });
127
+ },
128
+ });
129
+ /** Alias for {@link describe}. */
130
+ export const suite = describe;
131
+ /** Alias for {@link it}. */
132
+ export const test = it;
133
+ function chainBefore(existing, fn) {
134
+ return existing
135
+ ? async () => {
136
+ await existing();
137
+ await fn();
138
+ }
139
+ : fn;
140
+ }
141
+ function chainAfter(existing, fn) {
142
+ // Child/later runs first, then earlier (reverse order)
143
+ return existing
144
+ ? async () => {
145
+ await fn();
146
+ await existing();
147
+ }
148
+ : fn;
149
+ }
150
+ /**
151
+ * Registers a hook that runs before **each** test in the current suite (or
152
+ * globally if called outside a `describe`). Multiple calls are chained in
153
+ * registration order.
154
+ *
155
+ * @param fn - The setup function to run before each test.
156
+ */
157
+ export function beforeEach(fn) {
158
+ let target = currentSuite ?? rootHooks;
159
+ target.beforeEach = chainBefore(target.beforeEach, fn);
160
+ }
161
+ /**
162
+ * Registers a hook that runs after **each** test in the current suite (or
163
+ * globally if called outside a `describe`). Multiple calls are chained in
164
+ * reverse registration order. To run logic after a singular test, use
165
+ * `t.after()` from the {@link TestContext}
166
+ *
167
+ * @param fn - The teardown function to run after each test.
168
+ */
169
+ export function afterEach(fn) {
170
+ let target = currentSuite ?? rootHooks;
171
+ target.afterEach = chainAfter(target.afterEach, fn);
172
+ }
173
+ /**
174
+ * Registers a hook that runs once before **all** tests in the current suite
175
+ * (or globally if called outside a `describe`). Multiple calls are chained in
176
+ * registration order.
177
+ *
178
+ * @param fn - The setup function to run once before all tests in the suite.
179
+ */
180
+ export function beforeAll(fn) {
181
+ let target = currentSuite ?? rootHooks;
182
+ target.beforeAll = chainBefore(target.beforeAll, fn);
183
+ }
184
+ /**
185
+ * Registers a hook that runs once after **all** tests in the current suite (or
186
+ * globally if called outside a `describe`). Multiple calls are chained in
187
+ * reverse registration order.
188
+ *
189
+ * @param fn - The teardown function to run once after all tests in the suite.
190
+ */
191
+ export function afterAll(fn) {
192
+ let target = currentSuite ?? rootHooks;
193
+ target.afterAll = chainAfter(target.afterAll, fn);
194
+ }
195
+ /** Alias for {@link beforeAll} — matches the `node:test` API. */
196
+ export const before = beforeAll;
197
+ /** Alias for {@link afterAll} — matches the `node:test` API. */
198
+ export const after = afterAll;
@@ -0,0 +1,2 @@
1
+ export declare function importModule(specifier: string, meta: ImportMeta): Promise<any>;
2
+ //# sourceMappingURL=import-module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-module.d.ts","sourceRoot":"","sources":["../../src/lib/import-module.ts"],"names":[],"mappings":"AAkBA,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,CAUpF"}
@@ -0,0 +1,29 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { tsImport } from 'tsx/esm/api';
10
+ import { IS_BUN } from "./runtime.js";
11
+ function hasImportMetaResolve(meta) {
12
+ return 'resolve' in meta && typeof meta.resolve === 'function';
13
+ }
14
+ /*
15
+ * Loads a module specifier relative to the caller's module context.
16
+ *
17
+ * @param specifier The module specifier or file path to load.
18
+ * @param meta The caller's `import.meta`, used as the context for resolution.
19
+ * @returns The imported module namespace.
20
+ */
21
+ export async function importModule(specifier, meta) {
22
+ if (IS_BUN) {
23
+ if (!hasImportMetaResolve(meta)) {
24
+ throw new Error('importModule() requires import.meta.resolve() in Bun');
25
+ }
26
+ return import(__rewriteRelativeImportExtension(meta.resolve(specifier, meta.url)));
27
+ }
28
+ return tsImport(specifier, meta.url);
29
+ }
@@ -0,0 +1,52 @@
1
+ /** Records the arguments, return value, and any thrown error for a single call. */
2
+ export interface MockCall<Args extends unknown[] = unknown[], Result = unknown> {
3
+ arguments: Args;
4
+ result?: Result;
5
+ error?: unknown;
6
+ }
7
+ /**
8
+ * Metadata attached to every mock/spy function via its `.mock` property.
9
+ * `restore` is present on spies and reverts the original method when called.
10
+ */
11
+ export interface MockContext<Args extends unknown[] = unknown[], Result = unknown> {
12
+ calls: MockCall<Args, Result>[];
13
+ restore?: () => void;
14
+ }
15
+ /** A function augmented with a `.mock` property for inspecting recorded calls. */
16
+ export type MockFunction<T extends (...args: any[]) => any = (...args: any[]) => any> = T & {
17
+ mock: MockContext<Parameters<T>, ReturnType<T>>;
18
+ };
19
+ declare function createMockFn<T extends (...args: any[]) => any>(impl?: T): MockFunction<T>;
20
+ declare function createMethodMock<T extends object, K extends keyof T>(obj: T, method: K, impl?: T[K] extends (...args: any[]) => any ? (...args: Parameters<T[K]>) => any : never): MockFunction;
21
+ /**
22
+ * Utilities for creating mock functions and method spies. Mirrors the names
23
+ * on Node.js's built-in `MockTracker` from `node:test`.
24
+ *
25
+ * @example
26
+ * // Standalone mock
27
+ * const fn = mock.fn((x: number) => x * 2)
28
+ * fn(3)
29
+ * assert.equal(fn.mock.calls[0].result, 6)
30
+ *
31
+ * // Mock an existing method
32
+ * const spy = mock.method(console, 'log')
33
+ * console.log('hello')
34
+ * assert.equal(spy.mock.calls.length, 1)
35
+ * spy.mock.restore?.()
36
+ */
37
+ export declare const mock: {
38
+ /**
39
+ * Creates a mock function that records every call. If `impl` is provided it
40
+ * is used as the underlying implementation; otherwise the mock returns
41
+ * `undefined`.
42
+ */
43
+ fn: typeof createMockFn;
44
+ /**
45
+ * Replaces `obj[methodName]` with a mock and records every call. The
46
+ * original method is used as the implementation unless `impl` is provided.
47
+ * Call `mockFn.mock.restore()` to revert.
48
+ */
49
+ method: typeof createMethodMock;
50
+ };
51
+ export {};
52
+ //# sourceMappingURL=mock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../../src/lib/mock.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,MAAM,WAAW,QAAQ,CAAC,IAAI,SAAS,OAAO,EAAE,GAAG,OAAO,EAAE,EAAE,MAAM,GAAG,OAAO;IAC5E,SAAS,EAAE,IAAI,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW,CAAC,IAAI,SAAS,OAAO,EAAE,GAAG,OAAO,EAAE,EAAE,MAAM,GAAG,OAAO;IAC/E,KAAK,EAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAA;IAC/B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACrB;AAED,kFAAkF;AAClF,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,GAAG,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,IAAI,CAAC,GAAG;IAC1F,IAAI,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAA;CAChD,CAAA;AAED,iBAAS,YAAY,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAqBlF;AAED,iBAAS,gBAAgB,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,EAC3D,GAAG,EAAE,CAAC,EACN,MAAM,EAAE,CAAC,EACT,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,GAAG,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,GAAG,KAAK,GACvF,YAAY,CASd;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,IAAI;IACf;;;;OAIG;;IAEH;;;;OAIG;;CAEJ,CAAA"}
@@ -0,0 +1,61 @@
1
+ function createMockFn(impl) {
2
+ let calls = [];
3
+ let fn = function (...args) {
4
+ let call = { arguments: args };
5
+ calls.push(call);
6
+ if (impl) {
7
+ try {
8
+ let result = impl.apply(this, args);
9
+ call.result = result;
10
+ return result;
11
+ }
12
+ catch (error) {
13
+ call.error = error;
14
+ throw error;
15
+ }
16
+ }
17
+ return undefined;
18
+ };
19
+ fn.mock = { calls };
20
+ return fn;
21
+ }
22
+ function createMethodMock(obj, method, impl) {
23
+ let original = obj[method];
24
+ let effectiveImpl = (impl ?? original);
25
+ let mockFn = createMockFn(effectiveImpl);
26
+ obj[method] = mockFn;
27
+ mockFn.mock.restore = () => {
28
+ obj[method] = original;
29
+ };
30
+ return mockFn;
31
+ }
32
+ /**
33
+ * Utilities for creating mock functions and method spies. Mirrors the names
34
+ * on Node.js's built-in `MockTracker` from `node:test`.
35
+ *
36
+ * @example
37
+ * // Standalone mock
38
+ * const fn = mock.fn((x: number) => x * 2)
39
+ * fn(3)
40
+ * assert.equal(fn.mock.calls[0].result, 6)
41
+ *
42
+ * // Mock an existing method
43
+ * const spy = mock.method(console, 'log')
44
+ * console.log('hello')
45
+ * assert.equal(spy.mock.calls.length, 1)
46
+ * spy.mock.restore?.()
47
+ */
48
+ export const mock = {
49
+ /**
50
+ * Creates a mock function that records every call. If `impl` is provided it
51
+ * is used as the underlying implementation; otherwise the mock returns
52
+ * `undefined`.
53
+ */
54
+ fn: createMockFn,
55
+ /**
56
+ * Replaces `obj[methodName]` with a mock and records every call. The
57
+ * original method is used as the implementation unless `impl` is provided.
58
+ * Call `mockFn.mock.restore()` to revert.
59
+ */
60
+ method: createMethodMock,
61
+ };
@@ -0,0 +1,2 @@
1
+ export declare function normalizeLine(line: string): string;
2
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/lib/normalize.ts"],"names":[],"mappings":"AAYA,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASlD"}
@@ -0,0 +1,18 @@
1
+ function normalizeFilePath(path) {
2
+ let locSuffix = path.match(/(:\d+:\d+)$/)?.[0] || '';
3
+ let normalized = path
4
+ .replace(/^\/scripts\/@pkg\/([^):]+)/g, (...args) => args[1])
5
+ .replace(/^\/scripts\/@test\/([^):]+)/g, (...args) => args[1])
6
+ .replace(/^\/scripts\/([^):]+)/g, (...args) => args[1])
7
+ .replace(/^\s+/, ' ') + locSuffix;
8
+ return path.includes('/@test/') ? `./${normalized}` : normalized;
9
+ }
10
+ export function normalizeLine(line) {
11
+ let match = line.match(/ \(.*\)$/);
12
+ if (match) {
13
+ let filepath = match[0].slice(2, -1);
14
+ filepath = filepath.replace(/https?:\/\/localhost:\d+\//g, '/');
15
+ return line.slice(0, match.index) + ' (' + normalizeFilePath(filepath) + ')';
16
+ }
17
+ return line;
18
+ }
@@ -0,0 +1,15 @@
1
+ import type { BrowserContextOptions, LaunchOptions } from 'playwright';
2
+ import type { PlaywrightTestConfig } from 'playwright/test';
3
+ export type PlaywrightUseOpts = PlaywrightTestConfig['use'];
4
+ export declare function loadPlaywrightConfig(input: string | undefined, cwd?: string): Promise<PlaywrightTestConfig | undefined>;
5
+ export declare function getBrowserLauncher(playwrightUseOpts?: PlaywrightUseOpts): import("playwright").BrowserType<{}>;
6
+ export declare function resolveProjects(config?: PlaywrightTestConfig): Array<{
7
+ name?: string;
8
+ playwrightUseOpts: PlaywrightUseOpts;
9
+ }>;
10
+ export declare function getPlaywrightLaunchOptions(playwrightUseOpts?: PlaywrightUseOpts): LaunchOptions;
11
+ export declare function getPlaywrightPageOptions(playwrightUseOpts?: PlaywrightUseOpts): BrowserContextOptions & {
12
+ navigationTimeout?: number;
13
+ actionTimeout?: number;
14
+ };
15
+ //# sourceMappingURL=playwright.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playwright.d.ts","sourceRoot":"","sources":["../../src/lib/playwright.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AACtE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAG3D,MAAM,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAA;AAE3D,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,GAAG,SAAgB,GAClB,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAc3C;AAQD,wBAAgB,kBAAkB,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,wCAavE;AAED,wBAAgB,eAAe,CAC7B,MAAM,CAAC,EAAE,oBAAoB,GAC5B,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,iBAAiB,CAAA;CAAE,CAAC,CAahE;AAED,wBAAgB,0BAA0B,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,aAAa,CAK/F;AAED,wBAAgB,wBAAwB,CACtC,iBAAiB,CAAC,EAAE,iBAAiB,GACpC,qBAAqB,GAAG;IAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,CAwBhF"}
@@ -0,0 +1,81 @@
1
+ import * as path from 'node:path';
2
+ import * as fs from 'node:fs/promises';
3
+ import { chromium, firefox, webkit } from 'playwright';
4
+ import { importModule } from "./import-module.js";
5
+ export async function loadPlaywrightConfig(input, cwd = process.cwd()) {
6
+ let candidates = input
7
+ ? [path.resolve(cwd, input)]
8
+ : [path.join(cwd, 'playwright.config.ts'), path.join(cwd, 'playwright.config.js')];
9
+ for (let configPath of candidates) {
10
+ try {
11
+ await fs.access(configPath);
12
+ let mod = await importModule(configPath, import.meta);
13
+ return mod.default ?? mod;
14
+ }
15
+ catch {
16
+ // not found or failed to load — try next
17
+ }
18
+ }
19
+ }
20
+ const launchers = {
21
+ chromium,
22
+ firefox,
23
+ webkit,
24
+ };
25
+ export function getBrowserLauncher(playwrightUseOpts) {
26
+ if (playwrightUseOpts?.browserName) {
27
+ let launcher = launchers[playwrightUseOpts.browserName];
28
+ if (!launcher) {
29
+ let supportedBrowsers = Object.keys(launchers).join(', ');
30
+ throw new Error(`Unsupported browser "${playwrightUseOpts.browserName}". ` +
31
+ `Supported browsers are: ${supportedBrowsers}`);
32
+ }
33
+ return launcher;
34
+ }
35
+ return chromium;
36
+ }
37
+ export function resolveProjects(config) {
38
+ if (config?.projects?.length) {
39
+ return config.projects.map((p) => ({
40
+ name: p.name,
41
+ playwrightUseOpts: { ...config.use, ...p.use },
42
+ }));
43
+ }
44
+ return [
45
+ {
46
+ name: 'chromium',
47
+ playwrightUseOpts: config?.use,
48
+ },
49
+ ];
50
+ }
51
+ export function getPlaywrightLaunchOptions(playwrightUseOpts) {
52
+ return {
53
+ headless: playwrightUseOpts?.headless,
54
+ channel: playwrightUseOpts?.channel,
55
+ };
56
+ }
57
+ export function getPlaywrightPageOptions(playwrightUseOpts) {
58
+ return {
59
+ // Context options passed to browser.newPage()
60
+ bypassCSP: playwrightUseOpts?.bypassCSP,
61
+ colorScheme: playwrightUseOpts?.colorScheme,
62
+ deviceScaleFactor: playwrightUseOpts?.deviceScaleFactor,
63
+ extraHTTPHeaders: playwrightUseOpts?.extraHTTPHeaders,
64
+ geolocation: playwrightUseOpts?.geolocation,
65
+ hasTouch: playwrightUseOpts?.hasTouch,
66
+ httpCredentials: playwrightUseOpts?.httpCredentials,
67
+ ignoreHTTPSErrors: playwrightUseOpts?.ignoreHTTPSErrors,
68
+ isMobile: playwrightUseOpts?.isMobile,
69
+ javaScriptEnabled: playwrightUseOpts?.javaScriptEnabled,
70
+ locale: playwrightUseOpts?.locale,
71
+ offline: playwrightUseOpts?.offline,
72
+ permissions: playwrightUseOpts?.permissions,
73
+ storageState: playwrightUseOpts?.storageState,
74
+ timezoneId: playwrightUseOpts?.timezoneId,
75
+ userAgent: playwrightUseOpts?.userAgent,
76
+ viewport: playwrightUseOpts?.viewport,
77
+ // Additional options set on the page instance
78
+ navigationTimeout: playwrightUseOpts?.navigationTimeout,
79
+ actionTimeout: playwrightUseOpts?.actionTimeout,
80
+ };
81
+ }
@@ -0,0 +1,9 @@
1
+ import type { Reporter } from './index.ts';
2
+ import type { Counts, TestResults } from './results.ts';
3
+ export declare class DotReporter implements Reporter {
4
+ #private;
5
+ onSectionStart(_label: string): void;
6
+ onResult(results: TestResults, _env?: string): void;
7
+ onSummary(counts: Counts, durationMs: number): void;
8
+ }
9
+ //# sourceMappingURL=dot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dot.d.ts","sourceRoot":"","sources":["../../../src/lib/reporters/dot.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,KAAK,EAAE,MAAM,EAAc,WAAW,EAAE,MAAM,cAAc,CAAA;AAEnE,qBAAa,WAAY,YAAW,QAAQ;;IAI1C,cAAc,CAAC,MAAM,EAAE,MAAM,QAAI;IAEjC,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,MAAM,QAc3C;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QA6B3C;CACF"}
@@ -0,0 +1,56 @@
1
+ import { colors } from "../colors.js";
2
+ import { normalizeLine } from "../normalize.js";
3
+ export class DotReporter {
4
+ #failures = [];
5
+ #dotCount = 0;
6
+ onSectionStart(_label) { }
7
+ onResult(results, _env) {
8
+ for (let test of results.tests) {
9
+ if (test.status === 'passed') {
10
+ process.stdout.write(colors.green('.'));
11
+ }
12
+ else if (test.status === 'skipped') {
13
+ process.stdout.write(colors.dim('S'));
14
+ }
15
+ else if (test.status === 'todo') {
16
+ process.stdout.write(colors.dim('T'));
17
+ }
18
+ else {
19
+ process.stdout.write(colors.red('F'));
20
+ this.#failures.push({ name: `${test.suiteName} > ${test.name}`, error: test.error });
21
+ }
22
+ this.#dotCount++;
23
+ }
24
+ }
25
+ onSummary(counts, durationMs) {
26
+ if (this.#dotCount > 0)
27
+ console.log();
28
+ for (let i = 0; i < this.#failures.length; i++) {
29
+ let { name, error } = this.#failures[i];
30
+ console.log(`\n ${colors.red(`${i + 1})`)} ${name}`);
31
+ if (error) {
32
+ console.log(` ${colors.red(error.message)}`);
33
+ if (error.stack) {
34
+ let frames = error.stack
35
+ .split('\n')
36
+ .slice(1, 4)
37
+ .map((l) => ` ${normalizeLine(l).trim()}`)
38
+ .join('\n');
39
+ console.log(frames);
40
+ }
41
+ }
42
+ }
43
+ let { passed, failed, skipped, todo } = counts;
44
+ let info = colors.cyan('ℹ');
45
+ console.log();
46
+ console.log(`${info} tests ${passed + failed + skipped + todo}`);
47
+ console.log(`${info} pass ${passed}`);
48
+ console.log(`${info} fail ${failed}`);
49
+ if (skipped > 0)
50
+ console.log(`${info} skipped ${skipped}`);
51
+ if (todo > 0)
52
+ console.log(`${info} todo ${todo}`);
53
+ console.log(`${info} duration_ms ${durationMs.toFixed(5)}`);
54
+ console.log();
55
+ }
56
+ }
@@ -0,0 +1,9 @@
1
+ import type { Reporter } from './index.ts';
2
+ import type { Counts, TestResults } from './results.ts';
3
+ export declare class FilesReporter implements Reporter {
4
+ #private;
5
+ onSectionStart(_label: string): void;
6
+ onResult(results: TestResults, env?: string): void;
7
+ onSummary(counts: Counts, durationMs: number): void;
8
+ }
9
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../../src/lib/reporters/files.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,KAAK,EAAE,MAAM,EAAc,WAAW,EAAE,MAAM,cAAc,CAAA;AAEnE,qBAAa,aAAc,YAAW,QAAQ;;IAG5C,cAAc,CAAC,MAAM,EAAE,MAAM,QAAI;IAEjC,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE,MAAM,QA8B1C;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAgC3C;CACF"}
@@ -0,0 +1,71 @@
1
+ import * as path from 'node:path';
2
+ import { colors } from "../colors.js";
3
+ import { normalizeLine } from "../normalize.js";
4
+ export class FilesReporter {
5
+ #failures = [];
6
+ onSectionStart(_label) { }
7
+ onResult(results, env) {
8
+ let filePath = results.tests[0]?.filePath;
9
+ let fileName = filePath ? path.relative(process.cwd(), filePath) : '(unknown)';
10
+ let envLabel = env ? ` ${colors.dim(`[${env}]`)}` : '';
11
+ let totalDuration = results.tests.reduce((sum, t) => sum + t.duration, 0);
12
+ let hasFailed = results.tests.some((t) => t.status === 'failed');
13
+ let fileColor = hasFailed ? colors.red : colors.green;
14
+ let duration = hasFailed ? '' : ` (${totalDuration.toFixed(2)}ms)`;
15
+ console.log(`${colors.dim('▶')} ${fileColor(fileName)}${duration}${envLabel}`);
16
+ if (hasFailed) {
17
+ // Print failing tests with suite/test nesting using > separators
18
+ for (let test of results.tests) {
19
+ if (test.status !== 'failed')
20
+ continue;
21
+ let fullName = test.name ? `${test.suiteName} > ${test.name}` : test.suiteName;
22
+ console.log(` ${colors.red('✗')} ${fullName}`);
23
+ if (test.error) {
24
+ console.log(` ${colors.red(`Error: ${test.error.message}`)}`);
25
+ if (test.error.stack) {
26
+ let stack = test.error.stack
27
+ .split('\n')
28
+ .map((line) => normalizeLine(line))
29
+ .join('\n');
30
+ console.log(` ${stack.split('\n').slice(1, 5).join(`\n `)}`);
31
+ }
32
+ }
33
+ this.#failures.push({ suiteName: test.suiteName, name: test.name, error: test.error });
34
+ }
35
+ }
36
+ }
37
+ onSummary(counts, durationMs) {
38
+ if (this.#failures.length > 0) {
39
+ console.log();
40
+ console.log(colors.red('Failed tests:'));
41
+ for (let i = 0; i < this.#failures.length; i++) {
42
+ let { suiteName, name, error } = this.#failures[i];
43
+ let fullName = name ? `${suiteName} > ${name}` : suiteName;
44
+ console.log(`\n ${colors.red(`${i + 1})`)} ${fullName}`);
45
+ if (error) {
46
+ console.log(` ${colors.red(error.message)}`);
47
+ if (error.stack) {
48
+ let frames = error.stack
49
+ .split('\n')
50
+ .slice(1, 4)
51
+ .map((l) => ` ${normalizeLine(l).trim()}`)
52
+ .join('\n');
53
+ console.log(frames);
54
+ }
55
+ }
56
+ }
57
+ }
58
+ let { passed, failed, skipped, todo } = counts;
59
+ let info = colors.cyan('ℹ');
60
+ console.log();
61
+ console.log(`${info} tests ${passed + failed + skipped + todo}`);
62
+ console.log(`${info} pass ${passed}`);
63
+ console.log(`${info} fail ${failed}`);
64
+ if (skipped > 0)
65
+ console.log(`${info} skipped ${skipped}`);
66
+ if (todo > 0)
67
+ console.log(`${info} todo ${todo}`);
68
+ console.log(`${info} duration_ms ${durationMs.toFixed(5)}`);
69
+ console.log();
70
+ }
71
+ }
@@ -0,0 +1,13 @@
1
+ import { DotReporter } from './dot.ts';
2
+ import { FilesReporter } from './files.ts';
3
+ import type { Counts, TestResults } from './results.ts';
4
+ import { SpecReporter } from './spec.ts';
5
+ import { TapReporter } from './tap.ts';
6
+ export interface Reporter {
7
+ onResult(results: TestResults, env?: string): void;
8
+ onSummary(counts: Counts, durationMs: number): void;
9
+ onSectionStart(label: string): void;
10
+ }
11
+ export { DotReporter, FilesReporter, SpecReporter, TapReporter };
12
+ export declare function createReporter(type: string): Reporter;
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/reporters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAEtC,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAClD,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACnD,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACpC;AAED,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,CAAA;AAEhE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAYrD"}