@aljazmc/testero 0.3.3 → 0.4.1

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/bin/main.js CHANGED
@@ -1,1652 +1,1751 @@
1
1
  #!/usr/bin/env node
2
-
3
- // shared/config/src/config.ts
4
- import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
5
- import { join } from "node:path";
6
- import { pathToFileURL } from "node:url";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
5
+ var __esm = (fn, res, err) => function __init() {
6
+ if (err) throw err[0];
7
+ try {
8
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
9
+ } catch (e) {
10
+ throw err = [e], e;
11
+ }
12
+ };
7
13
 
8
14
  // shared/config/src/pattern.ts
9
- var Pattern = class _Pattern {
10
- #anchored;
11
- #regex;
12
- constructor(source) {
13
- const glob = source.replace(/^\.?\/+|\/+$/g, "");
14
- this.#anchored = glob.includes("/");
15
- this.#regex = new RegExp(`^${_Pattern.#toRegex(glob)}$`);
16
- }
17
- matches(path) {
18
- const segments = path.split("/");
19
- return segments.some(
20
- (segment, index) => this.#regex.test(
21
- this.#anchored ? segments.slice(0, index + 1).join("/") : segment
22
- )
23
- );
24
- }
25
- /** Translates `**`, `*`, `?` and `{a,b}` alternatives, other characters match themselves. */
26
- static #toRegex(glob) {
27
- const tokens = {
28
- "**/": "(?:.*/)?",
29
- "**": ".*",
30
- "*": "[^/]*",
31
- "?": "[^/]"
15
+ var Pattern;
16
+ var init_pattern = __esm({
17
+ "shared/config/src/pattern.ts"() {
18
+ "use strict";
19
+ Pattern = class _Pattern {
20
+ static {
21
+ __name(this, "Pattern");
22
+ }
23
+ #anchored;
24
+ #regex;
25
+ constructor(source) {
26
+ const glob = source.replace(/^\.?\/+|\/+$/g, "");
27
+ this.#anchored = glob.includes("/");
28
+ this.#regex = new RegExp(`^${_Pattern.#toRegex(glob)}$`);
29
+ }
30
+ matches(path) {
31
+ const segments = path.split("/");
32
+ return segments.some(
33
+ (segment, index) => this.#regex.test(
34
+ this.#anchored ? segments.slice(0, index + 1).join("/") : segment
35
+ )
36
+ );
37
+ }
38
+ /** Translates `**`, `*`, `?` and `{a,b}` alternatives, other characters match themselves. */
39
+ static #toRegex(glob) {
40
+ const tokens = {
41
+ "**/": "(?:.*/)?",
42
+ "**": ".*",
43
+ "*": "[^/]*",
44
+ "?": "[^/]"
45
+ };
46
+ let regex = "";
47
+ let braces = 0;
48
+ for (const [token] of glob.matchAll(/\*\*\/|\*\*|[\s\S]/g)) {
49
+ if (token in tokens) regex += tokens[token] ?? token;
50
+ else if (token === "{") {
51
+ braces++;
52
+ regex += "(?:";
53
+ } else if (token === "}" && braces > 0) {
54
+ braces--;
55
+ regex += ")";
56
+ } else if (token === "," && braces > 0) regex += "|";
57
+ else regex += token.replace(/[.+^${}()|[\]\\]/g, "\\$&");
58
+ }
59
+ return regex;
60
+ }
32
61
  };
33
- let regex = "";
34
- let braces = 0;
35
- for (const [token] of glob.matchAll(/\*\*\/|\*\*|[\s\S]/g)) {
36
- if (token in tokens) regex += tokens[token] ?? token;
37
- else if (token === "{") {
38
- braces++;
39
- regex += "(?:";
40
- } else if (token === "}" && braces > 0) {
41
- braces--;
42
- regex += ")";
43
- } else if (token === "," && braces > 0) regex += "|";
44
- else regex += token.replace(/[.+^${}()|[\]\\]/g, "\\$&");
45
- }
46
- return regex;
47
62
  }
48
- };
63
+ });
49
64
 
50
65
  // shared/config/src/ignore.ts
51
- var Ignore = class {
52
- #patterns;
53
- constructor(sources) {
54
- this.#patterns = sources.map((source) => new Pattern(source));
55
- }
56
- matches(path) {
57
- const normalized = path.replaceAll("\\", "/").replace(/^\.\//, "");
58
- return this.#patterns.some((pattern) => pattern.matches(normalized));
66
+ var Ignore;
67
+ var init_ignore = __esm({
68
+ "shared/config/src/ignore.ts"() {
69
+ "use strict";
70
+ init_pattern();
71
+ Ignore = class {
72
+ static {
73
+ __name(this, "Ignore");
74
+ }
75
+ #patterns;
76
+ constructor(sources) {
77
+ this.#patterns = sources.map((source) => new Pattern(source));
78
+ }
79
+ matches(path) {
80
+ const normalized = path.replaceAll("\\", "/").replace(/^\.\//, "");
81
+ return this.#patterns.some((pattern) => pattern.matches(normalized));
82
+ }
83
+ };
59
84
  }
60
- };
85
+ });
61
86
 
62
87
  // shared/config/src/config.ts
63
- var Config = class _Config {
64
- static extensions = ["ts", "js"];
65
- root;
66
- ignore;
67
- rules;
68
- /** What the config file set, before the defaults were merged in. */
69
- custom;
70
- constructor(options, root = process.cwd(), custom = {}) {
71
- this.root = root;
72
- this.ignore = new Ignore(options.ignore);
73
- this.rules = options.rules;
74
- this.custom = custom;
75
- }
76
- static async load(name, defaults, root = process.cwd()) {
77
- for (const extension of _Config.extensions) {
78
- const file = join(root, `${name}.config.${extension}`);
79
- if (existsSync(file)) {
80
- const { default: options } = await import(pathToFileURL(file).href);
81
- return new _Config(
82
- _Config.merge(defaults, options),
83
- root,
84
- options
85
- );
88
+ import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
89
+ import { join } from "node:path";
90
+ import { pathToFileURL } from "node:url";
91
+ var Config;
92
+ var init_config = __esm({
93
+ "shared/config/src/config.ts"() {
94
+ "use strict";
95
+ init_ignore();
96
+ Config = class _Config {
97
+ static {
98
+ __name(this, "Config");
86
99
  }
87
- }
88
- return new _Config(defaults, root);
89
- }
90
- /** Handles `--config`, writing the defaults to a TypeScript config file, or with `--config js` a JavaScript one. Returns whether it was given. */
91
- static init(name, defaults, argv = process.argv.slice(2), root = process.cwd()) {
92
- const index = argv.indexOf("--config");
93
- if (index === -1) return false;
94
- const next = argv[index + 1];
95
- const extension = next === void 0 || next.startsWith("-") ? "ts" : next;
96
- try {
97
- console.log(
98
- `created: ${_Config.export(name, defaults, extension, root)}`
99
- );
100
- } catch (error) {
101
- console.error(error instanceof Error ? error.message : error);
102
- process.exitCode = 1;
103
- }
104
- return true;
105
- }
106
- /** Writes the defaults to `<name>.config.<extension>` in the root, unless a config file is there already. */
107
- static export(name, defaults, extension = "ts", root = process.cwd()) {
108
- if (!_Config.extensions.includes(extension))
109
- throw new Error(
110
- `Unknown config file type ${extension}, use ts or js.`
111
- );
112
- for (const existing of _Config.extensions) {
113
- const file2 = `${name}.config.${existing}`;
114
- if (existsSync(join(root, file2)))
115
- throw new Error(`${file2} already exists.`);
116
- }
117
- const file = `${name}.config.${extension}`;
118
- writeFileSync(
119
- join(root, file),
120
- `export default ${_Config.#literal(defaults)};
100
+ static extensions = ["ts", "js"];
101
+ root;
102
+ ignore;
103
+ rules;
104
+ /** What the config file set, before the defaults were merged in. */
105
+ custom;
106
+ constructor(options, root = process.cwd(), custom = {}) {
107
+ this.root = root;
108
+ this.ignore = new Ignore(options.ignore);
109
+ this.rules = options.rules;
110
+ this.custom = custom;
111
+ }
112
+ static async load(name, defaults, root = process.cwd()) {
113
+ for (const extension of _Config.extensions) {
114
+ const file = join(root, `${name}.config.${extension}`);
115
+ if (existsSync(file)) {
116
+ const { default: options } = await import(pathToFileURL(file).href);
117
+ return new _Config(
118
+ _Config.merge(defaults, options),
119
+ root,
120
+ options
121
+ );
122
+ }
123
+ }
124
+ return new _Config(defaults, root);
125
+ }
126
+ /** Handles `--config`, writing the defaults to a TypeScript config file, or with `--config js` a JavaScript one. Returns whether it was given. */
127
+ static init(name, defaults, argv = process.argv.slice(2), root = process.cwd()) {
128
+ const index = argv.indexOf("--config");
129
+ if (index === -1) return false;
130
+ const next = argv[index + 1];
131
+ const extension = next === void 0 || next.startsWith("-") ? "ts" : next;
132
+ try {
133
+ console.log(
134
+ `created: ${_Config.export(name, defaults, extension, root)}`
135
+ );
136
+ } catch (error) {
137
+ console.error(error instanceof Error ? error.message : error);
138
+ process.exitCode = 1;
139
+ }
140
+ return true;
141
+ }
142
+ /** Writes the defaults to `<name>.config.<extension>` in the root, unless a config file is there already. */
143
+ static export(name, defaults, extension = "ts", root = process.cwd()) {
144
+ if (!_Config.extensions.includes(extension))
145
+ throw new Error(
146
+ `Unknown config file type ${extension}, use ts or js.`
147
+ );
148
+ for (const existing of _Config.extensions) {
149
+ const file2 = `${name}.config.${existing}`;
150
+ if (existsSync(join(root, file2)))
151
+ throw new Error(`${file2} already exists.`);
152
+ }
153
+ const file = `${name}.config.${extension}`;
154
+ writeFileSync(
155
+ join(root, file),
156
+ `export default ${_Config.#literal(defaults)};
121
157
  `
122
- );
123
- return file;
124
- }
125
- /** A value as JavaScript source, objects one property per line, short arrays on one line. */
126
- static #literal(value, indent = "") {
127
- const inner = `${indent} `;
128
- const lines = (items, open, close) => items.length === 0 ? `${open}${close}` : `${open}
158
+ );
159
+ return file;
160
+ }
161
+ /** A value as JavaScript source, objects one property per line, short arrays on one line. */
162
+ static #literal(value, indent = "") {
163
+ const inner = `${indent} `;
164
+ const lines = /* @__PURE__ */ __name((items, open, close) => items.length === 0 ? `${open}${close}` : `${open}
129
165
  ${items.map((item) => `${inner}${item},
130
- `).join("")}${indent}${close}`;
131
- if (Array.isArray(value)) {
132
- const items = value.map((item) => _Config.#literal(item, inner));
133
- const line = `[${items.join(", ")}]`;
134
- return line.length <= 60 && !line.includes("\n") ? line : lines(items, "[", "]");
135
- }
136
- if (value?.constructor === Object)
137
- return lines(
138
- Object.entries(value).map(
139
- ([key, item]) => `${/^[A-Za-z_$][\w$]*$/.test(key) ? key : JSON.stringify(key)}: ${_Config.#literal(item, inner)}`
140
- ),
141
- "{",
142
- "}"
143
- );
144
- return JSON.stringify(value);
145
- }
146
- /** Deep merges plain objects, anything else in override replaces base. */
147
- static merge(base, override) {
148
- const plain = (value) => value?.constructor === Object;
149
- if (!plain(base) || !plain(override)) return override ?? base;
150
- const merged = { ...base };
151
- for (const [key, value] of Object.entries(override))
152
- merged[key] = _Config.merge(base[key], value);
153
- return merged;
154
- }
155
- *files(directory = "") {
156
- const entries = readdirSync(join(this.root, directory), {
157
- withFileTypes: true
158
- });
159
- for (const entry of entries) {
160
- const path = directory ? `${directory}/${entry.name}` : entry.name;
161
- if (this.ignore.matches(path)) continue;
162
- if (entry.isDirectory()) yield* this.files(path);
163
- else yield path;
164
- }
165
- }
166
- /** Text files that are not ignored and that accepts takes, binaries are skipped. */
167
- *sources(accepts = () => true) {
168
- for (const file of this.files()) {
169
- if (!accepts(file)) continue;
170
- const path = join(this.root, file);
171
- const text = readFileSync(path, "utf8");
172
- if (!text.includes("\0")) yield { file, path, text };
173
- }
166
+ `).join("")}${indent}${close}`, "lines");
167
+ if (Array.isArray(value)) {
168
+ const items = value.map((item) => _Config.#literal(item, inner));
169
+ const line = `[${items.join(", ")}]`;
170
+ return line.length <= 60 && !line.includes("\n") ? line : lines(items, "[", "]");
171
+ }
172
+ if (value?.constructor === Object)
173
+ return lines(
174
+ Object.entries(value).map(
175
+ ([key, item]) => `${/^[A-Za-z_$][\w$]*$/.test(key) ? key : JSON.stringify(key)}: ${_Config.#literal(item, inner)}`
176
+ ),
177
+ "{",
178
+ "}"
179
+ );
180
+ return JSON.stringify(value);
181
+ }
182
+ /** Deep merges plain objects, anything else in override replaces base. */
183
+ static merge(base, override) {
184
+ const plain = /* @__PURE__ */ __name((value) => value?.constructor === Object, "plain");
185
+ if (!plain(base) || !plain(override)) return override ?? base;
186
+ const merged = { ...base };
187
+ for (const [key, value] of Object.entries(override))
188
+ merged[key] = _Config.merge(base[key], value);
189
+ return merged;
190
+ }
191
+ *files(directory = "") {
192
+ const entries = readdirSync(join(this.root, directory), {
193
+ withFileTypes: true
194
+ });
195
+ for (const entry of entries) {
196
+ const path = directory ? `${directory}/${entry.name}` : entry.name;
197
+ if (this.ignore.matches(path)) continue;
198
+ if (entry.isDirectory()) yield* this.files(path);
199
+ else yield path;
200
+ }
201
+ }
202
+ /** Text files that are not ignored and that accepts takes, binaries are skipped. */
203
+ *sources(accepts = () => true) {
204
+ for (const file of this.files()) {
205
+ if (!accepts(file)) continue;
206
+ const path = join(this.root, file);
207
+ const text = readFileSync(path, "utf8");
208
+ if (!text.includes("\0")) yield { file, path, text };
209
+ }
210
+ }
211
+ };
174
212
  }
175
- };
213
+ });
176
214
 
177
215
  // shared/config/src/includes.ts
178
- var Includes = class {
179
- #globs;
180
- constructor(globs) {
181
- this.#globs = globs.map((glob) => ({
182
- pattern: new Pattern(glob.replace(/^!/, "")),
183
- included: !glob.startsWith("!")
184
- }));
185
- }
186
- matches(file) {
187
- let included = false;
188
- for (const { pattern, included: positive } of this.#globs)
189
- if (pattern.matches(file)) included = positive;
190
- return included;
191
- }
192
- };
193
-
194
- // packages/testero/src/arguments.ts
195
- var Arguments = class {
196
- static usage = `Usage: testero [patterns...] [options]
197
-
198
- Runs the test files whose paths match a pattern, every test file without patterns.
199
-
200
- Options:
201
- -t, --testNamePattern <regex> run only tests whose name matches
202
- --bail stop after the first failing test file
203
- --verbose list every test
204
- --listTests print the test files instead of running them
205
- --passWithNoTests succeed when there are no test files
206
- --testTimeout <ms> milliseconds a test or hook may take
207
- --config [js] write the default testero.config.ts, or .js
208
- -h, --help show this help
209
- `;
210
- patterns = [];
211
- rules = {};
212
- namePattern;
213
- listTests = false;
214
- help = false;
215
- constructor(argv) {
216
- for (let index = 0; index < argv.length; index++) {
217
- const argument = argv[index];
218
- const [name, inline] = argument.split(/=(.*)/s);
219
- const value = () => {
220
- const next = inline ?? argv[++index];
221
- if (next === void 0)
222
- throw new Error(`${name} needs a value.`);
223
- return next;
224
- };
225
- switch (name) {
226
- case "-t":
227
- case "--testNamePattern":
228
- this.namePattern = new RegExp(value());
229
- break;
230
- case "--testTimeout":
231
- this.rules.testTimeout = Number(value());
232
- break;
233
- case "--bail":
234
- case "--verbose":
235
- case "--passWithNoTests":
236
- this.rules[name.slice(2)] = true;
237
- break;
238
- case "--listTests":
239
- this.listTests = true;
240
- break;
241
- case "-h":
242
- case "--help":
243
- this.help = true;
244
- break;
245
- default:
246
- if (name.startsWith("-"))
247
- throw new Error(`Unknown option ${name}.`);
248
- this.patterns.push(argument);
216
+ var Includes;
217
+ var init_includes = __esm({
218
+ "shared/config/src/includes.ts"() {
219
+ "use strict";
220
+ init_pattern();
221
+ Includes = class {
222
+ static {
223
+ __name(this, "Includes");
224
+ }
225
+ #globs;
226
+ constructor(globs) {
227
+ this.#globs = globs.map((glob) => ({
228
+ pattern: new Pattern(glob.replace(/^!/, "")),
229
+ included: !glob.startsWith("!")
230
+ }));
231
+ }
232
+ matches(file) {
233
+ let included = false;
234
+ for (const { pattern, included: positive } of this.#globs)
235
+ if (pattern.matches(file)) included = positive;
236
+ return included;
249
237
  }
250
- }
251
- }
252
- /** The configured rules, with the ones given on the command line instead. */
253
- options(rules2) {
254
- return {
255
- ...rules2,
256
- ...this.rules,
257
- patterns: this.patterns,
258
- namePattern: this.namePattern
259
238
  };
260
239
  }
261
- };
262
-
263
- // packages/testero/src/config.ts
264
- var extensions = "{js,jsx,ts,tsx,mjs,cjs,mts,cts}";
265
- var rules = {
266
- testMatch: [
267
- `**/__tests__/**/*.${extensions}`,
268
- `**/*.{spec,test}.${extensions}`
269
- ],
270
- testTimeout: 5e3,
271
- bail: false,
272
- verbose: false,
273
- passWithNoTests: false,
274
- clearMocks: false,
275
- resetMocks: false,
276
- restoreMocks: false
277
- };
278
- var config_default = {
279
- ignore: [".git", "node_modules"],
280
- rules
281
- };
240
+ });
282
241
 
283
- // packages/testero/src/testero.ts
284
- import { join as join3 } from "node:path";
242
+ // shared/config/src/index.ts
243
+ var init_src = __esm({
244
+ "shared/config/src/index.ts"() {
245
+ "use strict";
246
+ init_config();
247
+ init_includes();
248
+ init_pattern();
249
+ }
250
+ });
285
251
 
286
252
  // packages/testero/src/expect/format.ts
287
- var Format = class _Format {
288
- /** A value with one property or item per line. */
289
- static value(value) {
290
- return _Format.#print(value, "", true, []);
291
- }
292
- /** A value on one line. */
293
- static inline(value) {
294
- return _Format.#print(value, "", false, []);
295
- }
296
- /** A line diff of two values, `-` for expected lines and `+` for received ones. */
297
- static diff(expected, received) {
298
- const a = _Format.value(expected).split("\n");
299
- const b = _Format.value(received).split("\n");
300
- if (a.length * b.length > 4e6)
301
- return `Expected: ${_Format.inline(expected)}
253
+ var Format;
254
+ var init_format = __esm({
255
+ "packages/testero/src/expect/format.ts"() {
256
+ "use strict";
257
+ Format = class _Format {
258
+ static {
259
+ __name(this, "Format");
260
+ }
261
+ /** A value with one property or item per line. */
262
+ static value(value) {
263
+ return _Format.#print(value, "", true, []);
264
+ }
265
+ /** A value on one line. */
266
+ static inline(value) {
267
+ return _Format.#print(value, "", false, []);
268
+ }
269
+ /** A line diff of two values, `-` for expected lines and `+` for received ones. */
270
+ static diff(expected, received) {
271
+ const a = _Format.value(expected).split("\n");
272
+ const b = _Format.value(received).split("\n");
273
+ if (a.length * b.length > 4e6)
274
+ return `Expected: ${_Format.inline(expected)}
302
275
  Received: ${_Format.inline(received)}`;
303
- return ["- Expected", "+ Received", "", ..._Format.#lines(a, b)].join(
304
- "\n"
305
- );
306
- }
307
- /** Lines of a longest common subsequence diff. */
308
- static #lines(a, b) {
309
- const lengths = Array.from(
310
- { length: a.length + 1 },
311
- () => new Array(b.length + 1).fill(0)
312
- );
313
- for (let i2 = a.length - 1; i2 >= 0; i2--)
314
- for (let j2 = b.length - 1; j2 >= 0; j2--)
315
- lengths[i2][j2] = a[i2] === b[j2] ? lengths[i2 + 1][j2 + 1] + 1 : Math.max(
316
- lengths[i2 + 1][j2],
317
- lengths[i2][j2 + 1]
276
+ return ["- Expected", "+ Received", "", ..._Format.#lines(a, b)].join(
277
+ "\n"
318
278
  );
319
- const lines = [];
320
- let i = 0;
321
- let j = 0;
322
- while (i < a.length || j < b.length) {
323
- if (i < a.length && j < b.length && a[i] === b[j]) {
324
- lines.push(` ${a[i++]}`);
325
- j++;
326
- } else if (j >= b.length || i < a.length && lengths[i + 1][j] >= lengths[i][j + 1])
327
- lines.push(`- ${a[i++]}`);
328
- else lines.push(`+ ${b[j++]}`);
329
- }
330
- return lines;
331
- }
332
- static #print(value, indent, multiline, seen) {
333
- switch (typeof value) {
334
- case "string":
335
- return JSON.stringify(value);
336
- case "number":
337
- return Object.is(value, -0) ? "-0" : String(value);
338
- case "bigint":
339
- return `${value}n`;
340
- case "symbol":
341
- return value.toString();
342
- case "function":
343
- return `[Function ${value.name || "anonymous"}]`;
344
- case "undefined":
345
- return "undefined";
346
- case "boolean":
347
- return String(value);
348
- }
349
- if (value === null) return "null";
350
- const object = value;
351
- if (typeof object.asymmetricMatch === "function") return String(object);
352
- if (value instanceof Date)
353
- return Number.isNaN(value.getTime()) ? "Date { NaN }" : value.toISOString();
354
- if (value instanceof RegExp) return String(value);
355
- if (value instanceof Error) return `[${value.name}: ${value.message}]`;
356
- if (seen.includes(value)) return "[Circular]";
357
- const inner = [...seen, value];
358
- const print = (item) => _Format.#print(item, `${indent} `, multiline, inner);
359
- const name = object.constructor?.name;
360
- if (value instanceof Map)
361
- return _Format.#block(
362
- `Map {`,
363
- "}",
364
- [...value].map(
365
- ([key, item]) => `${print(key)} => ${print(item)}`
366
- ),
367
- indent,
368
- multiline
369
- );
370
- if (value instanceof Set)
371
- return _Format.#block(
372
- "Set {",
373
- "}",
374
- [...value].map(print),
375
- indent,
376
- multiline
377
- );
378
- if (Array.isArray(value) || ArrayBuffer.isView(value)) {
379
- const items = Array.from(value, print);
380
- const prefix2 = name && name !== "Array" ? `${name} [` : "[";
381
- return _Format.#block(prefix2, "]", items, indent, multiline);
382
- }
383
- const keys = [
384
- ...Object.keys(object),
385
- ...Object.getOwnPropertySymbols(object).filter(
386
- (symbol) => Object.getOwnPropertyDescriptor(object, symbol)?.enumerable
387
- )
388
- ];
389
- const entries = keys.map(
390
- (key) => `${typeof key === "symbol" ? key.toString() : JSON.stringify(key)}: ${print(object[key])}`
391
- );
392
- const prefix = name && name !== "Object" ? `${name} {` : "{";
393
- return _Format.#block(prefix, "}", entries, indent, multiline);
394
- }
395
- static #block(open, close, entries, indent, multiline) {
396
- if (entries.length === 0) return `${open}${close}`;
397
- if (!multiline) return `${open}${entries.join(", ")}${close}`;
398
- return `${open}
279
+ }
280
+ /** Lines of a longest common subsequence diff. */
281
+ static #lines(a, b) {
282
+ const lengths = Array.from(
283
+ { length: a.length + 1 },
284
+ () => new Array(b.length + 1).fill(0)
285
+ );
286
+ for (let i2 = a.length - 1; i2 >= 0; i2--)
287
+ for (let j2 = b.length - 1; j2 >= 0; j2--)
288
+ lengths[i2][j2] = a[i2] === b[j2] ? lengths[i2 + 1][j2 + 1] + 1 : Math.max(
289
+ lengths[i2 + 1][j2],
290
+ lengths[i2][j2 + 1]
291
+ );
292
+ const lines = [];
293
+ let i = 0;
294
+ let j = 0;
295
+ while (i < a.length || j < b.length) {
296
+ if (i < a.length && j < b.length && a[i] === b[j]) {
297
+ lines.push(` ${a[i++]}`);
298
+ j++;
299
+ } else if (j >= b.length || i < a.length && lengths[i + 1][j] >= lengths[i][j + 1])
300
+ lines.push(`- ${a[i++]}`);
301
+ else lines.push(`+ ${b[j++]}`);
302
+ }
303
+ return lines;
304
+ }
305
+ static #print(value, indent, multiline, seen) {
306
+ switch (typeof value) {
307
+ case "string":
308
+ return JSON.stringify(value);
309
+ case "number":
310
+ return Object.is(value, -0) ? "-0" : String(value);
311
+ case "bigint":
312
+ return `${value}n`;
313
+ case "symbol":
314
+ return value.toString();
315
+ case "function":
316
+ return `[Function ${value.name || "anonymous"}]`;
317
+ case "undefined":
318
+ return "undefined";
319
+ case "boolean":
320
+ return String(value);
321
+ }
322
+ if (value === null) return "null";
323
+ const object = value;
324
+ if (typeof object.asymmetricMatch === "function") return String(object);
325
+ if (value instanceof Date)
326
+ return Number.isNaN(value.getTime()) ? "Date { NaN }" : value.toISOString();
327
+ if (value instanceof RegExp) return String(value);
328
+ if (value instanceof Error) return `[${value.name}: ${value.message}]`;
329
+ if (seen.includes(value)) return "[Circular]";
330
+ const inner = [...seen, value];
331
+ const print = /* @__PURE__ */ __name((item) => _Format.#print(item, `${indent} `, multiline, inner), "print");
332
+ const name = object.constructor?.name;
333
+ if (value instanceof Map)
334
+ return _Format.#block(
335
+ `Map {`,
336
+ "}",
337
+ [...value].map(
338
+ ([key, item]) => `${print(key)} => ${print(item)}`
339
+ ),
340
+ indent,
341
+ multiline
342
+ );
343
+ if (value instanceof Set)
344
+ return _Format.#block(
345
+ "Set {",
346
+ "}",
347
+ [...value].map(print),
348
+ indent,
349
+ multiline
350
+ );
351
+ if (Array.isArray(value) || ArrayBuffer.isView(value)) {
352
+ const items = Array.from(value, print);
353
+ const prefix2 = name && name !== "Array" ? `${name} [` : "[";
354
+ return _Format.#block(prefix2, "]", items, indent, multiline);
355
+ }
356
+ const keys = [
357
+ ...Object.keys(object),
358
+ ...Object.getOwnPropertySymbols(object).filter(
359
+ (symbol) => Object.getOwnPropertyDescriptor(object, symbol)?.enumerable
360
+ )
361
+ ];
362
+ const entries = keys.map(
363
+ (key) => `${typeof key === "symbol" ? key.toString() : JSON.stringify(key)}: ${print(object[key])}`
364
+ );
365
+ const prefix = name && name !== "Object" ? `${name} {` : "{";
366
+ return _Format.#block(prefix, "}", entries, indent, multiline);
367
+ }
368
+ static #block(open, close, entries, indent, multiline) {
369
+ if (entries.length === 0) return `${open}${close}`;
370
+ if (!multiline) return `${open}${entries.join(", ")}${close}`;
371
+ return `${open}
399
372
  ${entries.map((entry) => `${indent} ${entry},
400
373
  `).join("")}${indent}${close}`;
374
+ }
375
+ };
401
376
  }
402
- };
377
+ });
403
378
 
404
379
  // packages/testero/src/each.ts
405
- var Each = class _Each {
406
- /**
407
- * The argument lists of a table: rows of arrays are spread, other rows
408
- * are one argument, and a tagged template gives one object per row.
409
- */
410
- static rows(table, values) {
411
- if (_Each.#template(table)) {
412
- const names = table.join("").trim().split(/\s*\|\s*/);
413
- const rows = [];
414
- for (let index = 0; index < values.length; index += names.length)
415
- rows.push([
416
- Object.fromEntries(
417
- names.map((name, offset) => [
418
- name,
419
- values[index + offset]
420
- ])
421
- )
422
- ]);
423
- return rows;
424
- }
425
- if (!Array.isArray(table))
426
- throw new TypeError(
427
- "each needs an array or a tagged template table"
428
- );
429
- return table.map((row) => Array.isArray(row) ? row : [row]);
430
- }
431
- /** A title with `$name` properties of an object row, or printf placeholders. */
432
- static title(title, args, index) {
433
- const [first] = args;
434
- if (args.length === 1 && _Each.#plain(first) && title.includes("$"))
435
- return title.replace(
436
- /\$([\w$]+(?:\.[\w$]+)*)/g,
437
- (match, path) => path === "#" ? String(index) : _Each.#show(_Each.#at(first, path.split("."), match))
438
- );
439
- const rest = [...args];
440
- return title.replace(/%([sdifjoOp#$%])/g, (match, flag) => {
441
- if (flag === "%") return "%";
442
- if (flag === "#") return String(index);
443
- if (flag === "$") return String(index + 1);
444
- if (rest.length === 0) return match;
445
- const value = rest.shift();
446
- switch (flag) {
447
- case "d":
448
- case "i":
449
- return String(
450
- flag === "i" ? Math.trunc(Number(value)) : Number(value)
380
+ var Each;
381
+ var init_each = __esm({
382
+ "packages/testero/src/each.ts"() {
383
+ "use strict";
384
+ init_format();
385
+ Each = class _Each {
386
+ static {
387
+ __name(this, "Each");
388
+ }
389
+ /**
390
+ * The argument lists of a table: rows of arrays are spread, other rows
391
+ * are one argument, and a tagged template gives one object per row.
392
+ */
393
+ static rows(table, values) {
394
+ if (_Each.#template(table)) {
395
+ const names = table.join("").trim().split(/\s*\|\s*/);
396
+ const rows = [];
397
+ for (let index = 0; index < values.length; index += names.length)
398
+ rows.push([
399
+ Object.fromEntries(
400
+ names.map((name, offset) => [
401
+ name,
402
+ values[index + offset]
403
+ ])
404
+ )
405
+ ]);
406
+ return rows;
407
+ }
408
+ if (!Array.isArray(table))
409
+ throw new TypeError(
410
+ "each needs an array or a tagged template table"
451
411
  );
452
- case "f":
453
- return String(Number(value));
454
- case "j":
455
- return JSON.stringify(value);
456
- case "s":
457
- return _Each.#show(value);
458
- default:
459
- return Format.inline(value);
460
- }
461
- });
462
- }
463
- static #template(table) {
464
- return Array.isArray(table) && "raw" in table;
465
- }
466
- static #plain(value) {
467
- return typeof value === "object" && value !== null && !Array.isArray(value);
468
- }
469
- static #at(value, path, fallback) {
470
- let current = value;
471
- for (const key of path) {
472
- if (typeof current !== "object" || current === null || !(key in current))
473
- return fallback;
474
- current = current[key];
475
- }
476
- return current;
477
- }
478
- /** Strings as they are, other values like Jest prints them. */
479
- static #show(value) {
480
- return typeof value === "string" ? value : Format.inline(value);
412
+ return table.map((row) => Array.isArray(row) ? row : [row]);
413
+ }
414
+ /** A title with `$name` properties of an object row, or printf placeholders. */
415
+ static title(title, args, index) {
416
+ const [first] = args;
417
+ if (args.length === 1 && _Each.#plain(first) && title.includes("$"))
418
+ return title.replace(
419
+ /\$([\w$]+(?:\.[\w$]+)*)/g,
420
+ (match, path) => path === "#" ? String(index) : _Each.#show(_Each.#at(first, path.split("."), match))
421
+ );
422
+ const rest = [...args];
423
+ return title.replace(/%([sdifjoOp#$%])/g, (match, flag) => {
424
+ if (flag === "%") return "%";
425
+ if (flag === "#") return String(index);
426
+ if (flag === "$") return String(index + 1);
427
+ if (rest.length === 0) return match;
428
+ const value = rest.shift();
429
+ switch (flag) {
430
+ case "d":
431
+ case "i":
432
+ return String(
433
+ flag === "i" ? Math.trunc(Number(value)) : Number(value)
434
+ );
435
+ case "f":
436
+ return String(Number(value));
437
+ case "j":
438
+ return JSON.stringify(value);
439
+ case "s":
440
+ return _Each.#show(value);
441
+ default:
442
+ return Format.inline(value);
443
+ }
444
+ });
445
+ }
446
+ static #template(table) {
447
+ return Array.isArray(table) && "raw" in table;
448
+ }
449
+ static #plain(value) {
450
+ return typeof value === "object" && value !== null && !Array.isArray(value);
451
+ }
452
+ static #at(value, path, fallback) {
453
+ let current = value;
454
+ for (const key of path) {
455
+ if (typeof current !== "object" || current === null || !(key in current))
456
+ return fallback;
457
+ current = current[key];
458
+ }
459
+ return current;
460
+ }
461
+ /** Strings as they are, other values like Jest prints them. */
462
+ static #show(value) {
463
+ return typeof value === "string" ? value : Format.inline(value);
464
+ }
465
+ };
481
466
  }
482
- };
467
+ });
483
468
 
484
469
  // packages/testero/src/suite.ts
485
- var Suite = class {
486
- name;
487
- mode;
488
- parent;
489
- children = [];
490
- hooks = {
491
- beforeAll: [],
492
- afterAll: [],
493
- beforeEach: [],
494
- afterEach: []
495
- };
496
- constructor(name, mode = "run", parent) {
497
- this.name = name;
498
- this.mode = mode;
499
- this.parent = parent;
500
- }
501
- /** The names of the suites from the file down to this one, the file has none. */
502
- get titles() {
503
- return this.parent ? [...this.parent.titles, this.name] : [];
504
- }
505
- /** This suite and the ones around it, from the file down. */
506
- get path() {
507
- return this.parent ? [...this.parent.path, this] : [this];
508
- }
509
- /** Every test, of nested suites too, in order. */
510
- *tests() {
511
- for (const child of this.children)
512
- if (child instanceof Test) yield child;
513
- else yield* child.tests();
514
- }
515
- };
516
- var Test = class {
517
- name;
518
- body;
519
- mode;
520
- timeout;
521
- suite;
522
- constructor(name, body, mode, timeout, suite) {
523
- this.name = name;
524
- this.body = body;
525
- this.mode = mode;
526
- this.timeout = timeout;
527
- this.suite = suite;
528
- }
529
- /** The names of the suites around the test, and its own. */
530
- get titles() {
531
- return [...this.suite.titles, this.name];
532
- }
533
- /** The titles joined like Jest does for `-t`. */
534
- get fullName() {
535
- return this.titles.join(" ");
536
- }
537
- /** Whether the test, or a suite around it, is marked with `only`. */
538
- get focused() {
539
- return this.mode === "only" || this.suite.path.some((suite) => suite.mode === "only");
540
- }
541
- /** Whether the test, or a suite around it, is skipped or todo. */
542
- get skipped() {
543
- return this.mode === "skip" || this.mode === "todo" || this.suite.path.some((suite) => suite.mode === "skip");
470
+ var Suite, Test;
471
+ var init_suite = __esm({
472
+ "packages/testero/src/suite.ts"() {
473
+ "use strict";
474
+ Suite = class {
475
+ static {
476
+ __name(this, "Suite");
477
+ }
478
+ name;
479
+ mode;
480
+ parent;
481
+ children = [];
482
+ hooks = {
483
+ beforeAll: [],
484
+ afterAll: [],
485
+ beforeEach: [],
486
+ afterEach: []
487
+ };
488
+ constructor(name, mode = "run", parent) {
489
+ this.name = name;
490
+ this.mode = mode;
491
+ this.parent = parent;
492
+ }
493
+ /** The names of the suites from the file down to this one, the file has none. */
494
+ get titles() {
495
+ return this.parent ? [...this.parent.titles, this.name] : [];
496
+ }
497
+ /** This suite and the ones around it, from the file down. */
498
+ get path() {
499
+ return this.parent ? [...this.parent.path, this] : [this];
500
+ }
501
+ /** Every test, of nested suites too, in order. */
502
+ *tests() {
503
+ for (const child of this.children)
504
+ if (child instanceof Test) yield child;
505
+ else yield* child.tests();
506
+ }
507
+ };
508
+ Test = class {
509
+ static {
510
+ __name(this, "Test");
511
+ }
512
+ name;
513
+ body;
514
+ mode;
515
+ timeout;
516
+ suite;
517
+ constructor(name, body, mode, timeout, suite) {
518
+ this.name = name;
519
+ this.body = body;
520
+ this.mode = mode;
521
+ this.timeout = timeout;
522
+ this.suite = suite;
523
+ }
524
+ /** The names of the suites around the test, and its own. */
525
+ get titles() {
526
+ return [...this.suite.titles, this.name];
527
+ }
528
+ /** The titles joined like Jest does for `-t`. */
529
+ get fullName() {
530
+ return this.titles.join(" ");
531
+ }
532
+ /** Whether the test, or a suite around it, is marked with `only`. */
533
+ get focused() {
534
+ return this.mode === "only" || this.suite.path.some((suite) => suite.mode === "only");
535
+ }
536
+ /** Whether the test, or a suite around it, is skipped or todo. */
537
+ get skipped() {
538
+ return this.mode === "skip" || this.mode === "todo" || this.suite.path.some((suite) => suite.mode === "skip");
539
+ }
540
+ };
544
541
  }
545
- };
542
+ });
546
543
 
547
544
  // packages/testero/src/collector.ts
548
- var Collector = class {
549
- root = new Suite("");
550
- #current = this.root;
551
- #closed = false;
552
- describe = this.#describes();
553
- test = this.#tests();
554
- /** Ends collection, tests cannot be declared while tests run. */
555
- close() {
556
- this.#closed = true;
557
- }
558
- /** `beforeAll`, `afterAll`, `beforeEach` or `afterEach`. */
559
- hook = (kind) => (body, timeout) => {
560
- this.#open(kind);
561
- this.#current.hooks[kind].push({ body, timeout });
562
- };
563
- #describes() {
564
- const describe = (mode) => (name, body) => this.#describe(name, body, mode);
565
- const each = (mode) => (table, ...values) => (name, body) => {
566
- for (const [index, row] of Each.rows(table, values).entries())
567
- this.#describe(
568
- Each.title(String(name), row, index),
569
- () => body(...row),
570
- mode
571
- );
572
- };
573
- return Object.assign(describe("run"), {
574
- only: Object.assign(describe("only"), { each: each("only") }),
575
- skip: Object.assign(describe("skip"), { each: each("skip") }),
576
- each: each("run")
577
- });
578
- }
579
- #tests() {
580
- const test = (mode) => (name, body, timeout) => this.#test(name, body, mode, timeout);
581
- const each = (mode) => (table, ...values) => (name, body, timeout) => {
582
- for (const [index, row] of Each.rows(table, values).entries())
583
- this.#test(
584
- Each.title(String(name), row, index),
585
- () => body(...row),
586
- mode,
587
- timeout
545
+ var Collector;
546
+ var init_collector = __esm({
547
+ "packages/testero/src/collector.ts"() {
548
+ "use strict";
549
+ init_each();
550
+ init_suite();
551
+ Collector = class {
552
+ static {
553
+ __name(this, "Collector");
554
+ }
555
+ root = new Suite("");
556
+ #current = this.root;
557
+ #closed = false;
558
+ describe = this.#describes();
559
+ test = this.#tests();
560
+ /** Ends collection, tests cannot be declared while tests run. */
561
+ close() {
562
+ this.#closed = true;
563
+ }
564
+ /** `beforeAll`, `afterAll`, `beforeEach` or `afterEach`. */
565
+ hook = /* @__PURE__ */ __name((kind) => (body, timeout) => {
566
+ this.#open(kind);
567
+ this.#current.hooks[kind].push({ body, timeout });
568
+ }, "hook");
569
+ #describes() {
570
+ const describe = /* @__PURE__ */ __name((mode) => (name, body) => this.#describe(name, body, mode), "describe");
571
+ const each = /* @__PURE__ */ __name((mode) => (table, ...values) => (name, body) => {
572
+ for (const [index, row] of Each.rows(table, values).entries())
573
+ this.#describe(
574
+ Each.title(String(name), row, index),
575
+ () => body(...row),
576
+ mode
577
+ );
578
+ }, "each");
579
+ return Object.assign(describe("run"), {
580
+ only: Object.assign(describe("only"), { each: each("only") }),
581
+ skip: Object.assign(describe("skip"), { each: each("skip") }),
582
+ each: each("run")
583
+ });
584
+ }
585
+ #tests() {
586
+ const test = /* @__PURE__ */ __name((mode) => (name, body, timeout) => this.#test(name, body, mode, timeout), "test");
587
+ const each = /* @__PURE__ */ __name((mode) => (table, ...values) => (name, body, timeout) => {
588
+ for (const [index, row] of Each.rows(table, values).entries())
589
+ this.#test(
590
+ Each.title(String(name), row, index),
591
+ () => body(...row),
592
+ mode,
593
+ timeout
594
+ );
595
+ }, "each");
596
+ return Object.assign(test("run"), {
597
+ only: Object.assign(test("only"), { each: each("only") }),
598
+ skip: Object.assign(test("skip"), { each: each("skip") }),
599
+ todo: /* @__PURE__ */ __name((name) => this.#test(name, void 0, "todo", void 0), "todo"),
600
+ each: each("run")
601
+ });
602
+ }
603
+ #describe(name, body, mode) {
604
+ this.#open("describe");
605
+ const suite = new Suite(String(name), mode, this.#current);
606
+ this.#current.children.push(suite);
607
+ const outer = this.#current;
608
+ this.#current = suite;
609
+ try {
610
+ const result = body();
611
+ if (result instanceof Promise)
612
+ throw new Error(
613
+ `describe "${suite.name}" returned a promise, its tests must be declared synchronously.`
614
+ );
615
+ } finally {
616
+ this.#current = outer;
617
+ }
618
+ }
619
+ #test(name, body, mode, timeout) {
620
+ this.#open("test");
621
+ if (mode !== "todo" && typeof body !== "function")
622
+ throw new TypeError(`test "${name}" needs a function.`);
623
+ this.#current.children.push(
624
+ new Test(String(name), body, mode, timeout, this.#current)
588
625
  );
626
+ }
627
+ #open(what) {
628
+ if (this.#closed)
629
+ throw new Error(
630
+ `Cannot declare ${what} while tests run, declare it at the top level or in describe.`
631
+ );
632
+ }
589
633
  };
590
- return Object.assign(test("run"), {
591
- only: Object.assign(test("only"), { each: each("only") }),
592
- skip: Object.assign(test("skip"), { each: each("skip") }),
593
- todo: (name) => this.#test(name, void 0, "todo", void 0),
594
- each: each("run")
595
- });
596
634
  }
597
- #describe(name, body, mode) {
598
- this.#open("describe");
599
- const suite = new Suite(String(name), mode, this.#current);
600
- this.#current.children.push(suite);
601
- const outer = this.#current;
602
- this.#current = suite;
603
- try {
604
- const result = body();
605
- if (result instanceof Promise)
606
- throw new Error(
607
- `describe "${suite.name}" returned a promise, its tests must be declared synchronously.`
608
- );
609
- } finally {
610
- this.#current = outer;
611
- }
612
- }
613
- #test(name, body, mode, timeout) {
614
- this.#open("test");
615
- if (mode !== "todo" && typeof body !== "function")
616
- throw new TypeError(`test "${name}" needs a function.`);
617
- this.#current.children.push(
618
- new Test(String(name), body, mode, timeout, this.#current)
619
- );
620
- }
621
- #open(what) {
622
- if (this.#closed)
623
- throw new Error(
624
- `Cannot declare ${what} while tests run, declare it at the top level or in describe.`
625
- );
626
- }
627
- };
635
+ });
628
636
 
629
637
  // packages/testero/src/mock.ts
630
- var Mock = class _Mock {
631
- function;
632
- state = _Mock.#empty();
633
- /** What the mock does after a reset, a spy calls its original. */
634
- #original;
635
- #restore;
636
- #implementation;
637
- #once = [];
638
- #name = "testero.fn()";
639
- constructor(implementation, restore) {
640
- this.#original = implementation;
641
- this.#implementation = implementation;
642
- this.#restore = restore;
643
- const mock = this;
644
- const fn = function(...args) {
645
- return mock.#call(this, args, new.target);
646
- };
647
- Object.defineProperty(fn, "mock", { get: () => mock.state });
648
- this.function = Object.assign(fn, this.#methods(fn));
649
- }
650
- #methods(fn) {
651
- const set = (change) => {
652
- change();
653
- return fn;
654
- };
655
- return {
656
- _isMockFunction: true,
657
- mockImplementation: (implementation) => set(() => {
638
+ var Mock, Mocks;
639
+ var init_mock = __esm({
640
+ "packages/testero/src/mock.ts"() {
641
+ "use strict";
642
+ Mock = class _Mock {
643
+ static {
644
+ __name(this, "Mock");
645
+ }
646
+ function;
647
+ state = _Mock.#empty();
648
+ /** What the mock does after a reset, a spy calls its original. */
649
+ #original;
650
+ #restore;
651
+ #implementation;
652
+ #once = [];
653
+ #name = "testero.fn()";
654
+ constructor(implementation, restore) {
655
+ this.#original = implementation;
658
656
  this.#implementation = implementation;
659
- }),
660
- mockImplementationOnce: (implementation) => set(() => this.#once.push(implementation)),
661
- mockReturnValue: (value) => fn.mockImplementation(() => value),
662
- mockReturnValueOnce: (value) => fn.mockImplementationOnce(() => value),
663
- mockResolvedValue: (value) => fn.mockImplementation(() => Promise.resolve(value)),
664
- mockResolvedValueOnce: (value) => fn.mockImplementationOnce(() => Promise.resolve(value)),
665
- mockRejectedValue: (value) => fn.mockImplementation(() => Promise.reject(value)),
666
- mockRejectedValueOnce: (value) => fn.mockImplementationOnce(() => Promise.reject(value)),
667
- mockReturnThis: () => fn.mockImplementation(function() {
668
- return this;
669
- }),
670
- mockName: (name) => set(() => {
671
- this.#name = name;
672
- }),
673
- getMockName: () => this.#name,
674
- getMockImplementation: () => this.#implementation,
675
- mockClear: () => set(() => this.clear()),
676
- mockReset: () => set(() => this.reset()),
677
- mockRestore: () => this.restore()
657
+ this.#restore = restore;
658
+ const mock = this;
659
+ const fn = /* @__PURE__ */ __name(function(...args) {
660
+ return mock.#call(this, args, new.target);
661
+ }, "fn");
662
+ Object.defineProperty(fn, "mock", { get: /* @__PURE__ */ __name(() => mock.state, "get") });
663
+ this.function = Object.assign(fn, this.#methods(fn));
664
+ }
665
+ #methods(fn) {
666
+ const set = /* @__PURE__ */ __name((change) => {
667
+ change();
668
+ return fn;
669
+ }, "set");
670
+ return {
671
+ _isMockFunction: true,
672
+ mockImplementation: /* @__PURE__ */ __name((implementation) => set(() => {
673
+ this.#implementation = implementation;
674
+ }), "mockImplementation"),
675
+ mockImplementationOnce: /* @__PURE__ */ __name((implementation) => set(() => this.#once.push(implementation)), "mockImplementationOnce"),
676
+ mockReturnValue: /* @__PURE__ */ __name((value) => fn.mockImplementation(() => value), "mockReturnValue"),
677
+ mockReturnValueOnce: /* @__PURE__ */ __name((value) => fn.mockImplementationOnce(() => value), "mockReturnValueOnce"),
678
+ mockResolvedValue: /* @__PURE__ */ __name((value) => fn.mockImplementation(() => Promise.resolve(value)), "mockResolvedValue"),
679
+ mockResolvedValueOnce: /* @__PURE__ */ __name((value) => fn.mockImplementationOnce(() => Promise.resolve(value)), "mockResolvedValueOnce"),
680
+ mockRejectedValue: /* @__PURE__ */ __name((value) => fn.mockImplementation(() => Promise.reject(value)), "mockRejectedValue"),
681
+ mockRejectedValueOnce: /* @__PURE__ */ __name((value) => fn.mockImplementationOnce(() => Promise.reject(value)), "mockRejectedValueOnce"),
682
+ mockReturnThis: /* @__PURE__ */ __name(() => fn.mockImplementation(function() {
683
+ return this;
684
+ }), "mockReturnThis"),
685
+ mockName: /* @__PURE__ */ __name((name) => set(() => {
686
+ this.#name = name;
687
+ }), "mockName"),
688
+ getMockName: /* @__PURE__ */ __name(() => this.#name, "getMockName"),
689
+ getMockImplementation: /* @__PURE__ */ __name(() => this.#implementation, "getMockImplementation"),
690
+ mockClear: /* @__PURE__ */ __name(() => set(() => this.clear()), "mockClear"),
691
+ mockReset: /* @__PURE__ */ __name(() => set(() => this.reset()), "mockReset"),
692
+ mockRestore: /* @__PURE__ */ __name(() => this.restore(), "mockRestore")
693
+ };
694
+ }
695
+ /** Forgets the calls. */
696
+ clear() {
697
+ this.state = _Mock.#empty();
698
+ }
699
+ /** Forgets the calls and the implementations given since. */
700
+ reset() {
701
+ this.clear();
702
+ this.#implementation = this.#original;
703
+ this.#once = [];
704
+ }
705
+ /** Resets, and puts a spied method back. */
706
+ restore() {
707
+ this.reset();
708
+ this.#restore?.();
709
+ }
710
+ #call(context, args, target) {
711
+ const result = { type: "incomplete", value: void 0 };
712
+ this.state.calls.push(args);
713
+ this.state.contexts.push(context);
714
+ this.state.instances.push(context);
715
+ this.state.results.push(result);
716
+ this.state.lastCall = args;
717
+ const implementation = this.#once.shift() ?? this.#implementation;
718
+ try {
719
+ result.value = !implementation ? void 0 : target ? Reflect.construct(implementation, args, target) : implementation.apply(context, args);
720
+ result.type = "return";
721
+ return result.value;
722
+ } catch (error) {
723
+ result.type = "throw";
724
+ result.value = error;
725
+ throw error;
726
+ }
727
+ }
728
+ static #empty() {
729
+ return {
730
+ calls: [],
731
+ results: [],
732
+ instances: [],
733
+ contexts: [],
734
+ lastCall: void 0
735
+ };
736
+ }
678
737
  };
679
- }
680
- /** Forgets the calls. */
681
- clear() {
682
- this.state = _Mock.#empty();
683
- }
684
- /** Forgets the calls and the implementations given since. */
685
- reset() {
686
- this.clear();
687
- this.#implementation = this.#original;
688
- this.#once = [];
689
- }
690
- /** Resets, and puts a spied method back. */
691
- restore() {
692
- this.reset();
693
- this.#restore?.();
694
- }
695
- #call(context, args, target) {
696
- const result = { type: "incomplete", value: void 0 };
697
- this.state.calls.push(args);
698
- this.state.contexts.push(context);
699
- this.state.instances.push(context);
700
- this.state.results.push(result);
701
- this.state.lastCall = args;
702
- const implementation = this.#once.shift() ?? this.#implementation;
703
- try {
704
- result.value = !implementation ? void 0 : target ? Reflect.construct(implementation, args, target) : implementation.apply(context, args);
705
- result.type = "return";
706
- return result.value;
707
- } catch (error) {
708
- result.type = "throw";
709
- result.value = error;
710
- throw error;
711
- }
712
- }
713
- static #empty() {
714
- return {
715
- calls: [],
716
- results: [],
717
- instances: [],
718
- contexts: [],
719
- lastCall: void 0
738
+ Mocks = class _Mocks {
739
+ static {
740
+ __name(this, "Mocks");
741
+ }
742
+ static #all = /* @__PURE__ */ new Map();
743
+ /** A mock function, doing what an implementation does, if given. */
744
+ static fn(implementation) {
745
+ const mock = new Mock(implementation);
746
+ _Mocks.#all.set(mock.function, mock);
747
+ return mock.function;
748
+ }
749
+ /** Replaces a method with a mock that calls it, until restored. */
750
+ static spyOn(object, method) {
751
+ const original = object[method];
752
+ if (_Mocks.isMockFunction(original)) return original;
753
+ if (typeof original !== "function")
754
+ throw new TypeError(
755
+ `Cannot spy on ${String(method)}, it is ${typeof original}, not a function.`
756
+ );
757
+ const own = Object.hasOwn(object, method);
758
+ const mock = new Mock(
759
+ function(...args) {
760
+ return original.apply(this, args);
761
+ },
762
+ () => {
763
+ if (own) object[method] = original;
764
+ else delete object[method];
765
+ }
766
+ );
767
+ object[method] = mock.function;
768
+ _Mocks.#all.set(mock.function, mock);
769
+ return mock.function;
770
+ }
771
+ static isMockFunction(value) {
772
+ return typeof value === "function" && value._isMockFunction === true;
773
+ }
774
+ static clearAll() {
775
+ for (const mock of _Mocks.#all.values()) mock.clear();
776
+ }
777
+ static resetAll() {
778
+ for (const mock of _Mocks.#all.values()) mock.reset();
779
+ }
780
+ static restoreAll() {
781
+ for (const mock of _Mocks.#all.values()) mock.restore();
782
+ }
783
+ /** Forgets every mock, between test files. */
784
+ static forget() {
785
+ _Mocks.#all.clear();
786
+ }
720
787
  };
721
788
  }
722
- };
723
- var Mocks = class _Mocks {
724
- static #all = /* @__PURE__ */ new Map();
725
- /** A mock function, doing what an implementation does, if given. */
726
- static fn(implementation) {
727
- const mock = new Mock(implementation);
728
- _Mocks.#all.set(mock.function, mock);
729
- return mock.function;
730
- }
731
- /** Replaces a method with a mock that calls it, until restored. */
732
- static spyOn(object, method) {
733
- const original = object[method];
734
- if (_Mocks.isMockFunction(original)) return original;
735
- if (typeof original !== "function")
736
- throw new TypeError(
737
- `Cannot spy on ${String(method)}, it is ${typeof original}, not a function.`
738
- );
739
- const own = Object.hasOwn(object, method);
740
- const mock = new Mock(
741
- function(...args) {
742
- return original.apply(this, args);
743
- },
744
- () => {
745
- if (own) object[method] = original;
746
- else delete object[method];
747
- }
748
- );
749
- object[method] = mock.function;
750
- _Mocks.#all.set(mock.function, mock);
751
- return mock.function;
752
- }
753
- static isMockFunction(value) {
754
- return typeof value === "function" && value._isMockFunction === true;
755
- }
756
- static clearAll() {
757
- for (const mock of _Mocks.#all.values()) mock.clear();
758
- }
759
- static resetAll() {
760
- for (const mock of _Mocks.#all.values()) mock.reset();
761
- }
762
- static restoreAll() {
763
- for (const mock of _Mocks.#all.values()) mock.restore();
764
- }
765
- /** Forgets every mock, between test files. */
766
- static forget() {
767
- _Mocks.#all.clear();
768
- }
769
- };
789
+ });
770
790
 
771
791
  // packages/testero/src/expect/equality.ts
772
- var Equality = class _Equality {
773
- /** Whether a value matches by its own rule, like `expect.any(Number)`. */
774
- static asymmetric(value) {
775
- return typeof value === "object" && value !== null && typeof value.asymmetricMatch === "function";
776
- }
777
- /**
778
- * Whether two values are deeply equal. Loosely, properties set to
779
- * undefined count as missing; strictly, types and holes in arrays count.
780
- */
781
- static equals(a, b, strict = false) {
782
- return _Equality.#equals(a, b, strict, [], []);
783
- }
784
- /** Whether received has every property expected has, recursively, like `toMatchObject`. */
785
- static matches(received, expected) {
786
- if (_Equality.asymmetric(expected))
787
- return expected.asymmetricMatch(received);
788
- if (!_Equality.#plainish(expected) || typeof received !== "object" || received === null)
789
- return _Equality.equals(received, expected);
790
- if (Array.isArray(expected))
791
- return Array.isArray(received) && received.length === expected.length && expected.every(
792
- (item, index) => _Equality.matches(received[index], item)
793
- );
794
- return _Equality.#keys(expected, true).every(
795
- (key) => key in received && _Equality.matches(
796
- received[key],
797
- expected[key]
798
- )
799
- );
800
- }
801
- /** Objects and arrays that `toMatchObject` compares by their properties. */
802
- static #plainish(value) {
803
- return typeof value === "object" && value !== null && !(value instanceof Date) && !(value instanceof RegExp) && !(value instanceof Map) && !(value instanceof Set) && !(value instanceof Error);
804
- }
805
- static #equals(a, b, strict, aSeen, bSeen) {
806
- if (_Equality.asymmetric(a)) return a.asymmetricMatch(b);
807
- if (_Equality.asymmetric(b)) return b.asymmetricMatch(a);
808
- if (Object.is(a, b)) return true;
809
- if (typeof a !== "object" || typeof b !== "object" || a === null || b === null)
810
- return false;
811
- const tag = Object.prototype.toString.call(a);
812
- if (tag !== Object.prototype.toString.call(b)) return false;
813
- if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b))
814
- return false;
815
- if (a instanceof Date)
816
- return Object.is(a.getTime(), b.getTime());
817
- if (a instanceof RegExp) return String(a) === String(b);
818
- if (a instanceof Error && b instanceof Error)
819
- return a.name === b.name && a.message === b.message;
820
- if (a instanceof Number || a instanceof String || a instanceof Boolean)
821
- return Object.is(
822
- a.valueOf(),
823
- b.valueOf()
824
- );
825
- const index = aSeen.indexOf(a);
826
- if (index >= 0) return bSeen[index] === b;
827
- aSeen.push(a);
828
- bSeen.push(b);
829
- try {
830
- return _Equality.#structures(a, b, strict, aSeen, bSeen);
831
- } finally {
832
- aSeen.pop();
833
- bSeen.pop();
834
- }
835
- }
836
- static #structures(a, b, strict, aSeen, bSeen) {
837
- const equal = (x, y) => _Equality.#equals(x, y, strict, aSeen, bSeen);
838
- if (a instanceof Map && b instanceof Map)
839
- return a.size === b.size && [...a].every(
840
- ([key, value]) => b.has(key) ? equal(value, b.get(key)) : [...b].some(
841
- ([other, item]) => equal(key, other) && equal(value, item)
842
- )
843
- );
844
- if (a instanceof Set && b instanceof Set)
845
- return a.size === b.size && [...a].every(
846
- (value) => b.has(value) || [...b].some((other) => equal(value, other))
847
- );
848
- if (Array.isArray(a) || ArrayBuffer.isView(a)) {
849
- const left = a;
850
- const right = b;
851
- if (left.length !== right.length) return false;
852
- for (let index = 0; index < left.length; index++) {
853
- if (strict && index in left !== index in right) return false;
854
- if (!equal(left[index], right[index])) return false;
792
+ var Equality;
793
+ var init_equality = __esm({
794
+ "packages/testero/src/expect/equality.ts"() {
795
+ "use strict";
796
+ Equality = class _Equality {
797
+ static {
798
+ __name(this, "Equality");
855
799
  }
856
- }
857
- const indexed = Array.isArray(a) || ArrayBuffer.isView(a);
858
- const keysOf = (object) => _Equality.#keys(object, strict).filter(
859
- (key) => !(indexed && typeof key === "string" && /^\d+$/.test(key))
860
- );
861
- const aKeys = keysOf(a);
862
- const bKeys = keysOf(b);
863
- if (aKeys.length !== bKeys.length) return false;
864
- return aKeys.every(
865
- (key) => Object.hasOwn(b, key) && equal(
866
- a[key],
867
- b[key]
868
- )
869
- );
870
- }
871
- /** Own enumerable keys, without the ones set to undefined unless strict. */
872
- static #keys(object, strict) {
873
- const record = object;
874
- const keys = [
875
- ...Object.keys(object),
876
- ...Object.getOwnPropertySymbols(object).filter(
877
- (symbol) => Object.getOwnPropertyDescriptor(object, symbol)?.enumerable
878
- )
879
- ];
880
- return strict ? keys : keys.filter((key) => record[key] !== void 0);
800
+ /** Whether a value matches by its own rule, like `expect.any(Number)`. */
801
+ static asymmetric(value) {
802
+ return typeof value === "object" && value !== null && typeof value.asymmetricMatch === "function";
803
+ }
804
+ /**
805
+ * Whether two values are deeply equal. Loosely, properties set to
806
+ * undefined count as missing; strictly, types and holes in arrays count.
807
+ */
808
+ static equals(a, b, strict = false) {
809
+ return _Equality.#equals(a, b, strict, [], []);
810
+ }
811
+ /** Whether received has every property expected has, recursively, like `toMatchObject`. */
812
+ static matches(received, expected) {
813
+ if (_Equality.asymmetric(expected))
814
+ return expected.asymmetricMatch(received);
815
+ if (!_Equality.#plainish(expected) || typeof received !== "object" || received === null)
816
+ return _Equality.equals(received, expected);
817
+ if (Array.isArray(expected))
818
+ return Array.isArray(received) && received.length === expected.length && expected.every(
819
+ (item, index) => _Equality.matches(received[index], item)
820
+ );
821
+ return _Equality.#keys(expected, true).every(
822
+ (key) => key in received && _Equality.matches(
823
+ received[key],
824
+ expected[key]
825
+ )
826
+ );
827
+ }
828
+ /** Objects and arrays that `toMatchObject` compares by their properties. */
829
+ static #plainish(value) {
830
+ return typeof value === "object" && value !== null && !(value instanceof Date) && !(value instanceof RegExp) && !(value instanceof Map) && !(value instanceof Set) && !(value instanceof Error);
831
+ }
832
+ static #equals(a, b, strict, aSeen, bSeen) {
833
+ if (_Equality.asymmetric(a)) return a.asymmetricMatch(b);
834
+ if (_Equality.asymmetric(b)) return b.asymmetricMatch(a);
835
+ if (Object.is(a, b)) return true;
836
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null)
837
+ return false;
838
+ const tag = Object.prototype.toString.call(a);
839
+ if (tag !== Object.prototype.toString.call(b)) return false;
840
+ if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b))
841
+ return false;
842
+ if (a instanceof Date)
843
+ return Object.is(a.getTime(), b.getTime());
844
+ if (a instanceof RegExp) return String(a) === String(b);
845
+ if (a instanceof Error && b instanceof Error)
846
+ return a.name === b.name && a.message === b.message;
847
+ if (a instanceof Number || a instanceof String || a instanceof Boolean)
848
+ return Object.is(
849
+ a.valueOf(),
850
+ b.valueOf()
851
+ );
852
+ const index = aSeen.indexOf(a);
853
+ if (index >= 0) return bSeen[index] === b;
854
+ aSeen.push(a);
855
+ bSeen.push(b);
856
+ try {
857
+ return _Equality.#structures(a, b, strict, aSeen, bSeen);
858
+ } finally {
859
+ aSeen.pop();
860
+ bSeen.pop();
861
+ }
862
+ }
863
+ static #structures(a, b, strict, aSeen, bSeen) {
864
+ const equal = /* @__PURE__ */ __name((x, y) => _Equality.#equals(x, y, strict, aSeen, bSeen), "equal");
865
+ if (a instanceof Map && b instanceof Map)
866
+ return a.size === b.size && [...a].every(
867
+ ([key, value]) => b.has(key) ? equal(value, b.get(key)) : [...b].some(
868
+ ([other, item]) => equal(key, other) && equal(value, item)
869
+ )
870
+ );
871
+ if (a instanceof Set && b instanceof Set)
872
+ return a.size === b.size && [...a].every(
873
+ (value) => b.has(value) || [...b].some((other) => equal(value, other))
874
+ );
875
+ if (Array.isArray(a) || ArrayBuffer.isView(a)) {
876
+ const left = a;
877
+ const right = b;
878
+ if (left.length !== right.length) return false;
879
+ for (let index = 0; index < left.length; index++) {
880
+ if (strict && index in left !== index in right) return false;
881
+ if (!equal(left[index], right[index])) return false;
882
+ }
883
+ }
884
+ const indexed = Array.isArray(a) || ArrayBuffer.isView(a);
885
+ const keysOf = /* @__PURE__ */ __name((object) => _Equality.#keys(object, strict).filter(
886
+ (key) => !(indexed && typeof key === "string" && /^\d+$/.test(key))
887
+ ), "keysOf");
888
+ const aKeys = keysOf(a);
889
+ const bKeys = keysOf(b);
890
+ if (aKeys.length !== bKeys.length) return false;
891
+ return aKeys.every(
892
+ (key) => Object.hasOwn(b, key) && equal(
893
+ a[key],
894
+ b[key]
895
+ )
896
+ );
897
+ }
898
+ /** Own enumerable keys, without the ones set to undefined unless strict. */
899
+ static #keys(object, strict) {
900
+ const record = object;
901
+ const keys = [
902
+ ...Object.keys(object),
903
+ ...Object.getOwnPropertySymbols(object).filter(
904
+ (symbol) => Object.getOwnPropertyDescriptor(object, symbol)?.enumerable
905
+ )
906
+ ];
907
+ return strict ? keys : keys.filter((key) => record[key] !== void 0);
908
+ }
909
+ };
881
910
  }
882
- };
911
+ });
883
912
 
884
913
  // packages/testero/src/expect/matchers.ts
885
- var show = (value) => Format.inline(value);
886
- var hint = (context, name, expected = "expected") => `expect(received)${context.promise ? `.${context.promise}` : ""}${context.isNot ? ".not" : ""}.${name}(${expected})`;
887
- var difference = (expected, received) => {
888
- const multiline = Format.value(expected).includes("\n") || Format.value(received).includes("\n");
889
- return multiline ? Format.diff(expected, received) : `Expected: ${show(expected)}
914
+ var show, hint, difference, callsOf, listCalls, comparison, predicate, thrownMatch, property, matchers;
915
+ var init_matchers = __esm({
916
+ "packages/testero/src/expect/matchers.ts"() {
917
+ "use strict";
918
+ init_mock();
919
+ init_equality();
920
+ init_format();
921
+ show = /* @__PURE__ */ __name((value) => Format.inline(value), "show");
922
+ hint = /* @__PURE__ */ __name((context, name, expected = "expected") => `expect(received)${context.promise ? `.${context.promise}` : ""}${context.isNot ? ".not" : ""}.${name}(${expected})`, "hint");
923
+ difference = /* @__PURE__ */ __name((expected, received) => {
924
+ const multiline = Format.value(expected).includes("\n") || Format.value(received).includes("\n");
925
+ return multiline ? Format.diff(expected, received) : `Expected: ${show(expected)}
890
926
  Received: ${show(received)}`;
891
- };
892
- var callsOf = (context, name, received) => {
893
- if (!Mocks.isMockFunction(received))
894
- throw new Error(
895
- `${hint(context, name)}
927
+ }, "difference");
928
+ callsOf = /* @__PURE__ */ __name((context, name, received) => {
929
+ if (!Mocks.isMockFunction(received))
930
+ throw new Error(
931
+ `${hint(context, name)}
896
932
 
897
933
  Matcher error: received value must be a mock or spy function
898
934
 
899
935
  Received: ${show(received)}`
900
- );
901
- return received.mock;
902
- };
903
- var listCalls = (calls) => calls.length === 0 ? "Number of calls: 0" : `Received
936
+ );
937
+ return received.mock;
938
+ }, "callsOf");
939
+ listCalls = /* @__PURE__ */ __name((calls) => calls.length === 0 ? "Number of calls: 0" : `Received
904
940
  ${calls.map(
905
- (call, index) => ` ${index + 1}: ${call.map(show).join(", ")}`
906
- ).join("\n")}
941
+ (call, index) => ` ${index + 1}: ${call.map(show).join(", ")}`
942
+ ).join("\n")}
907
943
 
908
- Number of calls: ${calls.length}`;
909
- var comparison = (name, operator, test) => function(received, expected) {
910
- if (typeof received !== "number" && typeof received !== "bigint")
911
- throw new Error(
912
- `${hint(this, name)}
944
+ Number of calls: ${calls.length}`, "listCalls");
945
+ comparison = /* @__PURE__ */ __name((name, operator, test) => function(received, expected) {
946
+ if (typeof received !== "number" && typeof received !== "bigint")
947
+ throw new Error(
948
+ `${hint(this, name)}
913
949
 
914
950
  Matcher error: received value must be a number or bigint
915
951
 
916
952
  Received: ${show(received)}`
917
- );
918
- return {
919
- pass: test(received, expected),
920
- message: () => `${hint(this, name)}
953
+ );
954
+ return {
955
+ pass: test(received, expected),
956
+ message: /* @__PURE__ */ __name(() => `${hint(this, name)}
921
957
 
922
958
  Expected:${this.isNot ? " not" : ""} ${operator} ${show(expected)}
923
- Received:${" ".repeat(operator.length + (this.isNot ? 6 : 2))}${show(received)}`
924
- };
925
- };
926
- var predicate = (name, test) => function(received) {
927
- return {
928
- pass: test(received),
929
- message: () => `${hint(this, name, "")}
959
+ Received:${" ".repeat(operator.length + (this.isNot ? 6 : 2))}${show(received)}`, "message")
960
+ };
961
+ }, "comparison");
962
+ predicate = /* @__PURE__ */ __name((name, test) => function(received) {
963
+ return {
964
+ pass: test(received),
965
+ message: /* @__PURE__ */ __name(() => `${hint(this, name, "")}
930
966
 
931
- Received: ${show(received)}`
932
- };
933
- };
934
- var thrownMatch = (expected, thrown) => {
935
- const message = thrown instanceof Error ? thrown.message : String(thrown);
936
- const received = `Received message: ${JSON.stringify(message)}`;
937
- if (expected === void 0) return { pass: true, expected: "", received };
938
- if (typeof expected === "string")
939
- return {
940
- pass: message.includes(expected),
941
- expected: `Expected substring: ${JSON.stringify(expected)}`,
942
- received
943
- };
944
- if (expected instanceof RegExp)
945
- return {
946
- pass: expected.test(message),
947
- expected: `Expected pattern: ${expected}`,
948
- received
949
- };
950
- if (typeof expected === "function")
951
- return {
952
- pass: thrown instanceof expected,
953
- expected: `Expected constructor: ${expected.name}`,
954
- received: `Received constructor: ${thrown?.constructor?.name ?? show(thrown)}
967
+ Received: ${show(received)}`, "message")
968
+ };
969
+ }, "predicate");
970
+ thrownMatch = /* @__PURE__ */ __name((expected, thrown) => {
971
+ const message = thrown instanceof Error ? thrown.message : String(thrown);
972
+ const received = `Received message: ${JSON.stringify(message)}`;
973
+ if (expected === void 0) return { pass: true, expected: "", received };
974
+ if (typeof expected === "string")
975
+ return {
976
+ pass: message.includes(expected),
977
+ expected: `Expected substring: ${JSON.stringify(expected)}`,
978
+ received
979
+ };
980
+ if (expected instanceof RegExp)
981
+ return {
982
+ pass: expected.test(message),
983
+ expected: `Expected pattern: ${expected}`,
984
+ received
985
+ };
986
+ if (typeof expected === "function")
987
+ return {
988
+ pass: thrown instanceof expected,
989
+ expected: `Expected constructor: ${expected.name}`,
990
+ received: `Received constructor: ${thrown?.constructor?.name ?? show(thrown)}
955
991
  ${received}`
956
- };
957
- if (Equality.asymmetric(expected))
958
- return {
959
- pass: expected.asymmetricMatch(thrown),
960
- expected: `Expected asymmetric matcher: ${String(expected)}`,
961
- received: `Received value: ${show(thrown)}`
962
- };
963
- if (expected instanceof Error)
964
- return {
965
- pass: message === expected.message,
966
- expected: `Expected message: ${JSON.stringify(expected.message)}`,
967
- received
968
- };
969
- throw new Error(
970
- `Matcher error: expected value must be a string, regular expression, class, error or asymmetric matcher
992
+ };
993
+ if (Equality.asymmetric(expected))
994
+ return {
995
+ pass: expected.asymmetricMatch(thrown),
996
+ expected: `Expected asymmetric matcher: ${String(expected)}`,
997
+ received: `Received value: ${show(thrown)}`
998
+ };
999
+ if (expected instanceof Error)
1000
+ return {
1001
+ pass: message === expected.message,
1002
+ expected: `Expected message: ${JSON.stringify(expected.message)}`,
1003
+ received
1004
+ };
1005
+ throw new Error(
1006
+ `Matcher error: expected value must be a string, regular expression, class, error or asymmetric matcher
971
1007
 
972
1008
  Expected: ${show(expected)}`
973
- );
974
- };
975
- var property = (object, path) => {
976
- const keys = typeof path === "string" ? path.split(/[.[\]]/).filter((key) => key !== "") : path;
977
- let current = object;
978
- const found = [];
979
- for (const key of keys) {
980
- if (current === null || current === void 0 || !(key in Object(current)))
981
- return { has: false, value: current, found };
982
- current = current[key];
983
- found.push(key);
984
- }
985
- return { has: true, value: current, found };
986
- };
987
- var matchers = {
988
- toBe(received, expected) {
989
- const pass = Object.is(received, expected);
990
- return {
991
- pass,
992
- message: () => {
993
- const header = `${hint(this, "toBe")} // Object.is equality
1009
+ );
1010
+ }, "thrownMatch");
1011
+ property = /* @__PURE__ */ __name((object, path) => {
1012
+ const keys = typeof path === "string" ? path.split(/[.[\]]/).filter((key) => key !== "") : path;
1013
+ let current = object;
1014
+ const found = [];
1015
+ for (const key of keys) {
1016
+ if (current === null || current === void 0 || !(key in Object(current)))
1017
+ return { has: false, value: current, found };
1018
+ current = current[key];
1019
+ found.push(key);
1020
+ }
1021
+ return { has: true, value: current, found };
1022
+ }, "property");
1023
+ matchers = {
1024
+ toBe(received, expected) {
1025
+ const pass = Object.is(received, expected);
1026
+ return {
1027
+ pass,
1028
+ message: /* @__PURE__ */ __name(() => {
1029
+ const header = `${hint(this, "toBe")} // Object.is equality
994
1030
 
995
1031
  `;
996
- if (this.isNot)
997
- return `${header}Expected: not ${show(expected)}`;
998
- const deep = Equality.equals(received, expected, true) ? '\n\nIf it should pass with deep equality, replace "toBe" with "toStrictEqual"' : "";
999
- return `${header}${difference(expected, received)}${deep}`;
1000
- }
1001
- };
1002
- },
1003
- toEqual(received, expected) {
1004
- return {
1005
- pass: Equality.equals(received, expected),
1006
- message: () => `${hint(this, "toEqual")} // deep equality
1032
+ if (this.isNot)
1033
+ return `${header}Expected: not ${show(expected)}`;
1034
+ const deep = Equality.equals(received, expected, true) ? '\n\nIf it should pass with deep equality, replace "toBe" with "toStrictEqual"' : "";
1035
+ return `${header}${difference(expected, received)}${deep}`;
1036
+ }, "message")
1037
+ };
1038
+ },
1039
+ toEqual(received, expected) {
1040
+ return {
1041
+ pass: Equality.equals(received, expected),
1042
+ message: /* @__PURE__ */ __name(() => `${hint(this, "toEqual")} // deep equality
1007
1043
 
1008
- ${this.isNot ? `Expected: not ${show(expected)}` : difference(expected, received)}`
1009
- };
1010
- },
1011
- toStrictEqual(received, expected) {
1012
- return {
1013
- pass: Equality.equals(received, expected, true),
1014
- message: () => `${hint(this, "toStrictEqual")} // deep equality
1044
+ ${this.isNot ? `Expected: not ${show(expected)}` : difference(expected, received)}`, "message")
1045
+ };
1046
+ },
1047
+ toStrictEqual(received, expected) {
1048
+ return {
1049
+ pass: Equality.equals(received, expected, true),
1050
+ message: /* @__PURE__ */ __name(() => `${hint(this, "toStrictEqual")} // deep equality
1015
1051
 
1016
- ${this.isNot ? `Expected: not ${show(expected)}` : difference(expected, received)}`
1017
- };
1018
- },
1019
- toMatchObject(received, expected) {
1020
- if (typeof received !== "object" || received === null)
1021
- throw new Error(
1022
- `${hint(this, "toMatchObject")}
1052
+ ${this.isNot ? `Expected: not ${show(expected)}` : difference(expected, received)}`, "message")
1053
+ };
1054
+ },
1055
+ toMatchObject(received, expected) {
1056
+ if (typeof received !== "object" || received === null)
1057
+ throw new Error(
1058
+ `${hint(this, "toMatchObject")}
1023
1059
 
1024
1060
  Matcher error: received value must be a non-null object
1025
1061
 
1026
1062
  Received: ${show(received)}`
1027
- );
1028
- return {
1029
- pass: Equality.matches(received, expected),
1030
- message: () => `${hint(this, "toMatchObject")}
1063
+ );
1064
+ return {
1065
+ pass: Equality.matches(received, expected),
1066
+ message: /* @__PURE__ */ __name(() => `${hint(this, "toMatchObject")}
1031
1067
 
1032
- ${this.isNot ? `Expected: not ${show(expected)}` : difference(expected, received)}`
1033
- };
1034
- },
1035
- toBeDefined: predicate("toBeDefined", (value) => value !== void 0),
1036
- toBeUndefined: predicate("toBeUndefined", (value) => value === void 0),
1037
- toBeNull: predicate("toBeNull", (value) => value === null),
1038
- toBeNaN: predicate("toBeNaN", (value) => Number.isNaN(value)),
1039
- toBeTruthy: predicate("toBeTruthy", (value) => Boolean(value)),
1040
- toBeFalsy: predicate("toBeFalsy", (value) => !value),
1041
- toBeGreaterThan: comparison("toBeGreaterThan", ">", (a, b) => a > b),
1042
- toBeGreaterThanOrEqual: comparison(
1043
- "toBeGreaterThanOrEqual",
1044
- ">=",
1045
- (a, b) => a >= b
1046
- ),
1047
- toBeLessThan: comparison("toBeLessThan", "<", (a, b) => a < b),
1048
- toBeLessThanOrEqual: comparison(
1049
- "toBeLessThanOrEqual",
1050
- "<=",
1051
- (a, b) => a <= b
1052
- ),
1053
- toBeCloseTo(received, expected, digits = 2) {
1054
- const value = received;
1055
- const pass = value === expected || Math.abs(expected - value) < 10 ** -digits / 2;
1056
- return {
1057
- pass,
1058
- message: () => `${hint(this, "toBeCloseTo", "expected, precision")}
1068
+ ${this.isNot ? `Expected: not ${show(expected)}` : difference(expected, received)}`, "message")
1069
+ };
1070
+ },
1071
+ toBeDefined: predicate("toBeDefined", (value) => value !== void 0),
1072
+ toBeUndefined: predicate("toBeUndefined", (value) => value === void 0),
1073
+ toBeNull: predicate("toBeNull", (value) => value === null),
1074
+ toBeNaN: predicate("toBeNaN", (value) => Number.isNaN(value)),
1075
+ toBeTruthy: predicate("toBeTruthy", (value) => Boolean(value)),
1076
+ toBeFalsy: predicate("toBeFalsy", (value) => !value),
1077
+ toBeGreaterThan: comparison("toBeGreaterThan", ">", (a, b) => a > b),
1078
+ toBeGreaterThanOrEqual: comparison(
1079
+ "toBeGreaterThanOrEqual",
1080
+ ">=",
1081
+ (a, b) => a >= b
1082
+ ),
1083
+ toBeLessThan: comparison("toBeLessThan", "<", (a, b) => a < b),
1084
+ toBeLessThanOrEqual: comparison(
1085
+ "toBeLessThanOrEqual",
1086
+ "<=",
1087
+ (a, b) => a <= b
1088
+ ),
1089
+ toBeCloseTo(received, expected, digits = 2) {
1090
+ const value = received;
1091
+ const pass = value === expected || Math.abs(expected - value) < 10 ** -digits / 2;
1092
+ return {
1093
+ pass,
1094
+ message: /* @__PURE__ */ __name(() => `${hint(this, "toBeCloseTo", "expected, precision")}
1059
1095
 
1060
1096
  Expected: ${this.isNot ? "not " : ""}${show(expected)}
1061
1097
  Received: ${show(received)}
1062
1098
 
1063
1099
  Expected precision: ${digits}
1064
1100
  Expected difference: < ${10 ** -digits / 2}
1065
- Received difference: ${Math.abs(expected - value)}`
1066
- };
1067
- },
1068
- toBeInstanceOf(received, expected) {
1069
- return {
1070
- pass: received instanceof expected,
1071
- message: () => `${hint(this, "toBeInstanceOf")}
1101
+ Received difference: ${Math.abs(expected - value)}`, "message")
1102
+ };
1103
+ },
1104
+ toBeInstanceOf(received, expected) {
1105
+ return {
1106
+ pass: received instanceof expected,
1107
+ message: /* @__PURE__ */ __name(() => `${hint(this, "toBeInstanceOf")}
1072
1108
 
1073
1109
  Expected constructor: ${this.isNot ? "not " : ""}${expected.name}
1074
- Received constructor: ${received?.constructor?.name ?? show(received)}`
1075
- };
1076
- },
1077
- toContain(received, expected) {
1078
- const text = typeof received === "string";
1079
- if (!text && (received === null || typeof received?.[Symbol.iterator] !== "function"))
1080
- throw new Error(
1081
- `${hint(this, "toContain")}
1110
+ Received constructor: ${received?.constructor?.name ?? show(received)}`, "message")
1111
+ };
1112
+ },
1113
+ toContain(received, expected) {
1114
+ const text = typeof received === "string";
1115
+ if (!text && (received === null || typeof received?.[Symbol.iterator] !== "function"))
1116
+ throw new Error(
1117
+ `${hint(this, "toContain")}
1082
1118
 
1083
1119
  Matcher error: received value must not be null nor undefined, and must be iterable
1084
1120
 
1085
1121
  Received: ${show(received)}`
1086
- );
1087
- const pass = text ? received.includes(String(expected)) : [...received].includes(expected);
1088
- return {
1089
- pass,
1090
- message: () => `${hint(this, "toContain")}
1122
+ );
1123
+ const pass = text ? received.includes(String(expected)) : [...received].includes(expected);
1124
+ return {
1125
+ pass,
1126
+ message: /* @__PURE__ */ __name(() => `${hint(this, "toContain")}
1091
1127
 
1092
1128
  Expected ${text ? "substring" : "value"}: ${this.isNot ? "not " : ""}${show(expected)}
1093
- Received ${text ? "string" : "value"}:${text ? " " : " "}${show(received)}`
1094
- };
1095
- },
1096
- toContainEqual(received, expected) {
1097
- const items = [...received];
1098
- return {
1099
- pass: items.some((item) => Equality.equals(item, expected)),
1100
- message: () => `${hint(this, "toContainEqual")}
1129
+ Received ${text ? "string" : "value"}:${text ? " " : " "}${show(received)}`, "message")
1130
+ };
1131
+ },
1132
+ toContainEqual(received, expected) {
1133
+ const items = [...received];
1134
+ return {
1135
+ pass: items.some((item) => Equality.equals(item, expected)),
1136
+ message: /* @__PURE__ */ __name(() => `${hint(this, "toContainEqual")}
1101
1137
 
1102
1138
  Expected value: ${this.isNot ? "not " : ""}${show(expected)}
1103
- Received value: ${show(received)}`
1104
- };
1105
- },
1106
- toHaveLength(received, expected) {
1107
- const length = received?.length;
1108
- if (typeof length !== "number")
1109
- throw new Error(
1110
- `${hint(this, "toHaveLength")}
1139
+ Received value: ${show(received)}`, "message")
1140
+ };
1141
+ },
1142
+ toHaveLength(received, expected) {
1143
+ const length = received?.length;
1144
+ if (typeof length !== "number")
1145
+ throw new Error(
1146
+ `${hint(this, "toHaveLength")}
1111
1147
 
1112
1148
  Matcher error: received value must have a length property whose value must be a number
1113
1149
 
1114
1150
  Received: ${show(received)}`
1115
- );
1116
- return {
1117
- pass: length === expected,
1118
- message: () => `${hint(this, "toHaveLength")}
1151
+ );
1152
+ return {
1153
+ pass: length === expected,
1154
+ message: /* @__PURE__ */ __name(() => `${hint(this, "toHaveLength")}
1119
1155
 
1120
1156
  Expected length: ${this.isNot ? "not " : ""}${expected}
1121
1157
  Received length: ${length}
1122
- Received value: ${show(received)}`
1123
- };
1124
- },
1125
- toHaveProperty(received, path, ...value) {
1126
- const { has, value: actual, found } = property(received, path);
1127
- const checksValue = value.length > 0;
1128
- const pass = has && (!checksValue || Equality.equals(actual, value[0]));
1129
- const shown = typeof path === "string" ? path : path.map(String).join(".");
1130
- return {
1131
- pass,
1132
- message: () => {
1133
- const header = `${hint(this, "toHaveProperty", checksValue ? "path, value" : "path")}
1158
+ Received value: ${show(received)}`, "message")
1159
+ };
1160
+ },
1161
+ toHaveProperty(received, path, ...value) {
1162
+ const { has, value: actual, found } = property(received, path);
1163
+ const checksValue = value.length > 0;
1164
+ const pass = has && (!checksValue || Equality.equals(actual, value[0]));
1165
+ const shown = typeof path === "string" ? path : path.map(String).join(".");
1166
+ return {
1167
+ pass,
1168
+ message: /* @__PURE__ */ __name(() => {
1169
+ const header = `${hint(this, "toHaveProperty", checksValue ? "path, value" : "path")}
1134
1170
 
1135
1171
  Expected path: ${JSON.stringify(shown)}
1136
1172
  `;
1137
- if (!has)
1138
- return `${header}Received path: ${JSON.stringify(found.map(String).join("."))}
1173
+ if (!has)
1174
+ return `${header}Received path: ${JSON.stringify(found.map(String).join("."))}
1139
1175
 
1140
1176
  Received value: ${show(actual)}`;
1141
- return checksValue ? `${header}
1177
+ return checksValue ? `${header}
1142
1178
  ${this.isNot ? `Expected value: not ${show(value[0])}` : difference(value[0], actual)}` : `${header}
1143
1179
  Received value: ${show(actual)}`;
1144
- }
1145
- };
1146
- },
1147
- toMatch(received, expected) {
1148
- if (typeof received !== "string")
1149
- throw new Error(
1150
- `${hint(this, "toMatch")}
1180
+ }, "message")
1181
+ };
1182
+ },
1183
+ toMatch(received, expected) {
1184
+ if (typeof received !== "string")
1185
+ throw new Error(
1186
+ `${hint(this, "toMatch")}
1151
1187
 
1152
1188
  Matcher error: received value must be a string
1153
1189
 
1154
1190
  Received: ${show(received)}`
1155
- );
1156
- const pattern = typeof expected === "string";
1157
- return {
1158
- pass: pattern ? received.includes(expected) : expected.test(received),
1159
- message: () => `${hint(this, "toMatch")}
1191
+ );
1192
+ const pattern = typeof expected === "string";
1193
+ return {
1194
+ pass: pattern ? received.includes(expected) : expected.test(received),
1195
+ message: /* @__PURE__ */ __name(() => `${hint(this, "toMatch")}
1160
1196
 
1161
1197
  Expected ${pattern ? "substring" : "pattern"}: ${this.isNot ? "not " : ""}${pattern ? show(expected) : String(expected)}
1162
- Received string:${pattern ? " " : " "}${show(received)}`
1163
- };
1164
- },
1165
- toThrow(received, expected) {
1166
- let thrown;
1167
- if (this.promise === "rejects") thrown = { value: received };
1168
- else if (typeof received !== "function")
1169
- throw new Error(
1170
- `${hint(this, "toThrow")}
1198
+ Received string:${pattern ? " " : " "}${show(received)}`, "message")
1199
+ };
1200
+ },
1201
+ toThrow(received, expected) {
1202
+ let thrown;
1203
+ if (this.promise === "rejects") thrown = { value: received };
1204
+ else if (typeof received !== "function")
1205
+ throw new Error(
1206
+ `${hint(this, "toThrow")}
1171
1207
 
1172
1208
  Matcher error: received value must be a function
1173
1209
 
1174
1210
  Received: ${show(received)}`
1175
- );
1176
- else
1177
- try {
1178
- received();
1179
- } catch (error) {
1180
- thrown = { value: error };
1181
- }
1182
- const match = thrown ? thrownMatch(expected, thrown.value) : void 0;
1183
- const name = expected === void 0 ? "" : "expected";
1184
- return {
1185
- pass: match?.pass ?? false,
1186
- message: () => {
1187
- const header = `${hint(this, "toThrow", name)}
1211
+ );
1212
+ else
1213
+ try {
1214
+ received();
1215
+ } catch (error) {
1216
+ thrown = { value: error };
1217
+ }
1218
+ const match = thrown ? thrownMatch(expected, thrown.value) : void 0;
1219
+ const name = expected === void 0 ? "" : "expected";
1220
+ return {
1221
+ pass: match?.pass ?? false,
1222
+ message: /* @__PURE__ */ __name(() => {
1223
+ const header = `${hint(this, "toThrow", name)}
1188
1224
 
1189
1225
  `;
1190
- if (!thrown || !match)
1191
- return `${header}${expected === void 0 ? "" : `${thrownMatch(expected, void 0).expected}
1226
+ if (!thrown || !match)
1227
+ return `${header}${expected === void 0 ? "" : `${thrownMatch(expected, void 0).expected}
1192
1228
 
1193
1229
  `}Received function did not throw`;
1194
- if (this.isNot && expected === void 0)
1195
- return `${header}Error name: ${thrown.value?.name ?? "thrown"}
1230
+ if (this.isNot && expected === void 0)
1231
+ return `${header}Error name: ${thrown.value?.name ?? "thrown"}
1196
1232
  ${match.received}`;
1197
- return `${header}${match.expected.replace(": ", this.isNot ? ": not " : ": ")}
1233
+ return `${header}${match.expected.replace(": ", this.isNot ? ": not " : ": ")}
1198
1234
  ${match.received}`;
1199
- }
1200
- };
1201
- },
1202
- toHaveBeenCalled(received) {
1203
- const { calls } = callsOf(this, "toHaveBeenCalled", received);
1204
- return {
1205
- pass: calls.length > 0,
1206
- message: () => `${hint(this, "toHaveBeenCalled", "")}
1235
+ }, "message")
1236
+ };
1237
+ },
1238
+ toHaveBeenCalled(received) {
1239
+ const { calls } = callsOf(this, "toHaveBeenCalled", received);
1240
+ return {
1241
+ pass: calls.length > 0,
1242
+ message: /* @__PURE__ */ __name(() => `${hint(this, "toHaveBeenCalled", "")}
1207
1243
 
1208
1244
  Expected number of calls: ${this.isNot ? "0" : ">= 1"}
1209
- ${listCalls(calls)}`
1210
- };
1211
- },
1212
- toHaveBeenCalledTimes(received, expected) {
1213
- const { calls } = callsOf(this, "toHaveBeenCalledTimes", received);
1214
- return {
1215
- pass: calls.length === expected,
1216
- message: () => `${hint(this, "toHaveBeenCalledTimes")}
1245
+ ${listCalls(calls)}`, "message")
1246
+ };
1247
+ },
1248
+ toHaveBeenCalledTimes(received, expected) {
1249
+ const { calls } = callsOf(this, "toHaveBeenCalledTimes", received);
1250
+ return {
1251
+ pass: calls.length === expected,
1252
+ message: /* @__PURE__ */ __name(() => `${hint(this, "toHaveBeenCalledTimes")}
1217
1253
 
1218
1254
  Expected number of calls: ${this.isNot ? "not " : ""}${expected}
1219
- Received number of calls: ${calls.length}`
1220
- };
1221
- },
1222
- toHaveBeenCalledWith(received, ...expected) {
1223
- const { calls } = callsOf(this, "toHaveBeenCalledWith", received);
1224
- return {
1225
- pass: calls.some((call) => Equality.equals(call, expected)),
1226
- message: () => `${hint(this, "toHaveBeenCalledWith", "...expected")}
1255
+ Received number of calls: ${calls.length}`, "message")
1256
+ };
1257
+ },
1258
+ toHaveBeenCalledWith(received, ...expected) {
1259
+ const { calls } = callsOf(this, "toHaveBeenCalledWith", received);
1260
+ return {
1261
+ pass: calls.some((call) => Equality.equals(call, expected)),
1262
+ message: /* @__PURE__ */ __name(() => `${hint(this, "toHaveBeenCalledWith", "...expected")}
1227
1263
 
1228
1264
  Expected: ${this.isNot ? "not " : ""}${expected.map(show).join(", ")}
1229
- ${listCalls(calls)}`
1230
- };
1231
- },
1232
- toHaveBeenLastCalledWith(received, ...expected) {
1233
- const { calls } = callsOf(this, "toHaveBeenLastCalledWith", received);
1234
- const last = calls.at(-1);
1235
- return {
1236
- pass: last !== void 0 && Equality.equals(last, expected),
1237
- message: () => `${hint(this, "toHaveBeenLastCalledWith", "...expected")}
1265
+ ${listCalls(calls)}`, "message")
1266
+ };
1267
+ },
1268
+ toHaveBeenLastCalledWith(received, ...expected) {
1269
+ const { calls } = callsOf(this, "toHaveBeenLastCalledWith", received);
1270
+ const last = calls.at(-1);
1271
+ return {
1272
+ pass: last !== void 0 && Equality.equals(last, expected),
1273
+ message: /* @__PURE__ */ __name(() => `${hint(this, "toHaveBeenLastCalledWith", "...expected")}
1238
1274
 
1239
1275
  Expected: ${this.isNot ? "not " : ""}${expected.map(show).join(", ")}
1240
- ${listCalls(calls)}`
1241
- };
1242
- },
1243
- toHaveBeenNthCalledWith(received, nth, ...expected) {
1244
- const { calls } = callsOf(this, "toHaveBeenNthCalledWith", received);
1245
- const call = calls[nth - 1];
1246
- return {
1247
- pass: call !== void 0 && Equality.equals(call, expected),
1248
- message: () => `${hint(this, "toHaveBeenNthCalledWith", "n, ...expected")}
1276
+ ${listCalls(calls)}`, "message")
1277
+ };
1278
+ },
1279
+ toHaveBeenNthCalledWith(received, nth, ...expected) {
1280
+ const { calls } = callsOf(this, "toHaveBeenNthCalledWith", received);
1281
+ const call = calls[nth - 1];
1282
+ return {
1283
+ pass: call !== void 0 && Equality.equals(call, expected),
1284
+ message: /* @__PURE__ */ __name(() => `${hint(this, "toHaveBeenNthCalledWith", "n, ...expected")}
1249
1285
 
1250
1286
  n: ${nth}
1251
1287
  Expected: ${this.isNot ? "not " : ""}${expected.map(show).join(", ")}
1252
- ${listCalls(calls)}`
1253
- };
1254
- },
1255
- toHaveReturned(received) {
1256
- const { results } = callsOf(this, "toHaveReturned", received);
1257
- const returns = results.filter((result) => result.type === "return");
1258
- return {
1259
- pass: returns.length > 0,
1260
- message: () => `${hint(this, "toHaveReturned", "")}
1288
+ ${listCalls(calls)}`, "message")
1289
+ };
1290
+ },
1291
+ toHaveReturned(received) {
1292
+ const { results } = callsOf(this, "toHaveReturned", received);
1293
+ const returns = results.filter((result) => result.type === "return");
1294
+ return {
1295
+ pass: returns.length > 0,
1296
+ message: /* @__PURE__ */ __name(() => `${hint(this, "toHaveReturned", "")}
1261
1297
 
1262
1298
  Expected number of returns: ${this.isNot ? "0" : ">= 1"}
1263
- Received number of returns: ${returns.length}`
1264
- };
1265
- },
1266
- toHaveReturnedTimes(received, expected) {
1267
- const { results } = callsOf(this, "toHaveReturnedTimes", received);
1268
- const returns = results.filter((result) => result.type === "return");
1269
- return {
1270
- pass: returns.length === expected,
1271
- message: () => `${hint(this, "toHaveReturnedTimes")}
1299
+ Received number of returns: ${returns.length}`, "message")
1300
+ };
1301
+ },
1302
+ toHaveReturnedTimes(received, expected) {
1303
+ const { results } = callsOf(this, "toHaveReturnedTimes", received);
1304
+ const returns = results.filter((result) => result.type === "return");
1305
+ return {
1306
+ pass: returns.length === expected,
1307
+ message: /* @__PURE__ */ __name(() => `${hint(this, "toHaveReturnedTimes")}
1272
1308
 
1273
1309
  Expected number of returns: ${this.isNot ? "not " : ""}${expected}
1274
- Received number of returns: ${returns.length}`
1275
- };
1276
- },
1277
- toHaveReturnedWith(received, expected) {
1278
- const { results } = callsOf(this, "toHaveReturnedWith", received);
1279
- return {
1280
- pass: results.some(
1281
- (result) => result.type === "return" && Equality.equals(result.value, expected)
1282
- ),
1283
- message: () => `${hint(this, "toHaveReturnedWith")}
1310
+ Received number of returns: ${returns.length}`, "message")
1311
+ };
1312
+ },
1313
+ toHaveReturnedWith(received, expected) {
1314
+ const { results } = callsOf(this, "toHaveReturnedWith", received);
1315
+ return {
1316
+ pass: results.some(
1317
+ (result) => result.type === "return" && Equality.equals(result.value, expected)
1318
+ ),
1319
+ message: /* @__PURE__ */ __name(() => `${hint(this, "toHaveReturnedWith")}
1284
1320
 
1285
1321
  Expected: ${this.isNot ? "not " : ""}${show(expected)}
1286
- Received: ${results.map((result) => show(result.value)).join(", ")}`
1287
- };
1288
- },
1289
- toHaveLastReturnedWith(received, expected) {
1290
- const { results } = callsOf(this, "toHaveLastReturnedWith", received);
1291
- const last = results.at(-1);
1292
- return {
1293
- pass: last?.type === "return" && Equality.equals(last.value, expected),
1294
- message: () => `${hint(this, "toHaveLastReturnedWith")}
1322
+ Received: ${results.map((result) => show(result.value)).join(", ")}`, "message")
1323
+ };
1324
+ },
1325
+ toHaveLastReturnedWith(received, expected) {
1326
+ const { results } = callsOf(this, "toHaveLastReturnedWith", received);
1327
+ const last = results.at(-1);
1328
+ return {
1329
+ pass: last?.type === "return" && Equality.equals(last.value, expected),
1330
+ message: /* @__PURE__ */ __name(() => `${hint(this, "toHaveLastReturnedWith")}
1295
1331
 
1296
1332
  Expected: ${this.isNot ? "not " : ""}${show(expected)}
1297
- Received: ${last ? show(last.value) : "no calls"}`
1298
- };
1299
- },
1300
- toHaveNthReturnedWith(received, nth, expected) {
1301
- const { results } = callsOf(this, "toHaveNthReturnedWith", received);
1302
- const result = results[nth - 1];
1303
- return {
1304
- pass: result?.type === "return" && Equality.equals(result.value, expected),
1305
- message: () => `${hint(this, "toHaveNthReturnedWith", "n, expected")}
1333
+ Received: ${last ? show(last.value) : "no calls"}`, "message")
1334
+ };
1335
+ },
1336
+ toHaveNthReturnedWith(received, nth, expected) {
1337
+ const { results } = callsOf(this, "toHaveNthReturnedWith", received);
1338
+ const result = results[nth - 1];
1339
+ return {
1340
+ pass: result?.type === "return" && Equality.equals(result.value, expected),
1341
+ message: /* @__PURE__ */ __name(() => `${hint(this, "toHaveNthReturnedWith", "n, expected")}
1306
1342
 
1307
1343
  n: ${nth}
1308
1344
  Expected: ${this.isNot ? "not " : ""}${show(expected)}
1309
- Received: ${result ? show(result.value) : "no call"}`
1345
+ Received: ${result ? show(result.value) : "no call"}`, "message")
1346
+ };
1347
+ }
1310
1348
  };
1311
1349
  }
1312
- };
1350
+ });
1313
1351
 
1314
1352
  // packages/testero/src/expect/assertion.ts
1315
- var AssertionError = class extends Error {
1316
- name = "AssertionError";
1317
- };
1318
- var Assertion = class _Assertion {
1319
- /** Assertions made since the last reset, for `expect.assertions(n)`. */
1320
- static count = 0;
1321
- #received;
1322
- #context;
1323
- constructor(received, context = { isNot: false, promise: "" }) {
1324
- this.#received = received;
1325
- this.#context = context;
1326
- }
1327
- /** The same assertion, expecting the opposite. */
1328
- get not() {
1329
- return new _Assertion(this.#received, {
1330
- ...this.#context,
1331
- isNot: !this.#context.isNot
1332
- });
1333
- }
1334
- /** The assertion on what a promise resolves to, its matchers return promises. */
1335
- get resolves() {
1336
- return new _Assertion(this.#received, {
1337
- ...this.#context,
1338
- promise: "resolves"
1339
- });
1340
- }
1341
- /** The assertion on why a promise rejects, its matchers return promises. */
1342
- get rejects() {
1343
- return new _Assertion(this.#received, {
1344
- ...this.#context,
1345
- promise: "rejects"
1346
- });
1347
- }
1348
- /** Adds a matcher to every assertion, like `expect.extend`. */
1349
- static define(name, matcher) {
1350
- matchers[name] = matcher;
1351
- Object.defineProperty(_Assertion.prototype, name, {
1352
- configurable: true,
1353
- writable: true,
1354
- value: function check(...args) {
1355
- return this.#run(name, args, check);
1356
- }
1357
- });
1358
- }
1359
- /** Runs a matcher now, or once the promise settles for `.resolves` and `.rejects`. */
1360
- #run(name, args, caller) {
1361
- _Assertion.count++;
1362
- const origin = {};
1363
- Error.captureStackTrace(origin, caller);
1364
- const { promise } = this.#context;
1365
- if (!promise) return this.#check(name, this.#received, args, origin);
1366
- const settled = (value, resolved) => {
1367
- if (resolved === (promise === "resolves"))
1368
- return this.#check(name, value, args, origin);
1369
- throw _Assertion.#error(
1370
- `expect(received).${promise}.${name}()
1353
+ var AssertionError, Assertion;
1354
+ var init_assertion = __esm({
1355
+ "packages/testero/src/expect/assertion.ts"() {
1356
+ "use strict";
1357
+ init_matchers();
1358
+ AssertionError = class extends Error {
1359
+ static {
1360
+ __name(this, "AssertionError");
1361
+ }
1362
+ name = "AssertionError";
1363
+ };
1364
+ Assertion = class _Assertion {
1365
+ static {
1366
+ __name(this, "Assertion");
1367
+ }
1368
+ /** Assertions made since the last reset, for `expect.assertions(n)`. */
1369
+ static count = 0;
1370
+ #received;
1371
+ #context;
1372
+ constructor(received, context = { isNot: false, promise: "" }) {
1373
+ this.#received = received;
1374
+ this.#context = context;
1375
+ }
1376
+ /** The same assertion, expecting the opposite. */
1377
+ get not() {
1378
+ return new _Assertion(this.#received, {
1379
+ ...this.#context,
1380
+ isNot: !this.#context.isNot
1381
+ });
1382
+ }
1383
+ /** The assertion on what a promise resolves to, its matchers return promises. */
1384
+ get resolves() {
1385
+ return new _Assertion(this.#received, {
1386
+ ...this.#context,
1387
+ promise: "resolves"
1388
+ });
1389
+ }
1390
+ /** The assertion on why a promise rejects, its matchers return promises. */
1391
+ get rejects() {
1392
+ return new _Assertion(this.#received, {
1393
+ ...this.#context,
1394
+ promise: "rejects"
1395
+ });
1396
+ }
1397
+ /** Adds a matcher to every assertion, like `expect.extend`. */
1398
+ static define(name, matcher) {
1399
+ matchers[name] = matcher;
1400
+ Object.defineProperty(_Assertion.prototype, name, {
1401
+ configurable: true,
1402
+ writable: true,
1403
+ value: /* @__PURE__ */ __name(function check(...args) {
1404
+ return this.#run(name, args, check);
1405
+ }, "check")
1406
+ });
1407
+ }
1408
+ /** Runs a matcher now, or once the promise settles for `.resolves` and `.rejects`. */
1409
+ #run(name, args, caller) {
1410
+ _Assertion.count++;
1411
+ const origin = {};
1412
+ Error.captureStackTrace(origin, caller);
1413
+ const { promise } = this.#context;
1414
+ if (!promise) return this.#check(name, this.#received, args, origin);
1415
+ const settled = /* @__PURE__ */ __name((value, resolved) => {
1416
+ if (resolved === (promise === "resolves"))
1417
+ return this.#check(name, value, args, origin);
1418
+ throw _Assertion.#error(
1419
+ `expect(received).${promise}.${name}()
1371
1420
 
1372
1421
  Received promise ${resolved ? "resolved" : "rejected"} instead of ${resolved ? "rejected" : "resolved"}
1373
1422
  ${resolved ? "Resolved" : "Rejected"} to value: ${String(value)}`,
1374
- origin
1375
- );
1376
- };
1377
- if (!(this.#received instanceof Promise) && typeof this.#received?.then !== "function")
1378
- throw _Assertion.#error(
1379
- `expect(received).${promise}.${name}()
1380
-
1381
- Matcher error: received value must be a promise`,
1382
- origin
1383
- );
1384
- return Promise.resolve(this.#received).then(
1385
- (value) => settled(value, true),
1386
- (reason) => settled(reason, false)
1387
- );
1388
- }
1389
- #check(name, received, args, origin) {
1390
- const matcher = matchers[name];
1391
- let result;
1392
- try {
1393
- result = matcher.call(this.#context, received, ...args);
1394
- } catch (error) {
1395
- throw _Assertion.#error(
1396
- error instanceof Error ? error.message : String(error),
1397
- origin
1398
- );
1399
- }
1400
- if (result.pass === this.#context.isNot)
1401
- throw _Assertion.#error(result.message(), origin);
1402
- }
1403
- static #error(message, origin) {
1404
- const error = new AssertionError(message);
1405
- const frames = origin.stack?.split("\n").slice(1).join("\n");
1406
- error.stack = `${error.name}: ${message}${frames ? `
1407
- ${frames}` : ""}`;
1408
- return error;
1409
- }
1410
- };
1411
- for (const [name, matcher] of Object.entries(matchers))
1412
- Assertion.define(name, matcher);
1413
-
1414
- // packages/testero/src/expect/asymmetric.ts
1415
- var Asymmetric = class {
1416
- sample;
1417
- /** Set for the `expect.not` variants, which match what the rule does not. */
1418
- inverse;
1419
- constructor(sample, inverse = false) {
1420
- this.sample = sample;
1421
- this.inverse = inverse;
1422
- }
1423
- asymmetricMatch(other) {
1424
- return this.matches(other) !== this.inverse;
1425
- }
1426
- get prefix() {
1427
- return this.inverse ? "Not" : "";
1428
- }
1429
- };
1430
- var Any = class extends Asymmetric {
1431
- matches(other) {
1432
- const primitives = /* @__PURE__ */ new Map([
1433
- [String, "string"],
1434
- [Number, "number"],
1435
- [Boolean, "boolean"],
1436
- [BigInt, "bigint"],
1437
- [Symbol, "symbol"],
1438
- [Function, "function"]
1439
- ]);
1440
- const type = primitives.get(this.sample);
1441
- if (type && typeof other === type) return true;
1442
- if (this.sample === Object)
1443
- return typeof other === "object" && other !== null;
1444
- return other instanceof this.sample;
1445
- }
1446
- toString() {
1447
- return `Any<${this.sample.name}>`;
1448
- }
1449
- };
1450
- var Anything = class extends Asymmetric {
1451
- matches(other) {
1452
- return other !== null && other !== void 0;
1453
- }
1454
- toString() {
1455
- return "Anything";
1456
- }
1457
- };
1458
- var ObjectContaining = class extends Asymmetric {
1459
- matches(other) {
1460
- if (typeof other !== "object" || other === null) return false;
1461
- return Reflect.ownKeys(this.sample).every(
1462
- (key) => key in other && Equality.equals(
1463
- this.sample[key],
1464
- other[key]
1465
- )
1466
- );
1467
- }
1468
- toString() {
1469
- return `Object${this.prefix}Containing ${Format.inline(this.sample)}`;
1470
- }
1471
- };
1472
- var ArrayContaining = class extends Asymmetric {
1473
- matches(other) {
1474
- return Array.isArray(other) && this.sample.every(
1475
- (item) => other.some((value) => Equality.equals(item, value))
1476
- );
1477
- }
1478
- toString() {
1479
- return `Array${this.prefix}Containing ${Format.inline(this.sample)}`;
1480
- }
1481
- };
1482
- var StringContaining = class extends Asymmetric {
1483
- matches(other) {
1484
- return typeof other === "string" && other.includes(this.sample);
1485
- }
1486
- toString() {
1487
- return `String${this.prefix}Containing ${JSON.stringify(this.sample)}`;
1488
- }
1489
- };
1490
- var StringMatching = class extends Asymmetric {
1491
- matches(other) {
1492
- return typeof other === "string" && this.sample.test(other);
1493
- }
1494
- toString() {
1495
- return `String${this.prefix}Matching ${this.sample}`;
1496
- }
1497
- };
1498
- var CloseTo = class extends Asymmetric {
1499
- #digits;
1500
- constructor(sample, digits = 2, inverse = false) {
1501
- super(sample, inverse);
1502
- this.#digits = digits;
1503
- }
1504
- matches(other) {
1505
- if (typeof other !== "number") return false;
1506
- if (other === this.sample) return true;
1507
- return Math.abs(this.sample - other) < 10 ** -this.#digits / 2;
1423
+ origin
1424
+ );
1425
+ }, "settled");
1426
+ if (!(this.#received instanceof Promise) && typeof this.#received?.then !== "function")
1427
+ throw _Assertion.#error(
1428
+ `expect(received).${promise}.${name}()
1429
+
1430
+ Matcher error: received value must be a promise`,
1431
+ origin
1432
+ );
1433
+ return Promise.resolve(this.#received).then(
1434
+ (value) => settled(value, true),
1435
+ (reason) => settled(reason, false)
1436
+ );
1437
+ }
1438
+ #check(name, received, args, origin) {
1439
+ const matcher = matchers[name];
1440
+ let result;
1441
+ try {
1442
+ result = matcher.call(this.#context, received, ...args);
1443
+ } catch (error) {
1444
+ throw _Assertion.#error(
1445
+ error instanceof Error ? error.message : String(error),
1446
+ origin
1447
+ );
1448
+ }
1449
+ if (result.pass === this.#context.isNot)
1450
+ throw _Assertion.#error(result.message(), origin);
1451
+ }
1452
+ static #error(message, origin) {
1453
+ const error = new AssertionError(message);
1454
+ const frames = origin.stack?.split("\n").slice(1).join("\n");
1455
+ error.stack = `${error.name}: ${message}${frames ? `
1456
+ ${frames}` : ""}`;
1457
+ return error;
1458
+ }
1459
+ };
1460
+ for (const [name, matcher] of Object.entries(matchers))
1461
+ Assertion.define(name, matcher);
1508
1462
  }
1509
- toString() {
1510
- return `Number${this.prefix}CloseTo ${this.sample} (${this.#digits} digits)`;
1463
+ });
1464
+
1465
+ // packages/testero/src/expect/asymmetric.ts
1466
+ var Asymmetric, Any, Anything, ObjectContaining, ArrayContaining, StringContaining, StringMatching, CloseTo;
1467
+ var init_asymmetric = __esm({
1468
+ "packages/testero/src/expect/asymmetric.ts"() {
1469
+ "use strict";
1470
+ init_equality();
1471
+ init_format();
1472
+ Asymmetric = class {
1473
+ static {
1474
+ __name(this, "Asymmetric");
1475
+ }
1476
+ sample;
1477
+ /** Set for the `expect.not` variants, which match what the rule does not. */
1478
+ inverse;
1479
+ constructor(sample, inverse = false) {
1480
+ this.sample = sample;
1481
+ this.inverse = inverse;
1482
+ }
1483
+ asymmetricMatch(other) {
1484
+ return this.matches(other) !== this.inverse;
1485
+ }
1486
+ get prefix() {
1487
+ return this.inverse ? "Not" : "";
1488
+ }
1489
+ };
1490
+ Any = class extends Asymmetric {
1491
+ static {
1492
+ __name(this, "Any");
1493
+ }
1494
+ matches(other) {
1495
+ const primitives = /* @__PURE__ */ new Map([
1496
+ [String, "string"],
1497
+ [Number, "number"],
1498
+ [Boolean, "boolean"],
1499
+ [BigInt, "bigint"],
1500
+ [Symbol, "symbol"],
1501
+ [Function, "function"]
1502
+ ]);
1503
+ const type = primitives.get(this.sample);
1504
+ if (type && typeof other === type) return true;
1505
+ if (this.sample === Object)
1506
+ return typeof other === "object" && other !== null;
1507
+ return other instanceof this.sample;
1508
+ }
1509
+ toString() {
1510
+ return `Any<${this.sample.name}>`;
1511
+ }
1512
+ };
1513
+ Anything = class extends Asymmetric {
1514
+ static {
1515
+ __name(this, "Anything");
1516
+ }
1517
+ matches(other) {
1518
+ return other !== null && other !== void 0;
1519
+ }
1520
+ toString() {
1521
+ return "Anything";
1522
+ }
1523
+ };
1524
+ ObjectContaining = class extends Asymmetric {
1525
+ static {
1526
+ __name(this, "ObjectContaining");
1527
+ }
1528
+ matches(other) {
1529
+ if (typeof other !== "object" || other === null) return false;
1530
+ return Reflect.ownKeys(this.sample).every(
1531
+ (key) => key in other && Equality.equals(
1532
+ this.sample[key],
1533
+ other[key]
1534
+ )
1535
+ );
1536
+ }
1537
+ toString() {
1538
+ return `Object${this.prefix}Containing ${Format.inline(this.sample)}`;
1539
+ }
1540
+ };
1541
+ ArrayContaining = class extends Asymmetric {
1542
+ static {
1543
+ __name(this, "ArrayContaining");
1544
+ }
1545
+ matches(other) {
1546
+ return Array.isArray(other) && this.sample.every(
1547
+ (item) => other.some((value) => Equality.equals(item, value))
1548
+ );
1549
+ }
1550
+ toString() {
1551
+ return `Array${this.prefix}Containing ${Format.inline(this.sample)}`;
1552
+ }
1553
+ };
1554
+ StringContaining = class extends Asymmetric {
1555
+ static {
1556
+ __name(this, "StringContaining");
1557
+ }
1558
+ matches(other) {
1559
+ return typeof other === "string" && other.includes(this.sample);
1560
+ }
1561
+ toString() {
1562
+ return `String${this.prefix}Containing ${JSON.stringify(this.sample)}`;
1563
+ }
1564
+ };
1565
+ StringMatching = class extends Asymmetric {
1566
+ static {
1567
+ __name(this, "StringMatching");
1568
+ }
1569
+ matches(other) {
1570
+ return typeof other === "string" && this.sample.test(other);
1571
+ }
1572
+ toString() {
1573
+ return `String${this.prefix}Matching ${this.sample}`;
1574
+ }
1575
+ };
1576
+ CloseTo = class extends Asymmetric {
1577
+ static {
1578
+ __name(this, "CloseTo");
1579
+ }
1580
+ #digits;
1581
+ constructor(sample, digits = 2, inverse = false) {
1582
+ super(sample, inverse);
1583
+ this.#digits = digits;
1584
+ }
1585
+ matches(other) {
1586
+ if (typeof other !== "number") return false;
1587
+ if (other === this.sample) return true;
1588
+ return Math.abs(this.sample - other) < 10 ** -this.#digits / 2;
1589
+ }
1590
+ toString() {
1591
+ return `Number${this.prefix}CloseTo ${this.sample} (${this.#digits} digits)`;
1592
+ }
1593
+ };
1511
1594
  }
1512
- };
1595
+ });
1513
1596
 
1514
1597
  // packages/testero/src/expect/expect.ts
1515
- var negatable = (inverse) => ({
1516
- objectContaining: (object) => new ObjectContaining(object, inverse),
1517
- arrayContaining: (array) => new ArrayContaining(array, inverse),
1518
- stringContaining: (text) => new StringContaining(text, inverse),
1519
- stringMatching: (pattern) => new StringMatching(new RegExp(pattern), inverse),
1520
- closeTo: (number, digits) => new CloseTo(number, digits, inverse)
1521
- });
1522
- var Expect = class {
1523
- #expected;
1524
- #needed = false;
1525
- /** Starts the counts of a new test. */
1526
- reset() {
1527
- Assertion.count = 0;
1528
- this.#expected = void 0;
1529
- this.#needed = false;
1530
- }
1531
- /** Failures of `expect.assertions(n)` and `expect.hasAssertions()` after a test. */
1532
- verify() {
1533
- const count = Assertion.count;
1534
- if (this.#expected !== void 0 && count !== this.#expected)
1535
- return [
1536
- new AssertionError(
1537
- `expect.assertions(${this.#expected})
1598
+ var negatable, Expect;
1599
+ var init_expect = __esm({
1600
+ "packages/testero/src/expect/expect.ts"() {
1601
+ "use strict";
1602
+ init_assertion();
1603
+ init_asymmetric();
1604
+ negatable = /* @__PURE__ */ __name((inverse) => ({
1605
+ objectContaining: /* @__PURE__ */ __name((object) => new ObjectContaining(object, inverse), "objectContaining"),
1606
+ arrayContaining: /* @__PURE__ */ __name((array) => new ArrayContaining(array, inverse), "arrayContaining"),
1607
+ stringContaining: /* @__PURE__ */ __name((text) => new StringContaining(text, inverse), "stringContaining"),
1608
+ stringMatching: /* @__PURE__ */ __name((pattern) => new StringMatching(new RegExp(pattern), inverse), "stringMatching"),
1609
+ closeTo: /* @__PURE__ */ __name((number, digits) => new CloseTo(number, digits, inverse), "closeTo")
1610
+ }), "negatable");
1611
+ Expect = class {
1612
+ static {
1613
+ __name(this, "Expect");
1614
+ }
1615
+ #expected;
1616
+ #needed = false;
1617
+ /** Starts the counts of a new test. */
1618
+ reset() {
1619
+ Assertion.count = 0;
1620
+ this.#expected = void 0;
1621
+ this.#needed = false;
1622
+ }
1623
+ /** Failures of `expect.assertions(n)` and `expect.hasAssertions()` after a test. */
1624
+ verify() {
1625
+ const count = Assertion.count;
1626
+ if (this.#expected !== void 0 && count !== this.#expected)
1627
+ return [
1628
+ new AssertionError(
1629
+ `expect.assertions(${this.#expected})
1538
1630
 
1539
1631
  Expected ${this.#expected} assertion${this.#expected === 1 ? "" : "s"} to be called but received ${count} assertion call${count === 1 ? "" : "s"}.`
1540
- )
1541
- ];
1542
- if (this.#needed && count === 0)
1543
- return [
1544
- new AssertionError(
1545
- "expect.hasAssertions()\n\nExpected at least one assertion to be called but received none."
1546
- )
1547
- ];
1548
- return [];
1549
- }
1550
- /** The function test files call as `expect`. */
1551
- create() {
1552
- const expect = (received) => new Assertion(received);
1553
- return Object.assign(expect, {
1554
- ...negatable(false),
1555
- not: negatable(true),
1556
- any: (type) => new Any(type),
1557
- anything: () => new Anything(void 0),
1558
- assertions: (count) => {
1559
- this.#expected = count;
1560
- },
1561
- hasAssertions: () => {
1562
- this.#needed = true;
1563
- },
1564
- extend: (added) => {
1565
- for (const [name, matcher] of Object.entries(added))
1566
- Assertion.define(name, matcher);
1632
+ )
1633
+ ];
1634
+ if (this.#needed && count === 0)
1635
+ return [
1636
+ new AssertionError(
1637
+ "expect.hasAssertions()\n\nExpected at least one assertion to be called but received none."
1638
+ )
1639
+ ];
1640
+ return [];
1641
+ }
1642
+ /** The function test files call as `expect`. */
1643
+ create() {
1644
+ const expect = /* @__PURE__ */ __name((received) => new Assertion(received), "expect");
1645
+ return Object.assign(expect, {
1646
+ ...negatable(false),
1647
+ not: negatable(true),
1648
+ any: /* @__PURE__ */ __name((type) => new Any(type), "any"),
1649
+ anything: /* @__PURE__ */ __name(() => new Anything(void 0), "anything"),
1650
+ assertions: /* @__PURE__ */ __name((count) => {
1651
+ this.#expected = count;
1652
+ }, "assertions"),
1653
+ hasAssertions: /* @__PURE__ */ __name(() => {
1654
+ this.#needed = true;
1655
+ }, "hasAssertions"),
1656
+ extend: /* @__PURE__ */ __name((added) => {
1657
+ for (const [name, matcher] of Object.entries(added))
1658
+ Assertion.define(name, matcher);
1659
+ }, "extend")
1660
+ });
1567
1661
  }
1568
- });
1662
+ };
1569
1663
  }
1570
- };
1664
+ });
1571
1665
 
1572
1666
  // packages/testero/src/globals.ts
1573
- var Globals = class _Globals {
1574
- static key = /* @__PURE__ */ Symbol.for("testero");
1575
- static names = [
1576
- "describe",
1577
- "fdescribe",
1578
- "xdescribe",
1579
- "test",
1580
- "it",
1581
- "fit",
1582
- "xit",
1583
- "xtest",
1584
- "beforeAll",
1585
- "afterAll",
1586
- "beforeEach",
1587
- "afterEach",
1588
- "expect",
1589
- "testero",
1590
- "jest"
1591
- ];
1592
- /** The module the transformer injects, it reads the globals of the file being run. */
1593
- static source() {
1594
- return [
1595
- 'const globals = globalThis[Symbol.for("testero")];',
1596
- ..._Globals.names.map(
1597
- (name) => `export const ${name} = globals.${name};`
1598
- )
1599
- ].join("\n");
1600
- }
1601
- /** Sets the globals for the next test file to import. */
1602
- static install(collector, expect, settings) {
1603
- const { describe, test } = collector;
1604
- const testero = {
1605
- fn: Mocks.fn,
1606
- spyOn: Mocks.spyOn,
1607
- isMockFunction: Mocks.isMockFunction,
1608
- clearAllMocks: () => {
1609
- Mocks.clearAll();
1610
- return testero;
1611
- },
1612
- resetAllMocks: () => {
1613
- Mocks.resetAll();
1614
- return testero;
1615
- },
1616
- restoreAllMocks: () => {
1617
- Mocks.restoreAll();
1618
- return testero;
1619
- },
1620
- /** Sets the timeout of the tests and hooks of this file. */
1621
- setTimeout: (timeout) => {
1622
- settings.timeout = timeout;
1623
- return testero;
1667
+ var Globals;
1668
+ var init_globals = __esm({
1669
+ "packages/testero/src/globals.ts"() {
1670
+ "use strict";
1671
+ init_mock();
1672
+ Globals = class _Globals {
1673
+ static {
1674
+ __name(this, "Globals");
1675
+ }
1676
+ static key = /* @__PURE__ */ Symbol.for("testero");
1677
+ static names = [
1678
+ "describe",
1679
+ "fdescribe",
1680
+ "xdescribe",
1681
+ "test",
1682
+ "it",
1683
+ "fit",
1684
+ "xit",
1685
+ "xtest",
1686
+ "beforeAll",
1687
+ "afterAll",
1688
+ "beforeEach",
1689
+ "afterEach",
1690
+ "expect",
1691
+ "testero",
1692
+ "jest"
1693
+ ];
1694
+ /** The module the transformer injects, it reads the globals of the file being run. */
1695
+ static source() {
1696
+ return [
1697
+ 'const globals = globalThis[Symbol.for("testero")];',
1698
+ ..._Globals.names.map(
1699
+ (name) => `export const ${name} = globals.${name};`
1700
+ )
1701
+ ].join("\n");
1702
+ }
1703
+ /** Sets the globals for the next test file to import. */
1704
+ static install(collector, expect, settings) {
1705
+ const { describe, test } = collector;
1706
+ const testero = {
1707
+ fn: Mocks.fn,
1708
+ spyOn: Mocks.spyOn,
1709
+ isMockFunction: Mocks.isMockFunction,
1710
+ clearAllMocks: /* @__PURE__ */ __name(() => {
1711
+ Mocks.clearAll();
1712
+ return testero;
1713
+ }, "clearAllMocks"),
1714
+ resetAllMocks: /* @__PURE__ */ __name(() => {
1715
+ Mocks.resetAll();
1716
+ return testero;
1717
+ }, "resetAllMocks"),
1718
+ restoreAllMocks: /* @__PURE__ */ __name(() => {
1719
+ Mocks.restoreAll();
1720
+ return testero;
1721
+ }, "restoreAllMocks"),
1722
+ /** Sets the timeout of the tests and hooks of this file. */
1723
+ setTimeout: /* @__PURE__ */ __name((timeout) => {
1724
+ settings.timeout = timeout;
1725
+ return testero;
1726
+ }, "setTimeout")
1727
+ };
1728
+ globalThis[_Globals.key] = {
1729
+ describe,
1730
+ fdescribe: describe.only,
1731
+ xdescribe: describe.skip,
1732
+ test,
1733
+ it: test,
1734
+ fit: test.only,
1735
+ xit: test.skip,
1736
+ xtest: test.skip,
1737
+ beforeAll: collector.hook("beforeAll"),
1738
+ afterAll: collector.hook("afterAll"),
1739
+ beforeEach: collector.hook("beforeEach"),
1740
+ afterEach: collector.hook("afterEach"),
1741
+ expect: expect.create(),
1742
+ testero,
1743
+ jest: testero
1744
+ };
1624
1745
  }
1625
- };
1626
- globalThis[_Globals.key] = {
1627
- describe,
1628
- fdescribe: describe.only,
1629
- xdescribe: describe.skip,
1630
- test,
1631
- it: test,
1632
- fit: test.only,
1633
- xit: test.skip,
1634
- xtest: test.skip,
1635
- beforeAll: collector.hook("beforeAll"),
1636
- afterAll: collector.hook("afterAll"),
1637
- beforeEach: collector.hook("beforeEach"),
1638
- afterEach: collector.hook("afterEach"),
1639
- expect: expect.create(),
1640
- testero,
1641
- jest: testero
1642
1746
  };
1643
1747
  }
1644
- };
1645
-
1646
- // packages/testero/src/reporter.ts
1647
- import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
1648
- import { dirname as dirname2, isAbsolute, relative } from "node:path";
1649
- import { fileURLToPath } from "node:url";
1748
+ });
1650
1749
 
1651
1750
  // packages/testero/src/transformer.ts
1652
1751
  import { mkdtempSync, readFileSync as readFileSync2, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
@@ -1654,563 +1753,738 @@ import { tmpdir } from "node:os";
1654
1753
  import { dirname, extname, join as join2, sep } from "node:path";
1655
1754
  import { pathToFileURL as pathToFileURL2 } from "node:url";
1656
1755
  import { build } from "esbuild";
1657
- var Transformer = class _Transformer {
1658
- /** Packages test files import the globals from, like with Jest. */
1659
- static modules = ["@jest/globals", "@aljazmc/testero"];
1660
- static #loaders = {
1661
- ".js": "js",
1662
- ".cjs": "js",
1663
- ".mjs": "js",
1664
- ".jsx": "jsx",
1665
- ".ts": "ts",
1666
- ".cts": "ts",
1667
- ".mts": "ts",
1668
- ".tsx": "tsx"
1669
- };
1670
- /** The import.meta properties that describe the source file. */
1671
- static #meta = ["url", "filename", "dirname"];
1672
- /** Marks the resolutions the plugin asks esbuild for itself. */
1673
- static #own = /* @__PURE__ */ Symbol("testero");
1674
- /** Where compiled test files go, stack frames there are testero's own. */
1675
- static prefix = join2(tmpdir(), "testero-build-");
1676
- #directory = mkdtempSync(_Transformer.prefix);
1677
- #globals = join2(this.#directory, "globals.mjs");
1678
- #count = 0;
1679
- constructor() {
1680
- writeFileSync2(this.#globals, Globals.source());
1681
- }
1682
- /** Bundles a test file, returns the URL of the module to import. */
1683
- async compile(file) {
1684
- const outfile = join2(this.#directory, `${++this.#count}.mjs`);
1685
- const { outputFiles } = await build({
1686
- entryPoints: [file],
1687
- outfile,
1688
- write: false,
1689
- bundle: true,
1690
- platform: "node",
1691
- format: "esm",
1692
- target: `node${process.versions.node.split(".")[0]}`,
1693
- sourcemap: "inline",
1694
- sourcesContent: false,
1695
- inject: [this.#globals],
1696
- define: Object.fromEntries(
1697
- _Transformer.#meta.map((name) => [
1698
- `import.meta.${name}`,
1699
- `__testero.${name}`
1700
- ])
1701
- ),
1702
- // CommonJS code in the bundle may require installed packages
1703
- banner: {
1704
- js: 'import { createRequire as __testeroRequire } from "node:module";\nconst require = __testeroRequire(import.meta.url);'
1705
- },
1706
- logLevel: "silent",
1707
- plugins: [this.#plugin()]
1708
- });
1709
- writeFileSync2(outfile, outputFiles[0]?.text ?? "");
1710
- return pathToFileURL2(outfile).href;
1711
- }
1712
- /** Removes the compiled files. */
1713
- dispose() {
1714
- rmSync(this.#directory, { recursive: true, force: true });
1715
- }
1716
- #plugin() {
1717
- return {
1718
- name: "testero",
1719
- setup: (build2) => {
1720
- build2.onResolve({ filter: /^testero-meta:/ }, (args) => ({
1721
- path: args.path.slice("testero-meta:".length),
1722
- namespace: "testero-meta"
1723
- }));
1724
- build2.onLoad(
1725
- { filter: /.*/, namespace: "testero-meta" },
1726
- (args) => ({
1727
- contents: `export default ${JSON.stringify({
1728
- url: pathToFileURL2(args.path).href,
1729
- filename: args.path,
1730
- dirname: dirname(args.path)
1731
- })};`,
1732
- loader: "js"
1733
- })
1734
- );
1735
- build2.onResolve({ filter: /^[^./]/ }, async (args) => {
1736
- if (args.pluginData === _Transformer.#own) return void 0;
1737
- if (_Transformer.modules.includes(args.path))
1738
- return { path: this.#globals };
1739
- const resolved = await build2.resolve(args.path, {
1740
- kind: args.kind,
1741
- resolveDir: args.resolveDir,
1742
- pluginData: _Transformer.#own
1743
- });
1744
- if (resolved.errors.length > 0)
1745
- return { errors: resolved.errors };
1746
- if (resolved.external)
1747
- return { path: args.path, external: true };
1748
- if (!_Transformer.#installed(resolved.path))
1749
- return { path: resolved.path };
1750
- return {
1751
- path: args.kind === "require-call" ? resolved.path : pathToFileURL2(resolved.path).href,
1752
- external: true
1753
- };
1756
+ var Transformer;
1757
+ var init_transformer = __esm({
1758
+ "packages/testero/src/transformer.ts"() {
1759
+ "use strict";
1760
+ init_globals();
1761
+ Transformer = class _Transformer {
1762
+ static {
1763
+ __name(this, "Transformer");
1764
+ }
1765
+ /** Packages test files import the globals from, like with Jest. */
1766
+ static modules = ["@jest/globals", "@aljazmc/testero"];
1767
+ static #loaders = {
1768
+ ".js": "js",
1769
+ ".cjs": "js",
1770
+ ".mjs": "js",
1771
+ ".jsx": "jsx",
1772
+ ".ts": "ts",
1773
+ ".cts": "ts",
1774
+ ".mts": "ts",
1775
+ ".tsx": "tsx"
1776
+ };
1777
+ /** The import.meta properties that describe the source file. */
1778
+ static #meta = ["url", "filename", "dirname"];
1779
+ /** Marks the resolutions the plugin asks esbuild for itself. */
1780
+ static #own = /* @__PURE__ */ Symbol("testero");
1781
+ /** Where compiled test files go, stack frames there are testero's own. */
1782
+ static prefix = join2(tmpdir(), "testero-build-");
1783
+ #directory = mkdtempSync(_Transformer.prefix);
1784
+ #globals = join2(this.#directory, "globals.mjs");
1785
+ #count = 0;
1786
+ constructor() {
1787
+ writeFileSync2(this.#globals, Globals.source());
1788
+ }
1789
+ /** Bundles a test file, returns the URL of the module to import. */
1790
+ async compile(file) {
1791
+ const outfile = join2(this.#directory, `${++this.#count}.mjs`);
1792
+ const { outputFiles } = await build({
1793
+ entryPoints: [file],
1794
+ outfile,
1795
+ write: false,
1796
+ bundle: true,
1797
+ platform: "node",
1798
+ format: "esm",
1799
+ target: `node${process.versions.node.split(".")[0]}`,
1800
+ sourcemap: "inline",
1801
+ sourcesContent: false,
1802
+ inject: [this.#globals],
1803
+ define: Object.fromEntries(
1804
+ _Transformer.#meta.map((name) => [
1805
+ `import.meta.${name}`,
1806
+ `__testero.${name}`
1807
+ ])
1808
+ ),
1809
+ // CommonJS code in the bundle may require installed packages
1810
+ banner: {
1811
+ js: 'import { createRequire as __testeroRequire } from "node:module";\nconst require = __testeroRequire(import.meta.url);'
1812
+ },
1813
+ logLevel: "silent",
1814
+ plugins: [this.#plugin()]
1754
1815
  });
1755
- build2.onLoad({ filter: /\.[cm]?[jt]sx?$/ }, (args) => {
1756
- if (_Transformer.#installed(args.path)) return void 0;
1757
- const source = readFileSync2(args.path, "utf8");
1758
- const contents = /\bimport\.meta\b/.test(source) ? `${source}
1816
+ writeFileSync2(outfile, outputFiles[0]?.text ?? "");
1817
+ return pathToFileURL2(outfile).href;
1818
+ }
1819
+ /** Removes the compiled files. */
1820
+ dispose() {
1821
+ rmSync(this.#directory, { recursive: true, force: true });
1822
+ }
1823
+ #plugin() {
1824
+ return {
1825
+ name: "testero",
1826
+ setup: /* @__PURE__ */ __name((build2) => {
1827
+ build2.onResolve({ filter: /^testero-meta:/ }, (args) => ({
1828
+ path: args.path.slice("testero-meta:".length),
1829
+ namespace: "testero-meta"
1830
+ }));
1831
+ build2.onLoad(
1832
+ { filter: /.*/, namespace: "testero-meta" },
1833
+ (args) => ({
1834
+ contents: `export default ${JSON.stringify({
1835
+ url: pathToFileURL2(args.path).href,
1836
+ filename: args.path,
1837
+ dirname: dirname(args.path)
1838
+ })};`,
1839
+ loader: "js"
1840
+ })
1841
+ );
1842
+ build2.onResolve({ filter: /^[^./]/ }, async (args) => {
1843
+ if (args.pluginData === _Transformer.#own) return void 0;
1844
+ if (_Transformer.modules.includes(args.path))
1845
+ return { path: this.#globals };
1846
+ const resolved = await build2.resolve(args.path, {
1847
+ kind: args.kind,
1848
+ resolveDir: args.resolveDir,
1849
+ pluginData: _Transformer.#own
1850
+ });
1851
+ if (resolved.errors.length > 0)
1852
+ return { errors: resolved.errors };
1853
+ if (resolved.external)
1854
+ return { path: args.path, external: true };
1855
+ if (!_Transformer.#installed(resolved.path))
1856
+ return { path: resolved.path };
1857
+ return {
1858
+ path: args.kind === "require-call" ? resolved.path : pathToFileURL2(resolved.path).href,
1859
+ external: true
1860
+ };
1861
+ });
1862
+ build2.onLoad({ filter: /\.[cm]?[jt]sx?$/ }, (args) => {
1863
+ if (_Transformer.#installed(args.path)) return void 0;
1864
+ const source = readFileSync2(args.path, "utf8");
1865
+ const contents = /\bimport\.meta\b/.test(source) ? `${source}
1759
1866
  import __testero from ${JSON.stringify(`testero-meta:${args.path}`)};
1760
1867
  ` : source;
1761
- return {
1762
- contents,
1763
- loader: _Transformer.#loaders[extname(args.path)] ?? "js",
1764
- resolveDir: dirname(args.path)
1765
- };
1766
- });
1868
+ return {
1869
+ contents,
1870
+ loader: _Transformer.#loaders[extname(args.path)] ?? "js",
1871
+ resolveDir: dirname(args.path)
1872
+ };
1873
+ });
1874
+ }, "setup")
1875
+ };
1876
+ }
1877
+ static #installed(path) {
1878
+ return path.split(sep).includes("node_modules");
1767
1879
  }
1768
1880
  };
1769
1881
  }
1770
- static #installed(path) {
1771
- return path.split(sep).includes("node_modules");
1772
- }
1773
- };
1882
+ });
1774
1883
 
1775
1884
  // packages/testero/src/reporter.ts
1776
- var Reporter = class _Reporter {
1777
- static #codes = {
1778
- bold: 1,
1779
- dim: 2,
1780
- inverse: 7,
1781
- red: 31,
1782
- green: 32,
1783
- yellow: 33,
1784
- magenta: 35,
1785
- gray: 90
1786
- };
1787
- /** Stack frames from testero itself are left out of failures. */
1788
- static #own = dirname2(fileURLToPath(import.meta.url));
1789
- #root;
1790
- #verbose;
1791
- #color;
1792
- #write;
1793
- constructor(root, options = {}) {
1794
- this.#root = root;
1795
- this.#verbose = options.verbose ?? false;
1796
- this.#color = options.color ?? (process.stdout.isTTY === true && !process.env.NO_COLOR);
1797
- this.#write = options.write ?? ((text) => process.stdout.write(text));
1798
- }
1799
- static failed(result) {
1800
- return result.errors.length > 0 || result.results.some((test) => test.status === "failed");
1801
- }
1802
- /** The results of a test file, with the failures in detail. */
1803
- file(result) {
1804
- const failed = _Reporter.failed(result);
1805
- const status = failed ? this.#paint(" FAIL ", "inverse", "bold", "red") : this.#paint(" PASS ", "inverse", "bold", "green");
1806
- const slow = result.duration > 1e3 ? ` (${(result.duration / 1e3).toFixed(2)} s)` : "";
1807
- const lines = [`${status} ${result.file}${slow}`];
1808
- if (this.#verbose || failed) lines.push(...this.#tree(result.results));
1809
- for (const error of result.errors)
1810
- lines.push(
1811
- "",
1812
- this.#paint(" \u25CF Test suite failed to run", "bold", "red"),
1813
- "",
1814
- this.#error(error)
1815
- );
1816
- for (const test of result.results)
1817
- for (const error of test.errors)
1818
- lines.push(
1819
- "",
1820
- this.#paint(
1821
- ` \u25CF ${test.test.titles.join(" \u203A ")}`,
1822
- "bold",
1823
- "red"
1824
- ),
1825
- "",
1826
- this.#error(error)
1827
- );
1828
- this.#write(`${lines.join("\n")}
1885
+ import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
1886
+ import { dirname as dirname2, isAbsolute, relative } from "node:path";
1887
+ import { fileURLToPath } from "node:url";
1888
+ var Reporter;
1889
+ var init_reporter = __esm({
1890
+ "packages/testero/src/reporter.ts"() {
1891
+ "use strict";
1892
+ init_assertion();
1893
+ init_format();
1894
+ init_transformer();
1895
+ Reporter = class _Reporter {
1896
+ static {
1897
+ __name(this, "Reporter");
1898
+ }
1899
+ static #codes = {
1900
+ bold: 1,
1901
+ dim: 2,
1902
+ inverse: 7,
1903
+ red: 31,
1904
+ green: 32,
1905
+ yellow: 33,
1906
+ magenta: 35,
1907
+ gray: 90
1908
+ };
1909
+ /** Stack frames from testero itself are left out of failures. */
1910
+ static #own = dirname2(fileURLToPath(import.meta.url));
1911
+ #root;
1912
+ #verbose;
1913
+ #color;
1914
+ #write;
1915
+ constructor(root, options = {}) {
1916
+ this.#root = root;
1917
+ this.#verbose = options.verbose ?? false;
1918
+ this.#color = options.color ?? (process.stdout.isTTY === true && !process.env.NO_COLOR);
1919
+ this.#write = options.write ?? ((text) => process.stdout.write(text));
1920
+ }
1921
+ static failed(result) {
1922
+ return result.errors.length > 0 || result.results.some((test) => test.status === "failed");
1923
+ }
1924
+ /** The results of a test file, with the failures in detail. */
1925
+ file(result) {
1926
+ const failed = _Reporter.failed(result);
1927
+ const status = failed ? this.#paint(" FAIL ", "inverse", "bold", "red") : this.#paint(" PASS ", "inverse", "bold", "green");
1928
+ const slow = result.duration > 1e3 ? ` (${(result.duration / 1e3).toFixed(2)} s)` : "";
1929
+ const lines = [`${status} ${result.file}${slow}`];
1930
+ if (this.#verbose || failed) lines.push(...this.#tree(result.results));
1931
+ for (const error of result.errors)
1932
+ lines.push(
1933
+ "",
1934
+ this.#paint(" \u25CF Test suite failed to run", "bold", "red"),
1935
+ "",
1936
+ this.#error(error)
1937
+ );
1938
+ for (const test of result.results)
1939
+ for (const error of test.errors)
1940
+ lines.push(
1941
+ "",
1942
+ this.#paint(
1943
+ ` \u25CF ${test.test.titles.join(" \u203A ")}`,
1944
+ "bold",
1945
+ "red"
1946
+ ),
1947
+ "",
1948
+ this.#error(error)
1949
+ );
1950
+ this.#write(`${lines.join("\n")}
1829
1951
  ${failed ? "\n" : ""}`);
1830
- }
1831
- /** Counts of test files and tests, and the time they took. */
1832
- summary(files, duration) {
1833
- const results = files.flatMap((file) => file.results);
1834
- const count = (status) => results.filter((result) => result.status === status).length;
1835
- const failedFiles = files.filter(_Reporter.failed).length;
1836
- const parts = (counts, total) => [
1837
- ...counts.filter(([amount]) => amount > 0).map(
1838
- ([amount, label, color]) => this.#paint(`${amount} ${label}`, "bold", color)
1839
- ),
1840
- `${total} total`
1841
- ].join(", ");
1842
- this.#write(
1843
- [
1844
- "",
1845
- `${this.#paint("Test Suites:", "bold")} ${parts(
1846
- [
1847
- [failedFiles, "failed", "red"],
1848
- [files.length - failedFiles, "passed", "green"]
1849
- ],
1850
- files.length
1851
- )}`,
1852
- `${this.#paint("Tests:", "bold")} ${parts(
1952
+ }
1953
+ /** Counts of test files and tests, and the time they took. */
1954
+ summary(files, duration) {
1955
+ const results = files.flatMap((file) => file.results);
1956
+ const count = /* @__PURE__ */ __name((status) => results.filter((result) => result.status === status).length, "count");
1957
+ const failedFiles = files.filter(_Reporter.failed).length;
1958
+ const parts = /* @__PURE__ */ __name((counts, total) => [
1959
+ ...counts.filter(([amount]) => amount > 0).map(
1960
+ ([amount, label, color]) => this.#paint(`${amount} ${label}`, "bold", color)
1961
+ ),
1962
+ `${total} total`
1963
+ ].join(", "), "parts");
1964
+ this.#write(
1853
1965
  [
1854
- [count("failed"), "failed", "red"],
1855
- [count("skipped"), "skipped", "yellow"],
1856
- [count("todo"), "todo", "magenta"],
1857
- [count("passed"), "passed", "green"]
1858
- ],
1859
- results.length
1860
- )}`,
1861
- `${this.#paint("Time:", "bold")} ${(duration / 1e3).toFixed(3)} s`,
1862
- ""
1863
- ].join("\n")
1864
- );
1865
- }
1866
- /** Explains that no test files were found. */
1867
- none(patterns) {
1868
- this.#write(
1869
- `No tests found${patterns.length > 0 ? `, no files match ${patterns.map((pattern) => JSON.stringify(pattern)).join(", ")}` : ""}.
1870
- `
1871
- );
1872
- }
1873
- /** Tests indented under their suites, with a mark for how each went. */
1874
- #tree(results) {
1875
- const lines = [];
1876
- let previous = [];
1877
- for (const { test, status, duration } of results) {
1878
- const titles = test.suite.titles;
1879
- let shared = 0;
1880
- while (shared < titles.length && titles[shared] === previous[shared])
1881
- shared++;
1882
- for (const [depth, title] of titles.slice(shared).entries())
1883
- lines.push(`${" ".repeat(shared + depth + 1)}${title}`);
1884
- previous = titles;
1885
- const marks = {
1886
- passed: this.#paint("\u2713", "green"),
1887
- failed: this.#paint("\u2715", "red"),
1888
- skipped: this.#paint("\u25CB skipped", "yellow"),
1889
- todo: this.#paint("\u270E todo", "magenta")
1890
- };
1891
- const time = duration >= 1 ? this.#paint(` (${Math.round(duration)} ms)`, "gray") : "";
1892
- lines.push(
1893
- `${" ".repeat(titles.length + 1)}${marks[status]} ${test.name}${status === "passed" || status === "failed" ? time : ""}`
1894
- );
1895
- }
1896
- return lines;
1897
- }
1898
- /** The message of an error, the code where it happened and its stack. */
1899
- #error(error) {
1900
- const message = error instanceof AssertionError ? error.message : error instanceof Error ? `${error.name}: ${error.message}` : `thrown: ${Format.inline(error)}`;
1901
- const frames = error instanceof Error ? this.#frames(error.stack ?? "") : [];
1902
- const [first] = frames;
1903
- const sections = [this.#highlight(message)];
1904
- const code = first ? this.#code(first) : "";
1905
- if (code) sections.push(code);
1906
- if (frames.length > 0)
1907
- sections.push(
1908
- frames.map(
1909
- (frame) => this.#paint(
1910
- `at ${this.#path(frame.file)}:${frame.line}:${frame.column}`,
1911
- "gray"
1912
- )
1913
- ).join("\n")
1914
- );
1915
- return _Reporter.#indent(sections.join("\n\n"), " ");
1916
- }
1917
- /** Colors expected values green and received values red, like Jest. */
1918
- #highlight(message) {
1919
- return message.split("\n").map((line) => {
1920
- if (/^(?:Expected|- )/.test(line))
1921
- return this.#paint(line, "green");
1922
- if (/^(?:Received|\+ )/.test(line))
1923
- return this.#paint(line, "red");
1924
- return line;
1925
- }).join("\n");
1926
- }
1927
- /** Stack frames of the code under test, without Node's and testero's own. */
1928
- #frames(stack) {
1929
- const frames = [];
1930
- for (const line of stack.split("\n")) {
1931
- const match = /^\s+at (?:.*?\()?(.+?):(\d+):(\d+)\)?$/.exec(line);
1932
- if (!match) continue;
1933
- let file = match[1];
1934
- if (file.startsWith("file://")) file = fileURLToPath(file);
1935
- if (!isAbsolute(file) || file.includes("node_modules") || file.startsWith(_Reporter.#own) || file.startsWith(Transformer.prefix))
1936
- continue;
1937
- frames.push({
1938
- file,
1939
- line: Number(match[2]),
1940
- column: Number(match[3])
1941
- });
1942
- }
1943
- return frames;
1944
- }
1945
- /** The lines around a position, with a mark under it. */
1946
- #code({ file, line, column }) {
1947
- if (!existsSync2(file)) return "";
1948
- const lines = readFileSync3(file, "utf8").split(/\r?\n/);
1949
- const first = Math.max(1, line - 2);
1950
- const last = Math.min(lines.length, line + 2);
1951
- const width = String(last).length;
1952
- const out = [];
1953
- for (let number = first; number <= last; number++) {
1954
- const gutter = `${String(number).padStart(width)} |`;
1955
- const text = lines[number - 1] ?? "";
1956
- if (number === line) {
1957
- out.push(
1958
- `${this.#paint(">", "red", "bold")} ${gutter} ${text}`
1966
+ "",
1967
+ `${this.#paint("Test Suites:", "bold")} ${parts(
1968
+ [
1969
+ [failedFiles, "failed", "red"],
1970
+ [files.length - failedFiles, "passed", "green"]
1971
+ ],
1972
+ files.length
1973
+ )}`,
1974
+ `${this.#paint("Tests:", "bold")} ${parts(
1975
+ [
1976
+ [count("failed"), "failed", "red"],
1977
+ [count("skipped"), "skipped", "yellow"],
1978
+ [count("todo"), "todo", "magenta"],
1979
+ [count("passed"), "passed", "green"]
1980
+ ],
1981
+ results.length
1982
+ )}`,
1983
+ `${this.#paint("Time:", "bold")} ${(duration / 1e3).toFixed(3)} s`,
1984
+ ""
1985
+ ].join("\n")
1959
1986
  );
1960
- out.push(
1961
- ` ${" ".repeat(width)} | ${" ".repeat(column - 1)}${this.#paint("^", "red", "bold")}`
1987
+ }
1988
+ /** Explains that no test files were found. */
1989
+ none(patterns) {
1990
+ this.#write(
1991
+ `No tests found${patterns.length > 0 ? `, no files match ${patterns.map((pattern) => JSON.stringify(pattern)).join(", ")}` : ""}.
1992
+ `
1962
1993
  );
1963
- } else out.push(this.#paint(` ${gutter} ${text}`, "dim"));
1964
- }
1965
- return out.join("\n");
1966
- }
1967
- #path(file) {
1968
- const path = relative(this.#root, file);
1969
- return path.startsWith("..") ? file : path;
1970
- }
1971
- #paint(text, ...styles) {
1972
- if (!this.#color) return text;
1973
- const codes = styles.map((style) => _Reporter.#codes[style]).join(";");
1974
- return `\x1B[${codes}m${text}\x1B[0m`;
1975
- }
1976
- static #indent(text, indent) {
1977
- return text.split("\n").map((line) => line ? indent + line : line).join("\n");
1994
+ }
1995
+ /** Tests indented under their suites, with a mark for how each went. */
1996
+ #tree(results) {
1997
+ const lines = [];
1998
+ let previous = [];
1999
+ for (const { test, status, duration } of results) {
2000
+ const titles = test.suite.titles;
2001
+ let shared = 0;
2002
+ while (shared < titles.length && titles[shared] === previous[shared])
2003
+ shared++;
2004
+ for (const [depth, title] of titles.slice(shared).entries())
2005
+ lines.push(`${" ".repeat(shared + depth + 1)}${title}`);
2006
+ previous = titles;
2007
+ const marks = {
2008
+ passed: this.#paint("\u2713", "green"),
2009
+ failed: this.#paint("\u2715", "red"),
2010
+ skipped: this.#paint("\u25CB skipped", "yellow"),
2011
+ todo: this.#paint("\u270E todo", "magenta")
2012
+ };
2013
+ const time = duration >= 1 ? this.#paint(` (${Math.round(duration)} ms)`, "gray") : "";
2014
+ lines.push(
2015
+ `${" ".repeat(titles.length + 1)}${marks[status]} ${test.name}${status === "passed" || status === "failed" ? time : ""}`
2016
+ );
2017
+ }
2018
+ return lines;
2019
+ }
2020
+ /** The message of an error, the code where it happened and its stack. */
2021
+ #error(error) {
2022
+ const message = error instanceof AssertionError ? error.message : error instanceof Error ? `${error.name}: ${error.message}` : `thrown: ${Format.inline(error)}`;
2023
+ const frames = error instanceof Error ? this.#frames(error.stack ?? "") : [];
2024
+ const [first] = frames;
2025
+ const sections = [this.#highlight(message)];
2026
+ const code = first ? this.#code(first) : "";
2027
+ if (code) sections.push(code);
2028
+ if (frames.length > 0)
2029
+ sections.push(
2030
+ frames.map(
2031
+ (frame) => this.#paint(
2032
+ `at ${this.#path(frame.file)}:${frame.line}:${frame.column}`,
2033
+ "gray"
2034
+ )
2035
+ ).join("\n")
2036
+ );
2037
+ return _Reporter.#indent(sections.join("\n\n"), " ");
2038
+ }
2039
+ /** Colors expected values green and received values red, like Jest. */
2040
+ #highlight(message) {
2041
+ return message.split("\n").map((line) => {
2042
+ if (/^(?:Expected|- )/.test(line))
2043
+ return this.#paint(line, "green");
2044
+ if (/^(?:Received|\+ )/.test(line))
2045
+ return this.#paint(line, "red");
2046
+ return line;
2047
+ }).join("\n");
2048
+ }
2049
+ /** Stack frames of the code under test, without Node's and testero's own. */
2050
+ #frames(stack) {
2051
+ const frames = [];
2052
+ for (const line of stack.split("\n")) {
2053
+ const match = /^\s+at (?:.*?\()?(.+?):(\d+):(\d+)\)?$/.exec(line);
2054
+ if (!match) continue;
2055
+ let file = match[1];
2056
+ if (file.startsWith("file://")) file = fileURLToPath(file);
2057
+ if (!isAbsolute(file) || file.includes("node_modules") || file.startsWith(_Reporter.#own) || file.startsWith(Transformer.prefix))
2058
+ continue;
2059
+ frames.push({
2060
+ file,
2061
+ line: Number(match[2]),
2062
+ column: Number(match[3])
2063
+ });
2064
+ }
2065
+ return frames;
2066
+ }
2067
+ /** The lines around a position, with a mark under it. */
2068
+ #code({ file, line, column }) {
2069
+ if (!existsSync2(file)) return "";
2070
+ const lines = readFileSync3(file, "utf8").split(/\r?\n/);
2071
+ const first = Math.max(1, line - 2);
2072
+ const last = Math.min(lines.length, line + 2);
2073
+ const width = String(last).length;
2074
+ const out = [];
2075
+ for (let number = first; number <= last; number++) {
2076
+ const gutter = `${String(number).padStart(width)} |`;
2077
+ const text = lines[number - 1] ?? "";
2078
+ if (number === line) {
2079
+ out.push(
2080
+ `${this.#paint(">", "red", "bold")} ${gutter} ${text}`
2081
+ );
2082
+ out.push(
2083
+ ` ${" ".repeat(width)} | ${" ".repeat(column - 1)}${this.#paint("^", "red", "bold")}`
2084
+ );
2085
+ } else out.push(this.#paint(` ${gutter} ${text}`, "dim"));
2086
+ }
2087
+ return out.join("\n");
2088
+ }
2089
+ #path(file) {
2090
+ const path = relative(this.#root, file);
2091
+ return path.startsWith("..") ? file : path;
2092
+ }
2093
+ #paint(text, ...styles) {
2094
+ if (!this.#color) return text;
2095
+ const codes = styles.map((style) => _Reporter.#codes[style]).join(";");
2096
+ return `\x1B[${codes}m${text}\x1B[0m`;
2097
+ }
2098
+ static #indent(text, indent) {
2099
+ return text.split("\n").map((line) => line ? indent + line : line).join("\n");
2100
+ }
2101
+ };
1978
2102
  }
1979
- };
2103
+ });
1980
2104
 
1981
2105
  // packages/testero/src/runner.ts
1982
- var Runner = class _Runner {
1983
- #settings;
1984
- #focused = false;
1985
- constructor(settings) {
1986
- this.#settings = settings;
1987
- }
1988
- /** Results of every test, and errors of `afterAll` hooks that belong to no test. */
1989
- async run(root) {
1990
- this.#focused = [...root.tests()].some((test) => test.focused);
1991
- const results = [];
1992
- const errors = [];
1993
- await this.#suite(root, [], results, errors);
1994
- return { results, errors };
1995
- }
1996
- /** Whether a test runs: not skipped, focused when others are, matching the name pattern. */
1997
- runs(test) {
1998
- return !test.skipped && (!this.#focused || test.focused) && (this.#settings.namePattern?.test(test.fullName) ?? true);
1999
- }
2000
- async #suite(suite, failed, results, errors) {
2001
- if (![...suite.tests()].some((test) => this.runs(test))) {
2002
- for (const test of suite.tests()) results.push(_Runner.#skip(test));
2003
- return;
2004
- }
2005
- const setup = [...failed];
2006
- if (setup.length === 0)
2007
- for (const hook of suite.hooks.beforeAll) {
2008
- const error = await this.#call(hook.body, hook.timeout);
2009
- if (error !== void 0) {
2010
- setup.push(error);
2011
- break;
2012
- }
2106
+ var Runner;
2107
+ var init_runner = __esm({
2108
+ "packages/testero/src/runner.ts"() {
2109
+ "use strict";
2110
+ init_suite();
2111
+ Runner = class _Runner {
2112
+ static {
2113
+ __name(this, "Runner");
2013
2114
  }
2014
- for (const child of suite.children)
2015
- if (child instanceof Suite)
2016
- await this.#suite(child, setup, results, errors);
2017
- else await this.#test(child, setup, results);
2018
- if (failed.length === 0)
2019
- for (const hook of suite.hooks.afterAll) {
2020
- const error = await this.#call(hook.body, hook.timeout);
2021
- if (error !== void 0) errors.push(error);
2115
+ #settings;
2116
+ #focused = false;
2117
+ constructor(settings) {
2118
+ this.#settings = settings;
2022
2119
  }
2023
- }
2024
- async #test(test, failed, results) {
2025
- if (!this.runs(test)) {
2026
- results.push(_Runner.#skip(test));
2027
- return;
2028
- }
2029
- const start = performance.now();
2030
- const errors = [...failed];
2031
- if (errors.length === 0) {
2032
- this.#settings.beforeTest?.(test);
2033
- for (const hook of _Runner.#hooks(test, "beforeEach")) {
2034
- const error = await this.#call(hook.body, hook.timeout);
2035
- if (error !== void 0) {
2036
- errors.push(error);
2037
- break;
2120
+ /** Results of every test, and errors of `afterAll` hooks that belong to no test. */
2121
+ async run(root) {
2122
+ this.#focused = [...root.tests()].some((test) => test.focused);
2123
+ const results = [];
2124
+ const errors = [];
2125
+ await this.#suite(root, [], results, errors);
2126
+ return { results, errors };
2127
+ }
2128
+ /** Whether a test runs: not skipped, focused when others are, matching the name pattern. */
2129
+ runs(test) {
2130
+ return !test.skipped && (!this.#focused || test.focused) && (this.#settings.namePattern?.test(test.fullName) ?? true);
2131
+ }
2132
+ async #suite(suite, failed, results, errors) {
2133
+ if (![...suite.tests()].some((test) => this.runs(test))) {
2134
+ for (const test of suite.tests()) results.push(_Runner.#skip(test));
2135
+ return;
2136
+ }
2137
+ const setup = [...failed];
2138
+ if (setup.length === 0)
2139
+ for (const hook of suite.hooks.beforeAll) {
2140
+ const error = await this.#call(hook.body, hook.timeout);
2141
+ if (error !== void 0) {
2142
+ setup.push(error);
2143
+ break;
2144
+ }
2145
+ }
2146
+ for (const child of suite.children)
2147
+ if (child instanceof Suite)
2148
+ await this.#suite(child, setup, results, errors);
2149
+ else await this.#test(child, setup, results);
2150
+ if (failed.length === 0)
2151
+ for (const hook of suite.hooks.afterAll) {
2152
+ const error = await this.#call(hook.body, hook.timeout);
2153
+ if (error !== void 0) errors.push(error);
2154
+ }
2155
+ }
2156
+ async #test(test, failed, results) {
2157
+ if (!this.runs(test)) {
2158
+ results.push(_Runner.#skip(test));
2159
+ return;
2160
+ }
2161
+ const start = performance.now();
2162
+ const errors = [...failed];
2163
+ if (errors.length === 0) {
2164
+ this.#settings.beforeTest?.(test);
2165
+ for (const hook of _Runner.#hooks(test, "beforeEach")) {
2166
+ const error = await this.#call(hook.body, hook.timeout);
2167
+ if (error !== void 0) {
2168
+ errors.push(error);
2169
+ break;
2170
+ }
2171
+ }
2172
+ if (errors.length === 0 && test.body) {
2173
+ const error = await this.#call(test.body, test.timeout);
2174
+ if (error !== void 0) errors.push(error);
2175
+ }
2176
+ for (const hook of _Runner.#hooks(test, "afterEach").reverse()) {
2177
+ const error = await this.#call(hook.body, hook.timeout);
2178
+ if (error !== void 0) errors.push(error);
2179
+ }
2180
+ errors.push(...this.#settings.afterTest?.(test) ?? []);
2038
2181
  }
2182
+ results.push({
2183
+ test,
2184
+ status: errors.length > 0 ? "failed" : "passed",
2185
+ duration: performance.now() - start,
2186
+ errors
2187
+ });
2039
2188
  }
2040
- if (errors.length === 0 && test.body) {
2041
- const error = await this.#call(test.body, test.timeout);
2042
- if (error !== void 0) errors.push(error);
2189
+ /** Hooks of a kind from the outermost suite to the test's own. */
2190
+ static #hooks(test, kind) {
2191
+ return test.suite.path.flatMap((suite) => suite.hooks[kind]);
2043
2192
  }
2044
- for (const hook of _Runner.#hooks(test, "afterEach").reverse()) {
2045
- const error = await this.#call(hook.body, hook.timeout);
2046
- if (error !== void 0) errors.push(error);
2193
+ static #skip(test) {
2194
+ return {
2195
+ test,
2196
+ status: test.mode === "todo" ? "todo" : "skipped",
2197
+ duration: 0,
2198
+ errors: []
2199
+ };
2200
+ }
2201
+ /** Calls a body within its timeout, returning what it threw, or undefined. */
2202
+ async #call(body, timeout = this.#settings.timeout) {
2203
+ let timer;
2204
+ const expired = new Promise((_, reject) => {
2205
+ timer = setTimeout(
2206
+ () => reject(
2207
+ new Error(
2208
+ `Exceeded timeout of ${timeout} ms for a test or hook.`
2209
+ )
2210
+ ),
2211
+ timeout
2212
+ );
2213
+ });
2214
+ try {
2215
+ await Promise.race([_Runner.#invoke(body), expired]);
2216
+ return void 0;
2217
+ } catch (error) {
2218
+ return error ?? new Error(`${String(error)} was thrown`);
2219
+ } finally {
2220
+ clearTimeout(timer);
2221
+ }
2222
+ }
2223
+ /** Runs a body as a promise, with a `done` callback when it takes one. */
2224
+ static #invoke(body) {
2225
+ if (body.length === 0)
2226
+ return new Promise((resolve) => resolve(body(() => {
2227
+ })));
2228
+ return new Promise((resolve, reject) => {
2229
+ body(
2230
+ (error) => error === void 0 ? resolve(void 0) : reject(error)
2231
+ );
2232
+ });
2047
2233
  }
2048
- errors.push(...this.#settings.afterTest?.(test) ?? []);
2049
- }
2050
- results.push({
2051
- test,
2052
- status: errors.length > 0 ? "failed" : "passed",
2053
- duration: performance.now() - start,
2054
- errors
2055
- });
2056
- }
2057
- /** Hooks of a kind from the outermost suite to the test's own. */
2058
- static #hooks(test, kind) {
2059
- return test.suite.path.flatMap((suite) => suite.hooks[kind]);
2060
- }
2061
- static #skip(test) {
2062
- return {
2063
- test,
2064
- status: test.mode === "todo" ? "todo" : "skipped",
2065
- duration: 0,
2066
- errors: []
2067
2234
  };
2068
2235
  }
2069
- /** Calls a body within its timeout, returning what it threw, or undefined. */
2070
- async #call(body, timeout = this.#settings.timeout) {
2071
- let timer;
2072
- const expired = new Promise((_, reject) => {
2073
- timer = setTimeout(
2074
- () => reject(
2075
- new Error(
2076
- `Exceeded timeout of ${timeout} ms for a test or hook.`
2077
- )
2078
- ),
2079
- timeout
2080
- );
2081
- });
2082
- try {
2083
- await Promise.race([_Runner.#invoke(body), expired]);
2084
- return void 0;
2085
- } catch (error) {
2086
- return error ?? new Error(`${String(error)} was thrown`);
2087
- } finally {
2088
- clearTimeout(timer);
2089
- }
2090
- }
2091
- /** Runs a body as a promise, with a `done` callback when it takes one. */
2092
- static #invoke(body) {
2093
- if (body.length === 0)
2094
- return new Promise((resolve) => resolve(body(() => {
2095
- })));
2096
- return new Promise((resolve, reject) => {
2097
- body(
2098
- (error) => error === void 0 ? resolve(void 0) : reject(error)
2099
- );
2100
- });
2101
- }
2102
- };
2236
+ });
2103
2237
 
2104
2238
  // packages/testero/src/testero.ts
2105
- var Testero = class {
2106
- #config;
2107
- #options;
2108
- #reporter;
2109
- /** Errors thrown outside of tests, like from a forgotten timer. */
2110
- #stray = [];
2111
- constructor(config, options, reporter = new Reporter(config.root, { verbose: options.verbose })) {
2112
- this.#config = config;
2113
- this.#options = options;
2114
- this.#reporter = reporter;
2115
- }
2116
- /** The test files to run, relative to the root. */
2117
- files() {
2118
- const includes = new Includes(this.#options.testMatch);
2119
- const patterns = this.#options.patterns.map(
2120
- (pattern) => new RegExp(pattern)
2121
- );
2122
- return [...this.#config.files()].filter(
2123
- (file) => includes.matches(file) && (patterns.length === 0 || patterns.some((pattern) => pattern.test(file)))
2124
- ).sort();
2239
+ import { join as join3 } from "node:path";
2240
+ var Testero;
2241
+ var init_testero = __esm({
2242
+ "packages/testero/src/testero.ts"() {
2243
+ "use strict";
2244
+ init_src();
2245
+ init_collector();
2246
+ init_expect();
2247
+ init_globals();
2248
+ init_mock();
2249
+ init_reporter();
2250
+ init_runner();
2251
+ init_transformer();
2252
+ Testero = class {
2253
+ static {
2254
+ __name(this, "Testero");
2255
+ }
2256
+ #config;
2257
+ #options;
2258
+ #reporter;
2259
+ /** Errors thrown outside of tests, like from a forgotten timer. */
2260
+ #stray = [];
2261
+ constructor(config, options, reporter = new Reporter(config.root, { verbose: options.verbose })) {
2262
+ this.#config = config;
2263
+ this.#options = options;
2264
+ this.#reporter = reporter;
2265
+ }
2266
+ /** The test files to run, relative to the root. */
2267
+ files() {
2268
+ const includes = new Includes(this.#options.testMatch);
2269
+ const patterns = this.#options.patterns.map(
2270
+ (pattern) => new RegExp(pattern)
2271
+ );
2272
+ return [...this.#config.files()].filter(
2273
+ (file) => includes.matches(file) && (patterns.length === 0 || patterns.some((pattern) => pattern.test(file)))
2274
+ ).sort();
2275
+ }
2276
+ /** Runs every test file, returns whether all of them passed. */
2277
+ async run() {
2278
+ const start = performance.now();
2279
+ const files = this.files();
2280
+ if (files.length === 0) {
2281
+ this.#reporter.none(this.#options.patterns);
2282
+ return this.#options.passWithNoTests;
2283
+ }
2284
+ process.setSourceMapsEnabled(true);
2285
+ const stray = /* @__PURE__ */ __name((error) => this.#stray.push(error), "stray");
2286
+ process.on("uncaughtException", stray);
2287
+ process.on("unhandledRejection", stray);
2288
+ const transformer = new Transformer();
2289
+ const results = [];
2290
+ try {
2291
+ for (const file of files) {
2292
+ const result = await this.#file(file, transformer);
2293
+ results.push(result);
2294
+ this.#reporter.file(result);
2295
+ if (this.#options.bail && Reporter.failed(result)) break;
2296
+ }
2297
+ } finally {
2298
+ transformer.dispose();
2299
+ process.off("uncaughtException", stray);
2300
+ process.off("unhandledRejection", stray);
2301
+ }
2302
+ this.#reporter.summary(results, performance.now() - start);
2303
+ return !results.some(Reporter.failed);
2304
+ }
2305
+ async #file(file, transformer) {
2306
+ const start = performance.now();
2307
+ const finish = /* @__PURE__ */ __name((results2, errors2) => {
2308
+ errors2.push(...this.#stray.splice(0));
2309
+ Mocks.restoreAll();
2310
+ Mocks.forget();
2311
+ return {
2312
+ file,
2313
+ results: results2,
2314
+ errors: errors2,
2315
+ duration: performance.now() - start
2316
+ };
2317
+ }, "finish");
2318
+ const collector = new Collector();
2319
+ const expect = new Expect();
2320
+ const settings = {
2321
+ timeout: this.#options.testTimeout,
2322
+ namePattern: this.#options.namePattern,
2323
+ beforeTest: /* @__PURE__ */ __name(() => {
2324
+ expect.reset();
2325
+ if (this.#options.restoreMocks) Mocks.restoreAll();
2326
+ else if (this.#options.resetMocks) Mocks.resetAll();
2327
+ else if (this.#options.clearMocks) Mocks.clearAll();
2328
+ }, "beforeTest"),
2329
+ afterTest: /* @__PURE__ */ __name(() => expect.verify(), "afterTest")
2330
+ };
2331
+ Globals.install(collector, expect, settings);
2332
+ try {
2333
+ await import(await transformer.compile(join3(this.#config.root, file)));
2334
+ } catch (error) {
2335
+ return finish([], [error]);
2336
+ } finally {
2337
+ collector.close();
2338
+ }
2339
+ if ([...collector.root.tests()].length === 0)
2340
+ return finish(
2341
+ [],
2342
+ [new Error("Your test suite must contain at least one test.")]
2343
+ );
2344
+ const { results, errors } = await new Runner(settings).run(
2345
+ collector.root
2346
+ );
2347
+ return finish(results, errors);
2348
+ }
2349
+ };
2125
2350
  }
2126
- /** Runs every test file, returns whether all of them passed. */
2127
- async run() {
2128
- const start = performance.now();
2129
- const files = this.files();
2130
- if (files.length === 0) {
2131
- this.#reporter.none(this.#options.patterns);
2132
- return this.#options.passWithNoTests;
2133
- }
2134
- process.setSourceMapsEnabled(true);
2135
- const stray = (error) => this.#stray.push(error);
2136
- process.on("uncaughtException", stray);
2137
- process.on("unhandledRejection", stray);
2138
- const transformer = new Transformer();
2139
- const results = [];
2140
- try {
2141
- for (const file of files) {
2142
- const result = await this.#file(file, transformer);
2143
- results.push(result);
2144
- this.#reporter.file(result);
2145
- if (this.#options.bail && Reporter.failed(result)) break;
2146
- }
2147
- } finally {
2148
- transformer.dispose();
2149
- process.off("uncaughtException", stray);
2150
- process.off("unhandledRejection", stray);
2151
- }
2152
- this.#reporter.summary(results, performance.now() - start);
2153
- return !results.some(Reporter.failed);
2351
+ });
2352
+
2353
+ // packages/testero/src/arguments.ts
2354
+ var Arguments;
2355
+ var init_arguments = __esm({
2356
+ "packages/testero/src/arguments.ts"() {
2357
+ "use strict";
2358
+ Arguments = class {
2359
+ static {
2360
+ __name(this, "Arguments");
2361
+ }
2362
+ static usage = `Usage: testero [patterns...] [options]
2363
+
2364
+ Runs the test files whose paths match a pattern, every test file without patterns.
2365
+
2366
+ Options:
2367
+ -t, --testNamePattern <regex> run only tests whose name matches
2368
+ --bail stop after the first failing test file
2369
+ --verbose list every test
2370
+ --listTests print the test files instead of running them
2371
+ --passWithNoTests succeed when there are no test files
2372
+ --testTimeout <ms> milliseconds a test or hook may take
2373
+ --config [js] write the default testero.config.ts, or .js
2374
+ -h, --help show this help
2375
+ `;
2376
+ patterns = [];
2377
+ rules = {};
2378
+ namePattern;
2379
+ listTests = false;
2380
+ help = false;
2381
+ constructor(argv) {
2382
+ for (let index = 0; index < argv.length; index++) {
2383
+ const argument = argv[index];
2384
+ const [name, inline] = argument.split(/=(.*)/s);
2385
+ const value = /* @__PURE__ */ __name(() => {
2386
+ const next = inline ?? argv[++index];
2387
+ if (next === void 0)
2388
+ throw new Error(`${name} needs a value.`);
2389
+ return next;
2390
+ }, "value");
2391
+ switch (name) {
2392
+ case "-t":
2393
+ case "--testNamePattern":
2394
+ this.namePattern = new RegExp(value());
2395
+ break;
2396
+ case "--testTimeout":
2397
+ this.rules.testTimeout = Number(value());
2398
+ break;
2399
+ case "--bail":
2400
+ case "--verbose":
2401
+ case "--passWithNoTests":
2402
+ this.rules[name.slice(2)] = true;
2403
+ break;
2404
+ case "--listTests":
2405
+ this.listTests = true;
2406
+ break;
2407
+ case "-h":
2408
+ case "--help":
2409
+ this.help = true;
2410
+ break;
2411
+ default:
2412
+ if (name.startsWith("-"))
2413
+ throw new Error(`Unknown option ${name}.`);
2414
+ this.patterns.push(argument);
2415
+ }
2416
+ }
2417
+ }
2418
+ /** The configured rules, with the ones given on the command line instead. */
2419
+ options(rules2) {
2420
+ return {
2421
+ ...rules2,
2422
+ ...this.rules,
2423
+ patterns: this.patterns,
2424
+ namePattern: this.namePattern
2425
+ };
2426
+ }
2427
+ };
2154
2428
  }
2155
- async #file(file, transformer) {
2156
- const start = performance.now();
2157
- const finish = (results2, errors2) => {
2158
- errors2.push(...this.#stray.splice(0));
2159
- Mocks.restoreAll();
2160
- Mocks.forget();
2161
- return {
2162
- file,
2163
- results: results2,
2164
- errors: errors2,
2165
- duration: performance.now() - start
2166
- };
2429
+ });
2430
+
2431
+ // packages/testero/src/config.ts
2432
+ var extensions, rules, config_default;
2433
+ var init_config2 = __esm({
2434
+ "packages/testero/src/config.ts"() {
2435
+ "use strict";
2436
+ extensions = "{js,jsx,ts,tsx,mjs,cjs,mts,cts}";
2437
+ rules = {
2438
+ testMatch: [
2439
+ `**/__tests__/**/*.${extensions}`,
2440
+ `**/*.{spec,test}.${extensions}`
2441
+ ],
2442
+ testTimeout: 5e3,
2443
+ bail: false,
2444
+ verbose: false,
2445
+ passWithNoTests: false,
2446
+ clearMocks: false,
2447
+ resetMocks: false,
2448
+ restoreMocks: false
2167
2449
  };
2168
- const collector = new Collector();
2169
- const expect = new Expect();
2170
- const settings = {
2171
- timeout: this.#options.testTimeout,
2172
- namePattern: this.#options.namePattern,
2173
- beforeTest: () => {
2174
- expect.reset();
2175
- if (this.#options.restoreMocks) Mocks.restoreAll();
2176
- else if (this.#options.resetMocks) Mocks.resetAll();
2177
- else if (this.#options.clearMocks) Mocks.clearAll();
2178
- },
2179
- afterTest: () => expect.verify()
2450
+ config_default = {
2451
+ ignore: [".git", "node_modules"],
2452
+ rules
2180
2453
  };
2181
- Globals.install(collector, expect, settings);
2454
+ }
2455
+ });
2456
+
2457
+ // packages/testero/src/index.ts
2458
+ var index_exports = {};
2459
+ var init_index = __esm({
2460
+ async "packages/testero/src/index.ts"() {
2461
+ "use strict";
2462
+ init_src();
2463
+ init_arguments();
2464
+ init_config2();
2465
+ init_testero();
2182
2466
  try {
2183
- await import(await transformer.compile(join3(this.#config.root, file)));
2467
+ if (Config.init("testero", config_default)) process.exit();
2468
+ const args = new Arguments(process.argv.slice(2));
2469
+ if (args.help) process.stdout.write(Arguments.usage);
2470
+ else {
2471
+ const config = await Config.load("testero", config_default);
2472
+ const testero = new Testero(config, args.options(config.rules));
2473
+ if (args.listTests)
2474
+ for (const file of testero.files()) console.log(file);
2475
+ else if (!await testero.run()) process.exitCode = 1;
2476
+ }
2184
2477
  } catch (error) {
2185
- return finish([], [error]);
2186
- } finally {
2187
- collector.close();
2478
+ console.error(error instanceof Error ? error.message : error);
2479
+ process.exitCode = 1;
2188
2480
  }
2189
- if ([...collector.root.tests()].length === 0)
2190
- return finish(
2191
- [],
2192
- [new Error("Your test suite must contain at least one test.")]
2193
- );
2194
- const { results, errors } = await new Runner(settings).run(
2195
- collector.root
2196
- );
2197
- return finish(results, errors);
2198
2481
  }
2199
- };
2482
+ });
2200
2483
 
2201
- // packages/testero/src/index.ts
2202
- try {
2203
- if (Config.init("testero", config_default)) process.exit();
2204
- const args = new Arguments(process.argv.slice(2));
2205
- if (args.help) process.stdout.write(Arguments.usage);
2206
- else {
2207
- const config = await Config.load("testero", config_default);
2208
- const testero = new Testero(config, args.options(config.rules));
2209
- if (args.listTests)
2210
- for (const file of testero.files()) console.log(file);
2211
- else if (!await testero.run()) process.exitCode = 1;
2212
- }
2213
- } catch (error) {
2214
- console.error(error instanceof Error ? error.message : error);
2215
- process.exitCode = 1;
2216
- }
2484
+ // packages/testero/src/main.ts
2485
+ init_testero();
2486
+ init_testero();
2487
+ if (import.meta.main) await init_index().then(() => index_exports);
2488
+ export {
2489
+ Testero as default
2490
+ };