@funktechno/multi-assert 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +113 -2
- package/dist/chunk-UP6HQ3WU.mjs +270 -0
- package/dist/chunk-UP6HQ3WU.mjs.map +1 -0
- package/dist/index.d.mts +33 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +299 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +13 -0
- package/dist/index.mjs.map +1 -0
- package/dist/jest.d.mts +24 -0
- package/dist/jest.d.ts +24 -0
- package/dist/jest.js +301 -0
- package/dist/jest.js.map +1 -0
- package/dist/jest.mjs +30 -0
- package/dist/jest.mjs.map +1 -0
- package/dist/types-BfMUWmjj.d.mts +49 -0
- package/dist/types-BfMUWmjj.d.ts +49 -0
- package/package.json +91 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 lastlink
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,114 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @funktechno/multi-assert
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Run a group of assertions in any Node test framework, keep going past failures, and get **one**
|
|
4
|
+
readable error that lists every failure. Works with Jest, Vitest, `node:test`, mocha/chai,
|
|
5
|
+
Playwright and anything else that throws on a failed assertion. No runtime dependencies.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install --save-dev @funktechno/multi-assert
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { multiAssert } from "@funktechno/multi-assert";
|
|
13
|
+
|
|
14
|
+
multiAssert([
|
|
15
|
+
() => expect(res.status).toBe(200),
|
|
16
|
+
() => expect(res.body.id).toBe(42),
|
|
17
|
+
() => assert.equal(res.headers["content-type"], "application/json"),
|
|
18
|
+
]);
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
2 of 3 assertions failed
|
|
23
|
+
|
|
24
|
+
✕ 1) expected 404 to be 200 // Object.is equality
|
|
25
|
+
→ src/api/__tests__/http.spec.ts:38:24
|
|
26
|
+
|
|
27
|
+
✕ 3) Expected values to be strictly equal:
|
|
28
|
+
+ actual - expected
|
|
29
|
+
+ 'text/html'
|
|
30
|
+
- 'application/json'
|
|
31
|
+
→ src/api/__tests__/http.spec.ts:40:14
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Call forms
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
// bare functions
|
|
38
|
+
multiAssert([() => a(), () => b()]);
|
|
39
|
+
|
|
40
|
+
// labelled, as tuples or as an object
|
|
41
|
+
multiAssert([["status is 200", () => a()], ["has id", () => b()]]);
|
|
42
|
+
multiAssert({ "status is 200": () => a(), "has id": () => b() });
|
|
43
|
+
|
|
44
|
+
// async checks run in order
|
|
45
|
+
await multiAssertAsync([async () => expect(await load()).toBe(1), () => b()]);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`multiAssert` is synchronous. A check that returns a promise is reported as a failure that tells
|
|
49
|
+
you to use `multiAssertAsync`, so a rejected promise is never silently dropped.
|
|
50
|
+
|
|
51
|
+
## Options
|
|
52
|
+
|
|
53
|
+
| Option | Default | Meaning |
|
|
54
|
+
| --- | --- | --- |
|
|
55
|
+
| `message` | none | Heading shown after the summary line, e.g. `"user API response"`. |
|
|
56
|
+
| `rethrowSingle` | `true` | If exactly one check fails, and it has no label and the group has no `message`, the original error is rethrown so the framework shows its native diff. |
|
|
57
|
+
| `color` | `"auto"` | Keep colors only on a color TTY with `NO_COLOR` and `CI` unset, so JUnit XML stays clean. |
|
|
58
|
+
| `stackFrames` | `1` | Stack frames shown under each failure (`0` hides them). |
|
|
59
|
+
| `adapters` | `[]` | Custom [error adapters](#custom-adapters), tried before the built-in ones. |
|
|
60
|
+
|
|
61
|
+
Set defaults once, for example in a test setup file: `configure({ stackFrames: 0 })`.
|
|
62
|
+
|
|
63
|
+
## The error
|
|
64
|
+
|
|
65
|
+
Two or more failures throw a `MultiAssertError` (an `AggregateError`):
|
|
66
|
+
|
|
67
|
+
- `errors`: the original errors, untouched, so `instanceof`, `actual` and `expected` survive;
|
|
68
|
+
- `failures`: `{ index, label?, message, actual?, expected?, operator?, location? }[]`;
|
|
69
|
+
- `passed` and `total` counts;
|
|
70
|
+
- `toJSON()` returns `{ name, message, passed, total, failures }`. `failures`, `passed` and `total` are non-enumerable so reporters that dump an error's own properties (Vitest) do not print them twice.
|
|
71
|
+
|
|
72
|
+
The first line of the message is a short summary because JUnit `message` attributes and CI
|
|
73
|
+
annotation titles usually show only that. `error.stack` points at the call to `multiAssert`, so
|
|
74
|
+
reporter code frames land in your test.
|
|
75
|
+
|
|
76
|
+
## Custom adapters
|
|
77
|
+
|
|
78
|
+
Built in: `expect` matcher results (Jest, Vitest, Playwright), `node:assert`, chai, plain
|
|
79
|
+
`Error` and non-Error throws. For an in-house assertion library:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
multiAssert(checks, {
|
|
83
|
+
adapters: [
|
|
84
|
+
{
|
|
85
|
+
matches: (e) => e instanceof MyAssertError,
|
|
86
|
+
normalize: (e) => ({ message: (e as MyAssertError).detail }),
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Jest matcher
|
|
93
|
+
|
|
94
|
+
`import "@funktechno/multi-assert/jest"` (or `installMultiAssertMatchers(expect)`) adds `expect(value).toPassAll([...])`, where each check receives `value`:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
expect(res).toPassAll([
|
|
98
|
+
["status", (r) => expect(r.status).toBe(200)],
|
|
99
|
+
["id", (r) => expect(r.body.id).toBe(42)],
|
|
100
|
+
]);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Types for the subpath need `moduleResolution` `node16`, `nodenext` or `bundler`.
|
|
104
|
+
|
|
105
|
+
## Development
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
npm ci
|
|
109
|
+
npm run lint
|
|
110
|
+
npm test
|
|
111
|
+
npm run build
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
MIT
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
// src/MultiAssertError.ts
|
|
2
|
+
var MultiAssertError = class extends AggregateError {
|
|
3
|
+
constructor(errors, message, failures, total) {
|
|
4
|
+
super(errors, message);
|
|
5
|
+
this.name = "MultiAssertError";
|
|
6
|
+
const hidden = { enumerable: false, writable: false, configurable: true };
|
|
7
|
+
Object.defineProperties(this, {
|
|
8
|
+
failures: { value: failures, ...hidden },
|
|
9
|
+
total: { value: total, ...hidden },
|
|
10
|
+
passed: { value: total - failures.length, ...hidden }
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Keeps `name` and `message` so reporters that serialise errors with `toJSON` (Vitest) still
|
|
15
|
+
* show the summary, and adds the structured failures.
|
|
16
|
+
*/
|
|
17
|
+
toJSON() {
|
|
18
|
+
return {
|
|
19
|
+
name: this.name,
|
|
20
|
+
message: this.message,
|
|
21
|
+
passed: this.passed,
|
|
22
|
+
total: this.total,
|
|
23
|
+
failures: this.failures
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// src/format.ts
|
|
29
|
+
var red = (s) => `\x1B[31m${s}\x1B[39m`;
|
|
30
|
+
var dim = (s) => `\x1B[2m${s}\x1B[22m`;
|
|
31
|
+
function shouldUseColor(color) {
|
|
32
|
+
if (color === true || color === false) return color;
|
|
33
|
+
if (typeof process === "undefined") return false;
|
|
34
|
+
const env = process.env ?? {};
|
|
35
|
+
return Boolean(process.stdout?.isTTY) && !env.NO_COLOR && !env.CI;
|
|
36
|
+
}
|
|
37
|
+
function summaryLine(failed, total, heading) {
|
|
38
|
+
const noun = total === 1 ? "assertion" : "assertions";
|
|
39
|
+
return `${failed} of ${total} ${noun} failed${heading ? ` (${heading})` : ""}`;
|
|
40
|
+
}
|
|
41
|
+
var INDENT = " ";
|
|
42
|
+
function formatFailures(failures, total, options) {
|
|
43
|
+
const paint = (fn, s) => options.color ? fn(s) : s;
|
|
44
|
+
const blocks = failures.map((failure) => {
|
|
45
|
+
const lines = failure.message.split("\n");
|
|
46
|
+
const title = failure.label ?? lines.shift() ?? "";
|
|
47
|
+
const out = [` ${paint(red, "\u2715")} ${failure.index}) ${title}`];
|
|
48
|
+
for (const line of lines) out.push(line ? `${INDENT}${line}` : "");
|
|
49
|
+
const frames = (options.frames.get(failure) ?? []).slice(0, options.stackFrames);
|
|
50
|
+
for (const frame of frames) out.push(paint(dim, `${INDENT}\u2192 ${frame}`));
|
|
51
|
+
return out.join("\n");
|
|
52
|
+
});
|
|
53
|
+
return `${summaryLine(failures.length, total, options.heading)}
|
|
54
|
+
|
|
55
|
+
${blocks.join("\n\n")}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/normalize.ts
|
|
59
|
+
var ESC = String.fromCharCode(27);
|
|
60
|
+
var ANSI = new RegExp(`(?:${ESC}|\\\\u001b|\\\\x1b)\\[[0-9;?]*[A-Za-z]`, "g");
|
|
61
|
+
var stripAnsi = (text) => text.replace(ANSI, "");
|
|
62
|
+
var isObject = (value) => typeof value === "object" && value !== null;
|
|
63
|
+
function safeInspect(value, max = 500) {
|
|
64
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
65
|
+
let text;
|
|
66
|
+
try {
|
|
67
|
+
text = JSON.stringify(
|
|
68
|
+
value,
|
|
69
|
+
(_key, v) => {
|
|
70
|
+
if (typeof v === "bigint") return `${v}n`;
|
|
71
|
+
if (typeof v === "function") return `[Function ${v.name || "anonymous"}]`;
|
|
72
|
+
if (typeof v === "symbol") return v.toString();
|
|
73
|
+
if (isObject(v)) {
|
|
74
|
+
if (seen.has(v)) return "[Circular]";
|
|
75
|
+
seen.add(v);
|
|
76
|
+
}
|
|
77
|
+
return v;
|
|
78
|
+
},
|
|
79
|
+
2
|
|
80
|
+
) ?? String(value);
|
|
81
|
+
} catch {
|
|
82
|
+
text = String(value);
|
|
83
|
+
}
|
|
84
|
+
return text.length > max ? `${text.slice(0, max)}\u2026` : text;
|
|
85
|
+
}
|
|
86
|
+
var matcherAdapter = {
|
|
87
|
+
name: "matcher",
|
|
88
|
+
matches: (e) => isObject(e) && isObject(e.matcherResult),
|
|
89
|
+
normalize(e) {
|
|
90
|
+
const result = e.matcherResult;
|
|
91
|
+
let message = result.message;
|
|
92
|
+
if (typeof message === "function") message = message();
|
|
93
|
+
if (Array.isArray(message)) message = message.join("\n");
|
|
94
|
+
return {
|
|
95
|
+
message: typeof message === "string" ? message : String(e.message ?? ""),
|
|
96
|
+
actual: result.actual,
|
|
97
|
+
expected: result.expected,
|
|
98
|
+
operator: typeof result.name === "string" ? result.name : void 0
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
var assertionAdapter = {
|
|
103
|
+
name: "assertion",
|
|
104
|
+
matches: (e) => e instanceof Error && ("actual" in e || "expected" in e || "operator" in e || e.name === "AssertionError"),
|
|
105
|
+
normalize(e) {
|
|
106
|
+
const err = e;
|
|
107
|
+
return {
|
|
108
|
+
message: err.message,
|
|
109
|
+
actual: err.actual,
|
|
110
|
+
expected: err.expected,
|
|
111
|
+
operator: typeof err.operator === "string" ? err.operator : void 0
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
var errorAdapter = {
|
|
116
|
+
name: "error",
|
|
117
|
+
matches: (e) => e instanceof Error || isObject(e) && typeof e.message === "string",
|
|
118
|
+
normalize(e) {
|
|
119
|
+
const err = e;
|
|
120
|
+
const message = err.message ? `${err.name}: ${err.message}` : String(err.name ?? "Error");
|
|
121
|
+
return { message };
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
var otherAdapter = {
|
|
125
|
+
name: "other",
|
|
126
|
+
matches: () => true,
|
|
127
|
+
normalize: (e) => ({ message: `[non-Error thrown] ${safeInspect(e)}` })
|
|
128
|
+
};
|
|
129
|
+
var builtInAdapters = [
|
|
130
|
+
matcherAdapter,
|
|
131
|
+
assertionAdapter,
|
|
132
|
+
errorAdapter,
|
|
133
|
+
otherAdapter
|
|
134
|
+
];
|
|
135
|
+
function normalizeError(error, custom = []) {
|
|
136
|
+
const adapter = [...custom, ...builtInAdapters].find((a) => {
|
|
137
|
+
try {
|
|
138
|
+
return a.matches(error);
|
|
139
|
+
} catch {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
const normalized = adapter.normalize(error);
|
|
144
|
+
return { ...normalized, message: stripAnsi(normalized.message) };
|
|
145
|
+
}
|
|
146
|
+
var OWN_FRAME = /[\\/]multi-assert[\\/](?:src|dist)[\\/]/;
|
|
147
|
+
var FRAME_LOCATION = /\(?((?:file:\/\/)?[^\s()]+?:\d+:\d+)\)?\s*$/;
|
|
148
|
+
function userFrames(stack) {
|
|
149
|
+
if (!stack) return [];
|
|
150
|
+
return stack.split("\n").filter((line) => /^\s*at /.test(line)).filter(
|
|
151
|
+
(line) => !line.includes("node_modules") && !line.includes("node:") && !OWN_FRAME.test(line)
|
|
152
|
+
).map((line) => FRAME_LOCATION.exec(line)?.[1]).filter((loc) => Boolean(loc)).map(relativize);
|
|
153
|
+
}
|
|
154
|
+
function relativize(location) {
|
|
155
|
+
const withoutScheme = location.replace(/^file:\/\//, "").replace(/^\/([A-Za-z]:)/, "$1");
|
|
156
|
+
const cwd = typeof process !== "undefined" && process.cwd ? process.cwd() : void 0;
|
|
157
|
+
if (!cwd) return withoutScheme;
|
|
158
|
+
const norm = (p) => p.replace(/\\/g, "/");
|
|
159
|
+
const base = norm(cwd).replace(/\/$/, "") + "/";
|
|
160
|
+
const loc = norm(withoutScheme);
|
|
161
|
+
return loc.toLowerCase().startsWith(base.toLowerCase()) ? loc.slice(base.length) : loc;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/multiAssert.ts
|
|
165
|
+
var defaults = {
|
|
166
|
+
rethrowSingle: true,
|
|
167
|
+
color: "auto",
|
|
168
|
+
stackFrames: 1,
|
|
169
|
+
adapters: []
|
|
170
|
+
};
|
|
171
|
+
function configure(options) {
|
|
172
|
+
Object.assign(defaults, options);
|
|
173
|
+
}
|
|
174
|
+
function toEntries(checks) {
|
|
175
|
+
const pairs = Array.isArray(checks) ? checks.map((c) => Array.isArray(c) ? [c[0], c[1]] : [void 0, c]) : Object.entries(checks ?? {});
|
|
176
|
+
return pairs.map(([label, fn], i) => {
|
|
177
|
+
if (typeof fn !== "function") {
|
|
178
|
+
throw new TypeError(
|
|
179
|
+
`multiAssert: check ${i + 1}${label ? ` ("${label}")` : ""} is not a function`
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
return { label, fn };
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
var isThenable = (value) => typeof value?.then === "function";
|
|
186
|
+
function captureSite(from) {
|
|
187
|
+
const holder = {};
|
|
188
|
+
const capture = Error.captureStackTrace;
|
|
189
|
+
if (capture) {
|
|
190
|
+
capture(holder, from);
|
|
191
|
+
return holder.stack;
|
|
192
|
+
}
|
|
193
|
+
return new Error().stack;
|
|
194
|
+
}
|
|
195
|
+
function finish(caught, total, options, site) {
|
|
196
|
+
if (caught.length === 0) return;
|
|
197
|
+
const settings = { ...defaults, ...options };
|
|
198
|
+
const [only] = caught;
|
|
199
|
+
if (caught.length === 1 && settings.rethrowSingle && only.error instanceof Error && !only.entry.label && !settings.message) {
|
|
200
|
+
throw only.error;
|
|
201
|
+
}
|
|
202
|
+
const frames = /* @__PURE__ */ new Map();
|
|
203
|
+
const failures = caught.map(({ entry, index, error: error2 }) => {
|
|
204
|
+
const normalized = normalizeError(error2, settings.adapters);
|
|
205
|
+
const failure = {
|
|
206
|
+
index,
|
|
207
|
+
label: entry.label,
|
|
208
|
+
message: normalized.message,
|
|
209
|
+
actual: normalized.actual,
|
|
210
|
+
expected: normalized.expected,
|
|
211
|
+
operator: normalized.operator
|
|
212
|
+
};
|
|
213
|
+
const list = userFrames(error2 instanceof Error ? error2.stack : void 0);
|
|
214
|
+
failure.location = list[0];
|
|
215
|
+
frames.set(failure, list);
|
|
216
|
+
return failure;
|
|
217
|
+
});
|
|
218
|
+
const message = formatFailures(failures, total, {
|
|
219
|
+
heading: settings.message,
|
|
220
|
+
color: shouldUseColor(settings.color),
|
|
221
|
+
stackFrames: settings.stackFrames,
|
|
222
|
+
frames
|
|
223
|
+
});
|
|
224
|
+
const error = new MultiAssertError(
|
|
225
|
+
caught.map((c) => c.error),
|
|
226
|
+
message,
|
|
227
|
+
failures,
|
|
228
|
+
total
|
|
229
|
+
);
|
|
230
|
+
const callSite = (site ?? "").split("\n").filter((l) => /^\s*at /.test(l));
|
|
231
|
+
error.stack = [`${error.name}: ${message}`, ...callSite].join("\n");
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
function multiAssert(checks, options = {}) {
|
|
235
|
+
const entries = toEntries(checks);
|
|
236
|
+
const caught = [];
|
|
237
|
+
entries.forEach((entry, i) => {
|
|
238
|
+
try {
|
|
239
|
+
const result = entry.fn();
|
|
240
|
+
if (isThenable(result)) {
|
|
241
|
+
result.then(void 0, () => void 0);
|
|
242
|
+
throw new TypeError("check returned a Promise; use multiAssertAsync() for async checks");
|
|
243
|
+
}
|
|
244
|
+
} catch (error) {
|
|
245
|
+
caught.push({ entry, index: i + 1, error });
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
finish(caught, entries.length, options, caught.length ? captureSite(multiAssert) : void 0);
|
|
249
|
+
}
|
|
250
|
+
async function multiAssertAsync(checks, options = {}) {
|
|
251
|
+
const site = captureSite(multiAssertAsync);
|
|
252
|
+
const entries = toEntries(checks);
|
|
253
|
+
const caught = [];
|
|
254
|
+
for (const [i, entry] of entries.entries()) {
|
|
255
|
+
try {
|
|
256
|
+
await entry.fn();
|
|
257
|
+
} catch (error) {
|
|
258
|
+
caught.push({ entry, index: i + 1, error });
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
finish(caught, entries.length, options, site);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export {
|
|
265
|
+
MultiAssertError,
|
|
266
|
+
configure,
|
|
267
|
+
multiAssert,
|
|
268
|
+
multiAssertAsync
|
|
269
|
+
};
|
|
270
|
+
//# sourceMappingURL=chunk-UP6HQ3WU.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/MultiAssertError.ts","../src/format.ts","../src/normalize.ts","../src/multiAssert.ts"],"sourcesContent":["import type { Failure } from \"./types\";\n\n/** Thrown when two or more checks in a group fail (or one fails and can't be rethrown as is). */\nexport class MultiAssertError extends AggregateError {\n declare readonly failures: Failure[];\n declare readonly passed: number;\n declare readonly total: number;\n\n constructor(errors: unknown[], message: string, failures: Failure[], total: number) {\n super(errors, message);\n this.name = \"MultiAssertError\";\n // Non-enumerable so reporters that dump an error's own properties (Vitest does) don't print\n // the same failures twice.\n const hidden = { enumerable: false, writable: false, configurable: true };\n Object.defineProperties(this, {\n failures: { value: failures, ...hidden },\n total: { value: total, ...hidden },\n passed: { value: total - failures.length, ...hidden },\n });\n }\n\n /**\n * Keeps `name` and `message` so reporters that serialise errors with `toJSON` (Vitest) still\n * show the summary, and adds the structured failures.\n */\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n passed: this.passed,\n total: this.total,\n failures: this.failures,\n };\n }\n}\n","import type { Failure, MultiAssertOptions } from \"./types\";\n\nconst red = (s: string) => `\\u001b[31m${s}\\u001b[39m`;\nconst dim = (s: string) => `\\u001b[2m${s}\\u001b[22m`;\n\nexport function shouldUseColor(color: MultiAssertOptions[\"color\"]): boolean {\n if (color === true || color === false) return color;\n if (typeof process === \"undefined\") return false;\n const env = process.env ?? {};\n return Boolean(process.stdout?.isTTY) && !env.NO_COLOR && !env.CI;\n}\n\nexport function summaryLine(failed: number, total: number, heading?: string): string {\n const noun = total === 1 ? \"assertion\" : \"assertions\";\n return `${failed} of ${total} ${noun} failed${heading ? ` (${heading})` : \"\"}`;\n}\n\nconst INDENT = \" \";\n\nexport interface FormatOptions {\n heading?: string;\n color: boolean;\n stackFrames: number;\n frames: Map<Failure, string[]>;\n}\n\nexport function formatFailures(failures: Failure[], total: number, options: FormatOptions): string {\n const paint = (fn: (s: string) => string, s: string) => (options.color ? fn(s) : s);\n const blocks = failures.map((failure) => {\n const lines = failure.message.split(\"\\n\");\n const title = failure.label ?? lines.shift() ?? \"\";\n const out = [` ${paint(red, \"✕\")} ${failure.index}) ${title}`];\n for (const line of lines) out.push(line ? `${INDENT}${line}` : \"\");\n const frames = (options.frames.get(failure) ?? []).slice(0, options.stackFrames);\n for (const frame of frames) out.push(paint(dim, `${INDENT}→ ${frame}`));\n return out.join(\"\\n\");\n });\n return `${summaryLine(failures.length, total, options.heading)}\\n\\n${blocks.join(\"\\n\\n\")}`;\n}\n","import type { ErrorAdapter, NormalizedError } from \"./types\";\n\nconst ESC = String.fromCharCode(27);\n/** Every ANSI CSI sequence, plus the already-escaped text form some reporters print. */\nconst ANSI = new RegExp(`(?:${ESC}|\\\\\\\\u001b|\\\\\\\\x1b)\\\\[[0-9;?]*[A-Za-z]`, \"g\");\n\nexport const stripAnsi = (text: string): string => text.replace(ANSI, \"\");\n\nconst isObject = (value: unknown): value is Record<string, any> =>\n typeof value === \"object\" && value !== null;\n\n/** Cycle-safe, truncated rendering for values that aren't Errors. */\nexport function safeInspect(value: unknown, max = 500): string {\n const seen = new WeakSet<object>();\n let text: string;\n try {\n text =\n JSON.stringify(\n value,\n (_key, v) => {\n if (typeof v === \"bigint\") return `${v}n`;\n if (typeof v === \"function\") return `[Function ${v.name || \"anonymous\"}]`;\n if (typeof v === \"symbol\") return v.toString();\n if (isObject(v)) {\n if (seen.has(v)) return \"[Circular]\";\n seen.add(v);\n }\n return v;\n },\n 2,\n ) ?? String(value);\n } catch {\n text = String(value);\n }\n return text.length > max ? `${text.slice(0, max)}…` : text;\n}\n\n/** Jest / Vitest / Playwright `expect` errors carry a `matcherResult`. */\nconst matcherAdapter: ErrorAdapter = {\n name: \"matcher\",\n matches: (e) => isObject(e) && isObject(e.matcherResult),\n normalize(e) {\n const result = (e as any).matcherResult;\n let message: unknown = result.message;\n if (typeof message === \"function\") message = message();\n if (Array.isArray(message)) message = message.join(\"\\n\");\n return {\n message: typeof message === \"string\" ? message : String((e as Error).message ?? \"\"),\n actual: result.actual,\n expected: result.expected,\n operator: typeof result.name === \"string\" ? result.name : undefined,\n };\n },\n};\n\n/** node:assert, chai, uvu and Vitest's chai-based errors expose actual / expected. */\nconst assertionAdapter: ErrorAdapter = {\n name: \"assertion\",\n matches: (e) =>\n e instanceof Error &&\n (\"actual\" in e || \"expected\" in e || \"operator\" in e || e.name === \"AssertionError\"),\n normalize(e) {\n const err = e as Error & { actual?: unknown; expected?: unknown; operator?: string };\n return {\n message: err.message,\n actual: err.actual,\n expected: err.expected,\n operator: typeof err.operator === \"string\" ? err.operator : undefined,\n };\n },\n};\n\nconst errorAdapter: ErrorAdapter = {\n name: \"error\",\n matches: (e) => e instanceof Error || (isObject(e) && typeof e.message === \"string\"),\n normalize(e) {\n const err = e as Error;\n const message = err.message ? `${err.name}: ${err.message}` : String(err.name ?? \"Error\");\n return { message };\n },\n};\n\nconst otherAdapter: ErrorAdapter = {\n name: \"other\",\n matches: () => true,\n normalize: (e) => ({ message: `[non-Error thrown] ${safeInspect(e)}` }),\n};\n\nexport const builtInAdapters: ErrorAdapter[] = [\n matcherAdapter,\n assertionAdapter,\n errorAdapter,\n otherAdapter,\n];\n\nexport function normalizeError(error: unknown, custom: ErrorAdapter[] = []): NormalizedError {\n const adapter = [...custom, ...builtInAdapters].find((a) => {\n try {\n return a.matches(error);\n } catch {\n return false;\n }\n }) as ErrorAdapter;\n const normalized = adapter.normalize(error);\n return { ...normalized, message: stripAnsi(normalized.message) };\n}\n\n/** Frames from this package's own source/dist, which should never be shown as a location. */\nconst OWN_FRAME = /[\\\\/]multi-assert[\\\\/](?:src|dist)[\\\\/]/;\nconst FRAME_LOCATION = /\\(?((?:file:\\/\\/)?[^\\s()]+?:\\d+:\\d+)\\)?\\s*$/;\n\nexport function userFrames(stack: string | undefined): string[] {\n if (!stack) return [];\n return stack\n .split(\"\\n\")\n .filter((line) => /^\\s*at /.test(line))\n .filter(\n (line) => !line.includes(\"node_modules\") && !line.includes(\"node:\") && !OWN_FRAME.test(line),\n )\n .map((line) => FRAME_LOCATION.exec(line)?.[1])\n .filter((loc): loc is string => Boolean(loc))\n .map(relativize);\n}\n\nfunction relativize(location: string): string {\n // file:///C:/x -> C:/x, but file:///home/x -> /home/x\n const withoutScheme = location.replace(/^file:\\/\\//, \"\").replace(/^\\/([A-Za-z]:)/, \"$1\");\n const cwd = typeof process !== \"undefined\" && process.cwd ? process.cwd() : undefined;\n if (!cwd) return withoutScheme;\n const norm = (p: string) => p.replace(/\\\\/g, \"/\");\n const base = norm(cwd).replace(/\\/$/, \"\") + \"/\";\n const loc = norm(withoutScheme);\n return loc.toLowerCase().startsWith(base.toLowerCase()) ? loc.slice(base.length) : loc;\n}\n","import { formatFailures, shouldUseColor } from \"./format\";\nimport { MultiAssertError } from \"./MultiAssertError\";\nimport { normalizeError, userFrames } from \"./normalize\";\nimport type {\n AsyncCheck,\n AsyncCheckFn,\n Check,\n CheckFn,\n Failure,\n MultiAssertOptions,\n} from \"./types\";\n\nconst defaults: Required<Omit<MultiAssertOptions, \"message\">> & { message?: string } = {\n rethrowSingle: true,\n color: \"auto\",\n stackFrames: 1,\n adapters: [],\n};\n\n/** Set defaults once, e.g. in a test setup file. */\nexport function configure(options: MultiAssertOptions): void {\n Object.assign(defaults, options);\n}\n\ninterface Entry<F> {\n label?: string;\n fn: F;\n}\n\nfunction toEntries<F extends () => unknown>(\n checks: Array<F | [string, F]> | Record<string, F>,\n): Entry<F>[] {\n const pairs: Array<[string | undefined, unknown]> = Array.isArray(checks)\n ? checks.map((c) => (Array.isArray(c) ? [c[0], c[1]] : [undefined, c]))\n : Object.entries(checks ?? {});\n return pairs.map(([label, fn], i) => {\n if (typeof fn !== \"function\") {\n throw new TypeError(\n `multiAssert: check ${i + 1}${label ? ` (\"${label}\")` : \"\"} is not a function`,\n );\n }\n return { label, fn: fn as F };\n });\n}\n\nconst isThenable = (value: unknown): value is PromiseLike<unknown> =>\n typeof (value as PromiseLike<unknown> | undefined)?.then === \"function\";\n\ninterface Caught {\n entry: Entry<unknown>;\n index: number;\n error: unknown;\n}\n\nfunction captureSite(from: (...args: any[]) => unknown): string | undefined {\n const holder: { stack?: string } = {};\n const capture = (Error as any).captureStackTrace as\n | ((target: object, ctor?: (...args: any[]) => unknown) => void)\n | undefined;\n if (capture) {\n capture(holder, from);\n return holder.stack;\n }\n return new Error().stack;\n}\n\nfunction finish(\n caught: Caught[],\n total: number,\n options: MultiAssertOptions,\n site: string | undefined,\n): void {\n if (caught.length === 0) return;\n const settings = { ...defaults, ...options };\n const [only] = caught;\n if (\n caught.length === 1 &&\n settings.rethrowSingle &&\n only.error instanceof Error &&\n !only.entry.label &&\n !settings.message\n ) {\n throw only.error;\n }\n\n const frames = new Map<Failure, string[]>();\n const failures = caught.map(({ entry, index, error }) => {\n const normalized = normalizeError(error, settings.adapters);\n const failure: Failure = {\n index,\n label: entry.label,\n message: normalized.message,\n actual: normalized.actual,\n expected: normalized.expected,\n operator: normalized.operator,\n };\n const list = userFrames(error instanceof Error ? error.stack : undefined);\n failure.location = list[0];\n frames.set(failure, list);\n return failure;\n });\n\n const message = formatFailures(failures, total, {\n heading: settings.message,\n color: shouldUseColor(settings.color),\n stackFrames: settings.stackFrames,\n frames,\n });\n const error = new MultiAssertError(\n caught.map((c) => c.error),\n message,\n failures,\n total,\n );\n // Point reporters at the test that called multiAssert, not at this package.\n const callSite = (site ?? \"\").split(\"\\n\").filter((l) => /^\\s*at /.test(l));\n // Standard `Name: message` header, so reporters that print the stack (Jest) show every failure.\n error.stack = [`${error.name}: ${message}`, ...callSite].join(\"\\n\");\n throw error;\n}\n\n/**\n * Runs every check instead of stopping at the first failure, then throws once with all failures\n * combined. Checks must be synchronous; use {@link multiAssertAsync} for async checks.\n */\nexport function multiAssert(\n checks: Check[] | Record<string, CheckFn>,\n options: MultiAssertOptions = {},\n): void {\n const entries = toEntries<CheckFn>(checks);\n const caught: Caught[] = [];\n entries.forEach((entry, i) => {\n try {\n const result = entry.fn();\n if (isThenable(result)) {\n // Avoid an unhandled rejection from a promise we are not going to await.\n result.then(undefined, () => undefined);\n throw new TypeError(\"check returned a Promise; use multiAssertAsync() for async checks\");\n }\n } catch (error) {\n caught.push({ entry, index: i + 1, error });\n }\n });\n finish(caught, entries.length, options, caught.length ? captureSite(multiAssert) : undefined);\n}\n\n/** Like {@link multiAssert}, but awaits each check in order. */\nexport async function multiAssertAsync(\n checks: AsyncCheck[] | Record<string, AsyncCheckFn>,\n options: MultiAssertOptions = {},\n): Promise<void> {\n const site = captureSite(multiAssertAsync);\n const entries = toEntries<AsyncCheckFn>(checks);\n const caught: Caught[] = [];\n for (const [i, entry] of entries.entries()) {\n try {\n await entry.fn();\n } catch (error) {\n caught.push({ entry, index: i + 1, error });\n }\n }\n finish(caught, entries.length, options, site);\n}\n"],"mappings":"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","names":["error"]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { M as MultiAssertOptions, C as Check, a as CheckFn, A as AsyncCheck, b as AsyncCheckFn, F as Failure } from './types-BfMUWmjj.mjs';
|
|
2
|
+
export { E as ErrorAdapter, N as NormalizedError } from './types-BfMUWmjj.mjs';
|
|
3
|
+
|
|
4
|
+
/** Set defaults once, e.g. in a test setup file. */
|
|
5
|
+
declare function configure(options: MultiAssertOptions): void;
|
|
6
|
+
/**
|
|
7
|
+
* Runs every check instead of stopping at the first failure, then throws once with all failures
|
|
8
|
+
* combined. Checks must be synchronous; use {@link multiAssertAsync} for async checks.
|
|
9
|
+
*/
|
|
10
|
+
declare function multiAssert(checks: Check[] | Record<string, CheckFn>, options?: MultiAssertOptions): void;
|
|
11
|
+
/** Like {@link multiAssert}, but awaits each check in order. */
|
|
12
|
+
declare function multiAssertAsync(checks: AsyncCheck[] | Record<string, AsyncCheckFn>, options?: MultiAssertOptions): Promise<void>;
|
|
13
|
+
|
|
14
|
+
/** Thrown when two or more checks in a group fail (or one fails and can't be rethrown as is). */
|
|
15
|
+
declare class MultiAssertError extends AggregateError {
|
|
16
|
+
readonly failures: Failure[];
|
|
17
|
+
readonly passed: number;
|
|
18
|
+
readonly total: number;
|
|
19
|
+
constructor(errors: unknown[], message: string, failures: Failure[], total: number);
|
|
20
|
+
/**
|
|
21
|
+
* Keeps `name` and `message` so reporters that serialise errors with `toJSON` (Vitest) still
|
|
22
|
+
* show the summary, and adds the structured failures.
|
|
23
|
+
*/
|
|
24
|
+
toJSON(): {
|
|
25
|
+
name: string;
|
|
26
|
+
message: string;
|
|
27
|
+
passed: number;
|
|
28
|
+
total: number;
|
|
29
|
+
failures: Failure[];
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { AsyncCheck, AsyncCheckFn, Check, CheckFn, Failure, MultiAssertError, MultiAssertOptions, configure, multiAssert, multiAssertAsync };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { M as MultiAssertOptions, C as Check, a as CheckFn, A as AsyncCheck, b as AsyncCheckFn, F as Failure } from './types-BfMUWmjj.js';
|
|
2
|
+
export { E as ErrorAdapter, N as NormalizedError } from './types-BfMUWmjj.js';
|
|
3
|
+
|
|
4
|
+
/** Set defaults once, e.g. in a test setup file. */
|
|
5
|
+
declare function configure(options: MultiAssertOptions): void;
|
|
6
|
+
/**
|
|
7
|
+
* Runs every check instead of stopping at the first failure, then throws once with all failures
|
|
8
|
+
* combined. Checks must be synchronous; use {@link multiAssertAsync} for async checks.
|
|
9
|
+
*/
|
|
10
|
+
declare function multiAssert(checks: Check[] | Record<string, CheckFn>, options?: MultiAssertOptions): void;
|
|
11
|
+
/** Like {@link multiAssert}, but awaits each check in order. */
|
|
12
|
+
declare function multiAssertAsync(checks: AsyncCheck[] | Record<string, AsyncCheckFn>, options?: MultiAssertOptions): Promise<void>;
|
|
13
|
+
|
|
14
|
+
/** Thrown when two or more checks in a group fail (or one fails and can't be rethrown as is). */
|
|
15
|
+
declare class MultiAssertError extends AggregateError {
|
|
16
|
+
readonly failures: Failure[];
|
|
17
|
+
readonly passed: number;
|
|
18
|
+
readonly total: number;
|
|
19
|
+
constructor(errors: unknown[], message: string, failures: Failure[], total: number);
|
|
20
|
+
/**
|
|
21
|
+
* Keeps `name` and `message` so reporters that serialise errors with `toJSON` (Vitest) still
|
|
22
|
+
* show the summary, and adds the structured failures.
|
|
23
|
+
*/
|
|
24
|
+
toJSON(): {
|
|
25
|
+
name: string;
|
|
26
|
+
message: string;
|
|
27
|
+
passed: number;
|
|
28
|
+
total: number;
|
|
29
|
+
failures: Failure[];
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { AsyncCheck, AsyncCheckFn, Check, CheckFn, Failure, MultiAssertError, MultiAssertOptions, configure, multiAssert, multiAssertAsync };
|