@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.
- package/LICENSE +21 -0
- package/README.md +430 -2
- package/dist/app/client/entry.d.ts +2 -0
- package/dist/app/client/entry.d.ts.map +1 -0
- package/dist/app/client/entry.js +324 -0
- package/dist/app/client/iframe.d.ts +2 -0
- package/dist/app/client/iframe.d.ts.map +1 -0
- package/dist/app/client/iframe.js +22 -0
- package/dist/app/server.d.ts +6 -0
- package/dist/app/server.d.ts.map +1 -0
- package/dist/app/server.js +303 -0
- package/dist/cli-entry.d.ts +3 -0
- package/dist/cli-entry.d.ts.map +1 -0
- package/dist/cli-entry.js +14 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +305 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/lib/colors.d.ts +2 -0
- package/dist/lib/colors.d.ts.map +1 -0
- package/dist/lib/colors.js +2 -0
- package/dist/lib/config.d.ts +91 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +255 -0
- package/dist/lib/context.d.ts +93 -0
- package/dist/lib/context.d.ts.map +1 -0
- package/dist/lib/context.js +65 -0
- package/dist/lib/coverage-loader.d.ts +16 -0
- package/dist/lib/coverage-loader.d.ts.map +1 -0
- package/dist/lib/coverage-loader.js +20 -0
- package/dist/lib/coverage.d.ts +28 -0
- package/dist/lib/coverage.d.ts.map +1 -0
- package/dist/lib/coverage.js +212 -0
- package/dist/lib/executor.d.ts +4 -0
- package/dist/lib/executor.d.ts.map +1 -0
- package/dist/lib/executor.js +128 -0
- package/dist/lib/fake-timers.d.ts +6 -0
- package/dist/lib/fake-timers.d.ts.map +1 -0
- package/dist/lib/fake-timers.js +45 -0
- package/dist/lib/framework.d.ts +107 -0
- package/dist/lib/framework.d.ts.map +1 -0
- package/dist/lib/framework.js +198 -0
- package/dist/lib/import-module.d.ts +2 -0
- package/dist/lib/import-module.d.ts.map +1 -0
- package/dist/lib/import-module.js +29 -0
- package/dist/lib/mock.d.ts +52 -0
- package/dist/lib/mock.d.ts.map +1 -0
- package/dist/lib/mock.js +61 -0
- package/dist/lib/normalize.d.ts +2 -0
- package/dist/lib/normalize.d.ts.map +1 -0
- package/dist/lib/normalize.js +18 -0
- package/dist/lib/playwright.d.ts +15 -0
- package/dist/lib/playwright.d.ts.map +1 -0
- package/dist/lib/playwright.js +81 -0
- package/dist/lib/reporters/dot.d.ts +9 -0
- package/dist/lib/reporters/dot.d.ts.map +1 -0
- package/dist/lib/reporters/dot.js +56 -0
- package/dist/lib/reporters/files.d.ts +9 -0
- package/dist/lib/reporters/files.d.ts.map +1 -0
- package/dist/lib/reporters/files.js +71 -0
- package/dist/lib/reporters/index.d.ts +13 -0
- package/dist/lib/reporters/index.d.ts.map +1 -0
- package/dist/lib/reporters/index.js +18 -0
- package/dist/lib/reporters/results.d.ts +30 -0
- package/dist/lib/reporters/results.d.ts.map +1 -0
- package/dist/lib/reporters/results.js +1 -0
- package/dist/lib/reporters/spec.d.ts +9 -0
- package/dist/lib/reporters/spec.d.ts.map +1 -0
- package/dist/lib/reporters/spec.js +153 -0
- package/dist/lib/reporters/tap.d.ts +9 -0
- package/dist/lib/reporters/tap.d.ts.map +1 -0
- package/dist/lib/reporters/tap.js +54 -0
- package/dist/lib/runner-browser.d.ts +21 -0
- package/dist/lib/runner-browser.d.ts.map +1 -0
- package/dist/lib/runner-browser.js +117 -0
- package/dist/lib/runner.d.ts +14 -0
- package/dist/lib/runner.d.ts.map +1 -0
- package/dist/lib/runner.js +118 -0
- package/dist/lib/runtime.d.ts +2 -0
- package/dist/lib/runtime.d.ts.map +1 -0
- package/dist/lib/runtime.js +2 -0
- package/dist/lib/ts-transform.d.ts +4 -0
- package/dist/lib/ts-transform.d.ts.map +1 -0
- package/dist/lib/ts-transform.js +29 -0
- package/dist/lib/watcher.d.ts +5 -0
- package/dist/lib/watcher.d.ts.map +1 -0
- package/dist/lib/watcher.js +39 -0
- package/dist/lib/worker-e2e.d.ts +2 -0
- package/dist/lib/worker-e2e.d.ts.map +1 -0
- package/dist/lib/worker-e2e.js +49 -0
- package/dist/lib/worker.d.ts +2 -0
- package/dist/lib/worker.d.ts.map +1 -0
- package/dist/lib/worker.js +57 -0
- package/dist/test/coverage/fixture.d.ts +5 -0
- package/dist/test/coverage/fixture.d.ts.map +1 -0
- package/dist/test/coverage/fixture.js +32 -0
- package/dist/test/coverage/test-browser.d.ts +2 -0
- package/dist/test/coverage/test-browser.d.ts.map +1 -0
- package/dist/test/coverage/test-browser.js +24 -0
- package/dist/test/coverage/test-e2e.d.ts +2 -0
- package/dist/test/coverage/test-e2e.d.ts.map +1 -0
- package/dist/test/coverage/test-e2e.js +60 -0
- package/dist/test/coverage/test-unit.d.ts +2 -0
- package/dist/test/coverage/test-unit.d.ts.map +1 -0
- package/dist/test/coverage/test-unit.js +27 -0
- package/dist/test/framework.test.browser.d.ts +2 -0
- package/dist/test/framework.test.browser.d.ts.map +1 -0
- package/dist/test/framework.test.browser.js +107 -0
- package/dist/test/framework.test.e2e.d.ts +2 -0
- package/dist/test/framework.test.e2e.d.ts.map +1 -0
- package/dist/test/framework.test.e2e.js +34 -0
- package/package.json +79 -5
- package/src/app/client/entry.ts +353 -0
- package/src/app/client/iframe.ts +18 -0
- package/src/app/server.ts +336 -0
- package/src/cli-entry.ts +15 -0
- package/src/cli.ts +384 -0
- package/src/index.ts +16 -0
- package/src/lib/colors.ts +3 -0
- package/src/lib/config.ts +377 -0
- package/src/lib/context.ts +168 -0
- package/src/lib/coverage-loader.ts +31 -0
- package/src/lib/coverage.ts +320 -0
- package/src/lib/executor.ts +145 -0
- package/src/lib/fake-timers.ts +64 -0
- package/src/lib/framework.ts +251 -0
- package/src/lib/import-module.ts +29 -0
- package/src/lib/mock.ts +89 -0
- package/src/lib/normalize.ts +22 -0
- package/src/lib/playwright.ts +100 -0
- package/src/lib/reporters/dot.ts +58 -0
- package/src/lib/reporters/files.ts +77 -0
- package/src/lib/reporters/index.ts +27 -0
- package/src/lib/reporters/results.ts +29 -0
- package/src/lib/reporters/spec.ts +174 -0
- package/src/lib/reporters/tap.ts +58 -0
- package/src/lib/runner-browser.ts +165 -0
- package/src/lib/runner.ts +189 -0
- package/src/lib/runtime.ts +2 -0
- package/src/lib/ts-transform.ts +36 -0
- package/src/lib/watcher.ts +46 -0
- package/src/lib/worker-e2e.ts +54 -0
- package/src/lib/worker.ts +50 -0
- package/src/test/coverage/fixture.ts +34 -0
- package/src/test/coverage/test-browser.ts +29 -0
- package/src/test/coverage/test-e2e.ts +70 -0
- package/src/test/coverage/test-unit.ts +32 -0
- 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 @@
|
|
|
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"}
|
package/dist/lib/mock.js
ADDED
|
@@ -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 @@
|
|
|
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"}
|