miaoda-game-devkit 0.6.4 → 0.6.6
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/dist/cli/phaser-lint.js +2 -2
- package/dist/cli/react-lint.js +2 -2
- package/dist/react/testing.d.mts +12 -0
- package/dist/react/testing.d.ts +12 -0
- package/dist/react/testing.js +112 -14
- package/dist/react/testing.mjs +112 -14
- package/dist/react/vitest-config.js +636 -440
- package/dist/react/vitest-config.mjs +640 -444
- package/dist/react/vitest-setup.js +112 -14
- package/dist/react/vitest-setup.mjs +112 -14
- package/dist/rules/react-test-boundary-plugin.js +1 -1
- package/package.json +1 -1
package/dist/cli/phaser-lint.js
CHANGED
|
@@ -222,12 +222,12 @@ function auditReactAuthoritativePlaythrough(projectRoot2) {
|
|
|
222
222
|
const hasObserve = declaresObserve(testSource);
|
|
223
223
|
if (!hasObserve) {
|
|
224
224
|
issues.push(
|
|
225
|
-
`${PRODUCTION_PLAYTHROUGH} must declare observe because production uses the devkit clock or Controller ownership hook.
|
|
225
|
+
`${PRODUCTION_PLAYTHROUGH} must declare observe because production uses the devkit clock or Controller ownership hook. Pass { observe: () => telemetry.read.session() } to playthroughTest, using Telemetry backed by the same production Controller rendered by <App />; DOM labels are not an authoritative gameplay boundary.`
|
|
226
226
|
);
|
|
227
227
|
}
|
|
228
228
|
if (clockFiles.length > 0 && !testingImports.has("ManualGameClock")) {
|
|
229
229
|
issues.push(
|
|
230
|
-
`${PRODUCTION_PLAYTHROUGH} must import ManualGameClock because production owns gameplay time in ${clockFiles.join(", ")}.
|
|
230
|
+
`${PRODUCTION_PLAYTHROUGH} must import ManualGameClock because production owns gameplay time in ${clockFiles.join(", ")}. Add this import to the test: import { ManualGameClock } from "miaoda-game-devkit/react/testing"; Then create one, inject it through the production <App /> factory, and advance it with step: () => clock.stepFrame().`
|
|
231
231
|
);
|
|
232
232
|
}
|
|
233
233
|
return {
|
package/dist/cli/react-lint.js
CHANGED
|
@@ -222,12 +222,12 @@ function auditReactAuthoritativePlaythrough(projectRoot2) {
|
|
|
222
222
|
const hasObserve = declaresObserve(testSource);
|
|
223
223
|
if (!hasObserve) {
|
|
224
224
|
issues.push(
|
|
225
|
-
`${PRODUCTION_PLAYTHROUGH} must declare observe because production uses the devkit clock or Controller ownership hook.
|
|
225
|
+
`${PRODUCTION_PLAYTHROUGH} must declare observe because production uses the devkit clock or Controller ownership hook. Pass { observe: () => telemetry.read.session() } to playthroughTest, using Telemetry backed by the same production Controller rendered by <App />; DOM labels are not an authoritative gameplay boundary.`
|
|
226
226
|
);
|
|
227
227
|
}
|
|
228
228
|
if (clockFiles.length > 0 && !testingImports.has("ManualGameClock")) {
|
|
229
229
|
issues.push(
|
|
230
|
-
`${PRODUCTION_PLAYTHROUGH} must import ManualGameClock because production owns gameplay time in ${clockFiles.join(", ")}.
|
|
230
|
+
`${PRODUCTION_PLAYTHROUGH} must import ManualGameClock because production owns gameplay time in ${clockFiles.join(", ")}. Add this import to the test: import { ManualGameClock } from "miaoda-game-devkit/react/testing"; Then create one, inject it through the production <App /> factory, and advance it with step: () => clock.stepFrame().`
|
|
231
231
|
);
|
|
232
232
|
}
|
|
233
233
|
return {
|
package/dist/react/testing.d.mts
CHANGED
|
@@ -25,7 +25,18 @@ declare class ManualGameClock implements GameClock {
|
|
|
25
25
|
interface ReactFailureEntry {
|
|
26
26
|
code: string;
|
|
27
27
|
message: string;
|
|
28
|
+
errorName?: string;
|
|
29
|
+
actual?: string;
|
|
30
|
+
expected?: string;
|
|
31
|
+
origin?: ReactFailureOrigin;
|
|
32
|
+
layer?: ReactFailureLayer;
|
|
28
33
|
}
|
|
34
|
+
interface ReactFailureOrigin {
|
|
35
|
+
file: string;
|
|
36
|
+
line: number;
|
|
37
|
+
column: number;
|
|
38
|
+
}
|
|
39
|
+
type ReactFailureLayer = "product" | "test" | "harness" | "dependency" | "unknown";
|
|
29
40
|
interface ReactFailureDiagnostic {
|
|
30
41
|
source: "playthrough" | "test-runtime";
|
|
31
42
|
entries: ReactFailureEntry[];
|
|
@@ -126,6 +137,7 @@ interface ReactPlaythroughTest {
|
|
|
126
137
|
interface ReactPlaythroughAuditInput {
|
|
127
138
|
name: string;
|
|
128
139
|
state: "passed" | "failed" | "skipped" | "pending";
|
|
140
|
+
mode?: "run" | "only" | "skip" | "todo";
|
|
129
141
|
metadata?: ReactPlaythroughMetadata;
|
|
130
142
|
}
|
|
131
143
|
interface ReactPlaythroughAuditResult {
|
package/dist/react/testing.d.ts
CHANGED
|
@@ -25,7 +25,18 @@ declare class ManualGameClock implements GameClock {
|
|
|
25
25
|
interface ReactFailureEntry {
|
|
26
26
|
code: string;
|
|
27
27
|
message: string;
|
|
28
|
+
errorName?: string;
|
|
29
|
+
actual?: string;
|
|
30
|
+
expected?: string;
|
|
31
|
+
origin?: ReactFailureOrigin;
|
|
32
|
+
layer?: ReactFailureLayer;
|
|
28
33
|
}
|
|
34
|
+
interface ReactFailureOrigin {
|
|
35
|
+
file: string;
|
|
36
|
+
line: number;
|
|
37
|
+
column: number;
|
|
38
|
+
}
|
|
39
|
+
type ReactFailureLayer = "product" | "test" | "harness" | "dependency" | "unknown";
|
|
29
40
|
interface ReactFailureDiagnostic {
|
|
30
41
|
source: "playthrough" | "test-runtime";
|
|
31
42
|
entries: ReactFailureEntry[];
|
|
@@ -126,6 +137,7 @@ interface ReactPlaythroughTest {
|
|
|
126
137
|
interface ReactPlaythroughAuditInput {
|
|
127
138
|
name: string;
|
|
128
139
|
state: "passed" | "failed" | "skipped" | "pending";
|
|
140
|
+
mode?: "run" | "only" | "skip" | "todo";
|
|
129
141
|
metadata?: ReactPlaythroughMetadata;
|
|
130
142
|
}
|
|
131
143
|
interface ReactPlaythroughAuditResult {
|
package/dist/react/testing.js
CHANGED
|
@@ -122,47 +122,145 @@ function safeJson(value) {
|
|
|
122
122
|
return void 0;
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
|
-
function
|
|
125
|
+
function diagnosticValue(value) {
|
|
126
|
+
if (value === void 0) return void 0;
|
|
126
127
|
if (typeof value === "string") {
|
|
127
|
-
return value.
|
|
128
|
+
return truncate(value.replace(/\s+/g, " ").trim());
|
|
129
|
+
}
|
|
130
|
+
if (value === null || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
131
|
+
return String(value);
|
|
132
|
+
}
|
|
133
|
+
if (typeof value === "symbol") return value.toString();
|
|
134
|
+
if (typeof value === "function") {
|
|
135
|
+
return `Function<${value.name || "anonymous"}>`;
|
|
136
|
+
}
|
|
137
|
+
const json = safeJson(value);
|
|
138
|
+
return truncate(json ?? String(value));
|
|
139
|
+
}
|
|
140
|
+
function normalizeFile(value) {
|
|
141
|
+
return value.replaceAll("\\", "/").replace(/^file:\/\//, "");
|
|
142
|
+
}
|
|
143
|
+
function failureLayer(file) {
|
|
144
|
+
const normalized = normalizeFile(file);
|
|
145
|
+
if (normalized.includes("/miaoda-game-devkit/") || normalized.includes("/packages/game-devkit/")) {
|
|
146
|
+
return "harness";
|
|
147
|
+
}
|
|
148
|
+
if (normalized.includes("/node_modules/")) return "dependency";
|
|
149
|
+
if (/(?:^|\/)tests?\//.test(normalized) || /\.(?:test|spec)\.[cm]?[jt]sx?$/.test(normalized)) {
|
|
150
|
+
return "test";
|
|
151
|
+
}
|
|
152
|
+
if (/(?:^|\/)src\//.test(normalized)) return "product";
|
|
153
|
+
return "unknown";
|
|
154
|
+
}
|
|
155
|
+
function parsedOrigin(value) {
|
|
156
|
+
if (!value || typeof value !== "object") return void 0;
|
|
157
|
+
const frame = value;
|
|
158
|
+
if (typeof frame.file !== "string" || typeof frame.line !== "number" || typeof frame.column !== "number") {
|
|
159
|
+
return void 0;
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
file: normalizeFile(frame.file),
|
|
163
|
+
line: frame.line,
|
|
164
|
+
column: frame.column
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function stackOrigins(record) {
|
|
168
|
+
if (Array.isArray(record.stacks)) {
|
|
169
|
+
const parsed = record.stacks.map(parsedOrigin).filter((origin) => Boolean(origin));
|
|
170
|
+
if (parsed.length > 0) return parsed;
|
|
171
|
+
}
|
|
172
|
+
if (typeof record.stack !== "string") return [];
|
|
173
|
+
const origins = [];
|
|
174
|
+
const pattern = /(?:at\s+.*?\()?((?:file:\/\/)?[^()\s]+):(\d+):(\d+)\)?/g;
|
|
175
|
+
for (const match of record.stack.matchAll(pattern)) {
|
|
176
|
+
origins.push({
|
|
177
|
+
file: normalizeFile(match[1]),
|
|
178
|
+
line: Number(match[2]),
|
|
179
|
+
column: Number(match[3])
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
return origins;
|
|
183
|
+
}
|
|
184
|
+
function selectOrigin(record) {
|
|
185
|
+
const origins = stackOrigins(record);
|
|
186
|
+
return origins.find((origin) => {
|
|
187
|
+
const layer = failureLayer(origin.file);
|
|
188
|
+
return layer === "product" || layer === "test";
|
|
189
|
+
}) ?? origins.find((origin) => failureLayer(origin.file) !== "dependency");
|
|
190
|
+
}
|
|
191
|
+
function fallbackCode(record, origin, defaultCode) {
|
|
192
|
+
if (typeof record.code === "string" && record.code.trim()) {
|
|
193
|
+
return record.code.trim();
|
|
194
|
+
}
|
|
195
|
+
if (record.name === "AssertionError" && record.actual !== void 0 && record.expected !== void 0) {
|
|
196
|
+
return "EXPECTATION_MISMATCH";
|
|
197
|
+
}
|
|
198
|
+
if (record.name === "TypeError" && origin) {
|
|
199
|
+
const layer = failureLayer(origin.file);
|
|
200
|
+
if (layer === "product") return "PRODUCT_RUNTIME_TYPE_ERROR";
|
|
201
|
+
if (layer === "test") return "TEST_API_MISMATCH";
|
|
202
|
+
}
|
|
203
|
+
return defaultCode;
|
|
204
|
+
}
|
|
205
|
+
function structuredEntry(record, message, defaultCode) {
|
|
206
|
+
const origin = selectOrigin(record);
|
|
207
|
+
return {
|
|
208
|
+
code: fallbackCode(record, origin, defaultCode),
|
|
209
|
+
message: truncate(message),
|
|
210
|
+
errorName: typeof record.name === "string" && record.name.trim() ? record.name.trim() : void 0,
|
|
211
|
+
actual: diagnosticValue(record.actual),
|
|
212
|
+
expected: diagnosticValue(record.expected),
|
|
213
|
+
origin,
|
|
214
|
+
layer: origin ? failureLayer(origin.file) : void 0
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function collectEntries(value, fallbackCode2, seen) {
|
|
218
|
+
if (typeof value === "string") {
|
|
219
|
+
return value.trim() ? [{ code: fallbackCode2, message: truncate(value) }] : [];
|
|
128
220
|
}
|
|
129
221
|
if (value === null || value === void 0 || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || typeof value === "symbol") {
|
|
130
|
-
return [{ code:
|
|
222
|
+
return [{ code: fallbackCode2, message: String(value) }];
|
|
131
223
|
}
|
|
132
224
|
if (typeof value === "function") {
|
|
133
225
|
return [
|
|
134
|
-
{ code:
|
|
226
|
+
{ code: fallbackCode2, message: `Function<${value.name || "anonymous"}>` }
|
|
135
227
|
];
|
|
136
228
|
}
|
|
137
229
|
if (seen.has(value)) return [];
|
|
138
230
|
seen.add(value);
|
|
139
231
|
if (Array.isArray(value)) {
|
|
140
|
-
return value.flatMap((item) => collectEntries(item,
|
|
232
|
+
return value.flatMap((item) => collectEntries(item, fallbackCode2, seen));
|
|
141
233
|
}
|
|
142
234
|
const record = value;
|
|
143
|
-
const code = typeof record.code === "string" && record.code.trim() ? record.code.trim() : fallbackCode;
|
|
144
235
|
const entries = [];
|
|
145
236
|
if (typeof record.message === "string" && record.message.trim()) {
|
|
146
|
-
entries.push(
|
|
237
|
+
entries.push(structuredEntry(record, record.message, fallbackCode2));
|
|
147
238
|
}
|
|
148
239
|
if (record.cause !== void 0) {
|
|
149
|
-
entries.push(...collectEntries(record.cause,
|
|
240
|
+
entries.push(...collectEntries(record.cause, fallbackCode2, seen));
|
|
150
241
|
}
|
|
151
242
|
if (Array.isArray(record.errors)) {
|
|
152
|
-
entries.push(...collectEntries(record.errors,
|
|
243
|
+
entries.push(...collectEntries(record.errors, fallbackCode2, seen));
|
|
153
244
|
}
|
|
154
245
|
if (entries.length > 0) return entries;
|
|
155
246
|
if (typeof record.stack === "string" && record.stack.trim()) {
|
|
156
|
-
return [
|
|
247
|
+
return [structuredEntry(record, record.stack, fallbackCode2)];
|
|
157
248
|
}
|
|
158
249
|
const json = safeJson(value);
|
|
159
|
-
return json && json !== "{}" ? [
|
|
250
|
+
return json && json !== "{}" ? [structuredEntry(record, json, fallbackCode2)] : [];
|
|
160
251
|
}
|
|
161
|
-
function extractFailureEntries(value,
|
|
162
|
-
const entries = collectEntries(value,
|
|
252
|
+
function extractFailureEntries(value, fallbackCode2 = "TEST_FAILURE") {
|
|
253
|
+
const entries = collectEntries(value, fallbackCode2, /* @__PURE__ */ new WeakSet());
|
|
163
254
|
const keys = /* @__PURE__ */ new Set();
|
|
164
255
|
return entries.filter((entry) => {
|
|
165
|
-
const key =
|
|
256
|
+
const key = [
|
|
257
|
+
entry.code,
|
|
258
|
+
entry.message,
|
|
259
|
+
entry.errorName,
|
|
260
|
+
entry.actual,
|
|
261
|
+
entry.expected,
|
|
262
|
+
entry.origin ? `${entry.origin.file}:${entry.origin.line}:${entry.origin.column}` : void 0
|
|
263
|
+
].join("\0");
|
|
166
264
|
if (keys.has(key)) return false;
|
|
167
265
|
keys.add(key);
|
|
168
266
|
return true;
|
package/dist/react/testing.mjs
CHANGED
|
@@ -84,47 +84,145 @@ function safeJson(value) {
|
|
|
84
84
|
return void 0;
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
|
-
function
|
|
87
|
+
function diagnosticValue(value) {
|
|
88
|
+
if (value === void 0) return void 0;
|
|
88
89
|
if (typeof value === "string") {
|
|
89
|
-
return value.
|
|
90
|
+
return truncate(value.replace(/\s+/g, " ").trim());
|
|
91
|
+
}
|
|
92
|
+
if (value === null || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
93
|
+
return String(value);
|
|
94
|
+
}
|
|
95
|
+
if (typeof value === "symbol") return value.toString();
|
|
96
|
+
if (typeof value === "function") {
|
|
97
|
+
return `Function<${value.name || "anonymous"}>`;
|
|
98
|
+
}
|
|
99
|
+
const json = safeJson(value);
|
|
100
|
+
return truncate(json ?? String(value));
|
|
101
|
+
}
|
|
102
|
+
function normalizeFile(value) {
|
|
103
|
+
return value.replaceAll("\\", "/").replace(/^file:\/\//, "");
|
|
104
|
+
}
|
|
105
|
+
function failureLayer(file) {
|
|
106
|
+
const normalized = normalizeFile(file);
|
|
107
|
+
if (normalized.includes("/miaoda-game-devkit/") || normalized.includes("/packages/game-devkit/")) {
|
|
108
|
+
return "harness";
|
|
109
|
+
}
|
|
110
|
+
if (normalized.includes("/node_modules/")) return "dependency";
|
|
111
|
+
if (/(?:^|\/)tests?\//.test(normalized) || /\.(?:test|spec)\.[cm]?[jt]sx?$/.test(normalized)) {
|
|
112
|
+
return "test";
|
|
113
|
+
}
|
|
114
|
+
if (/(?:^|\/)src\//.test(normalized)) return "product";
|
|
115
|
+
return "unknown";
|
|
116
|
+
}
|
|
117
|
+
function parsedOrigin(value) {
|
|
118
|
+
if (!value || typeof value !== "object") return void 0;
|
|
119
|
+
const frame = value;
|
|
120
|
+
if (typeof frame.file !== "string" || typeof frame.line !== "number" || typeof frame.column !== "number") {
|
|
121
|
+
return void 0;
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
file: normalizeFile(frame.file),
|
|
125
|
+
line: frame.line,
|
|
126
|
+
column: frame.column
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function stackOrigins(record) {
|
|
130
|
+
if (Array.isArray(record.stacks)) {
|
|
131
|
+
const parsed = record.stacks.map(parsedOrigin).filter((origin) => Boolean(origin));
|
|
132
|
+
if (parsed.length > 0) return parsed;
|
|
133
|
+
}
|
|
134
|
+
if (typeof record.stack !== "string") return [];
|
|
135
|
+
const origins = [];
|
|
136
|
+
const pattern = /(?:at\s+.*?\()?((?:file:\/\/)?[^()\s]+):(\d+):(\d+)\)?/g;
|
|
137
|
+
for (const match of record.stack.matchAll(pattern)) {
|
|
138
|
+
origins.push({
|
|
139
|
+
file: normalizeFile(match[1]),
|
|
140
|
+
line: Number(match[2]),
|
|
141
|
+
column: Number(match[3])
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
return origins;
|
|
145
|
+
}
|
|
146
|
+
function selectOrigin(record) {
|
|
147
|
+
const origins = stackOrigins(record);
|
|
148
|
+
return origins.find((origin) => {
|
|
149
|
+
const layer = failureLayer(origin.file);
|
|
150
|
+
return layer === "product" || layer === "test";
|
|
151
|
+
}) ?? origins.find((origin) => failureLayer(origin.file) !== "dependency");
|
|
152
|
+
}
|
|
153
|
+
function fallbackCode(record, origin, defaultCode) {
|
|
154
|
+
if (typeof record.code === "string" && record.code.trim()) {
|
|
155
|
+
return record.code.trim();
|
|
156
|
+
}
|
|
157
|
+
if (record.name === "AssertionError" && record.actual !== void 0 && record.expected !== void 0) {
|
|
158
|
+
return "EXPECTATION_MISMATCH";
|
|
159
|
+
}
|
|
160
|
+
if (record.name === "TypeError" && origin) {
|
|
161
|
+
const layer = failureLayer(origin.file);
|
|
162
|
+
if (layer === "product") return "PRODUCT_RUNTIME_TYPE_ERROR";
|
|
163
|
+
if (layer === "test") return "TEST_API_MISMATCH";
|
|
164
|
+
}
|
|
165
|
+
return defaultCode;
|
|
166
|
+
}
|
|
167
|
+
function structuredEntry(record, message, defaultCode) {
|
|
168
|
+
const origin = selectOrigin(record);
|
|
169
|
+
return {
|
|
170
|
+
code: fallbackCode(record, origin, defaultCode),
|
|
171
|
+
message: truncate(message),
|
|
172
|
+
errorName: typeof record.name === "string" && record.name.trim() ? record.name.trim() : void 0,
|
|
173
|
+
actual: diagnosticValue(record.actual),
|
|
174
|
+
expected: diagnosticValue(record.expected),
|
|
175
|
+
origin,
|
|
176
|
+
layer: origin ? failureLayer(origin.file) : void 0
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function collectEntries(value, fallbackCode2, seen) {
|
|
180
|
+
if (typeof value === "string") {
|
|
181
|
+
return value.trim() ? [{ code: fallbackCode2, message: truncate(value) }] : [];
|
|
90
182
|
}
|
|
91
183
|
if (value === null || value === void 0 || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || typeof value === "symbol") {
|
|
92
|
-
return [{ code:
|
|
184
|
+
return [{ code: fallbackCode2, message: String(value) }];
|
|
93
185
|
}
|
|
94
186
|
if (typeof value === "function") {
|
|
95
187
|
return [
|
|
96
|
-
{ code:
|
|
188
|
+
{ code: fallbackCode2, message: `Function<${value.name || "anonymous"}>` }
|
|
97
189
|
];
|
|
98
190
|
}
|
|
99
191
|
if (seen.has(value)) return [];
|
|
100
192
|
seen.add(value);
|
|
101
193
|
if (Array.isArray(value)) {
|
|
102
|
-
return value.flatMap((item) => collectEntries(item,
|
|
194
|
+
return value.flatMap((item) => collectEntries(item, fallbackCode2, seen));
|
|
103
195
|
}
|
|
104
196
|
const record = value;
|
|
105
|
-
const code = typeof record.code === "string" && record.code.trim() ? record.code.trim() : fallbackCode;
|
|
106
197
|
const entries = [];
|
|
107
198
|
if (typeof record.message === "string" && record.message.trim()) {
|
|
108
|
-
entries.push(
|
|
199
|
+
entries.push(structuredEntry(record, record.message, fallbackCode2));
|
|
109
200
|
}
|
|
110
201
|
if (record.cause !== void 0) {
|
|
111
|
-
entries.push(...collectEntries(record.cause,
|
|
202
|
+
entries.push(...collectEntries(record.cause, fallbackCode2, seen));
|
|
112
203
|
}
|
|
113
204
|
if (Array.isArray(record.errors)) {
|
|
114
|
-
entries.push(...collectEntries(record.errors,
|
|
205
|
+
entries.push(...collectEntries(record.errors, fallbackCode2, seen));
|
|
115
206
|
}
|
|
116
207
|
if (entries.length > 0) return entries;
|
|
117
208
|
if (typeof record.stack === "string" && record.stack.trim()) {
|
|
118
|
-
return [
|
|
209
|
+
return [structuredEntry(record, record.stack, fallbackCode2)];
|
|
119
210
|
}
|
|
120
211
|
const json = safeJson(value);
|
|
121
|
-
return json && json !== "{}" ? [
|
|
212
|
+
return json && json !== "{}" ? [structuredEntry(record, json, fallbackCode2)] : [];
|
|
122
213
|
}
|
|
123
|
-
function extractFailureEntries(value,
|
|
124
|
-
const entries = collectEntries(value,
|
|
214
|
+
function extractFailureEntries(value, fallbackCode2 = "TEST_FAILURE") {
|
|
215
|
+
const entries = collectEntries(value, fallbackCode2, /* @__PURE__ */ new WeakSet());
|
|
125
216
|
const keys = /* @__PURE__ */ new Set();
|
|
126
217
|
return entries.filter((entry) => {
|
|
127
|
-
const key =
|
|
218
|
+
const key = [
|
|
219
|
+
entry.code,
|
|
220
|
+
entry.message,
|
|
221
|
+
entry.errorName,
|
|
222
|
+
entry.actual,
|
|
223
|
+
entry.expected,
|
|
224
|
+
entry.origin ? `${entry.origin.file}:${entry.origin.line}:${entry.origin.column}` : void 0
|
|
225
|
+
].join("\0");
|
|
128
226
|
if (keys.has(key)) return false;
|
|
129
227
|
keys.add(key);
|
|
130
228
|
return true;
|