@arjunkhera/atlas 0.4.31 → 0.4.32
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.
|
@@ -186,17 +186,26 @@ function caseTexts(xml) {
|
|
|
186
186
|
|
|
187
187
|
// The results of one report: Map of case name to pass, assertion, error or skipped.
|
|
188
188
|
// For pytest, a name with parameters ("test_x[1]") counts under its function name.
|
|
189
|
-
|
|
189
|
+
// `known` holds the case names that the case reader found in the file. node --test writes a test
|
|
190
|
+
// whose body holds subtests as a <testsuite> with the subtests as its cases, and no case of its own
|
|
191
|
+
// name. So a subtest counts under the outermost suite around it whose name is known (item
|
|
192
|
+
// 30badd46). A suite of describe() has no known name, so its cases count under their own names.
|
|
193
|
+
// Limit: node writes no failure for a parent test that fails outside its subtests, so that failure
|
|
194
|
+
// does not show here.
|
|
195
|
+
export function readReport(xml, known = []) {
|
|
190
196
|
const cases = readJunit(xml);
|
|
191
197
|
const texts = caseTexts(xml);
|
|
192
198
|
const rank = { pass: 0, skipped: 1, error: 2, assertion: 3 };
|
|
199
|
+
const named = new Set(known);
|
|
200
|
+
// The junit reporter of node writes a double quote as "&quot;", so one
|
|
201
|
+
// unescape leaves """ (f2d07e35). Undo the second escape here.
|
|
202
|
+
const unquote = (text) => text.replaceAll('"', '"');
|
|
193
203
|
const out = new Map();
|
|
194
204
|
cases.forEach((c, i) => {
|
|
195
205
|
let result = c.result;
|
|
196
206
|
if (result === 'fail') result = /AssertionError|ERR_ASSERTION/.test(`${c.message ?? ''}\n${texts[i] ?? ''}`) ? 'assertion' : 'error';
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const name = c.name.replaceAll('"', '"').replace(/\[.*\]$/, '');
|
|
207
|
+
const parent = (c.suites ?? []).map(unquote).find((s) => named.has(s));
|
|
208
|
+
const name = parent ?? unquote(c.name).replace(/\[.*\]$/, '');
|
|
200
209
|
// When one name holds several results, the worst one counts: a fail beats a pass.
|
|
201
210
|
if (!out.has(name) || rank[result] > rank[out.get(name)]) out.set(name, result);
|
|
202
211
|
});
|
|
@@ -336,7 +345,7 @@ export function runSide({ top, changed, plans, side, work = join(tmpdir(), 'atla
|
|
|
336
345
|
if (r.error) why = `the runner did not finish: ${r.error.message}`;
|
|
337
346
|
else if (!existsSync(report)) why = `the runner wrote no report (exit ${r.status})`;
|
|
338
347
|
else {
|
|
339
|
-
try { cases = Object.fromEntries(readReport(readFileSync(report, 'utf8'))); } catch (error) { why = `the report does not read: ${error.message}`; }
|
|
348
|
+
try { cases = Object.fromEntries(readReport(readFileSync(report, 'utf8'), p.known)); } catch (error) { why = `the report does not read: ${error.message}`; }
|
|
340
349
|
}
|
|
341
350
|
out.files[p.path] = { command, cases, why };
|
|
342
351
|
}
|
|
@@ -84,7 +84,7 @@ export function plan({ root, base, head = 'HEAD', list = null }) {
|
|
|
84
84
|
if (!picked.length && !all) continue;
|
|
85
85
|
if (!runner) { files.push({ path, runner: null, result: 'not run', why: 'no runner for this file yet' }); plans.push({ path, runner: null, picked, all }); continue; }
|
|
86
86
|
files.push({ path, runner, result: 'ran', why: null });
|
|
87
|
-
plans.push({ path, runner, picked, all, head: r.status === 'removed' ? null : r.path });
|
|
87
|
+
plans.push({ path, runner, picked, all, head: r.status === 'removed' ? null : r.path, known: before ? [...before.cases.keys()] : [] });
|
|
88
88
|
}
|
|
89
89
|
files.sort((x, y) => (x.path < y.path ? -1 : 1));
|
|
90
90
|
return {
|
|
@@ -131,7 +131,7 @@ export function runOld({ top, changed, plans, work = join(tmpdir(), 'atlas-after
|
|
|
131
131
|
if (r.error) why = `the runner did not finish: ${r.error.message}`;
|
|
132
132
|
else if (!existsSync(report)) why = `the runner wrote no report (exit ${r.status})`;
|
|
133
133
|
else {
|
|
134
|
-
try { cases = Object.fromEntries(readReport(readFileSync(report, 'utf8'))); } catch (error) { why = `the report does not read: ${error.message}`; }
|
|
134
|
+
try { cases = Object.fromEntries(readReport(readFileSync(report, 'utf8'), p.known)); } catch (error) { why = `the report does not read: ${error.message}`; }
|
|
135
135
|
}
|
|
136
136
|
out.files[p.path] = { command, cases, why };
|
|
137
137
|
}
|
package/package.json
CHANGED
package/tests/junit.mjs
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
// write this report: node --test (--test-reporter=junit), pytest
|
|
4
4
|
// (--junitxml), TestNG, JUnit, Vitest and Jest.
|
|
5
5
|
//
|
|
6
|
-
// readJunit(xml) -> [{ name, classname, file, suite, result, message }]
|
|
6
|
+
// readJunit(xml) -> [{ name, classname, file, suite, suites, result, message }]
|
|
7
7
|
// result: "pass", "fail" or "skipped". An <error> is a fail too.
|
|
8
|
+
// suites: the names of the <testsuite> elements around the case, outermost
|
|
9
|
+
// first. node --test writes a test with subtests as a <testsuite>.
|
|
8
10
|
//
|
|
9
11
|
// The reader has no dependency. It reads only the parts it needs: the
|
|
10
12
|
// <testsuite> nesting, each <testcase> and its <failure>, <error> and
|
|
@@ -44,9 +46,10 @@ export function readJunit(xml) {
|
|
|
44
46
|
if (tag === 'testcase') {
|
|
45
47
|
if (closing) { current = null; continue; }
|
|
46
48
|
const a = attributes(attrs);
|
|
47
|
-
const
|
|
49
|
+
const names = suites.map((s) => s.name).filter(Boolean);
|
|
50
|
+
const suite = names.join(' > ');
|
|
48
51
|
const file = a.file ?? suites.map((s) => s.file).filter(Boolean).at(-1) ?? null;
|
|
49
|
-
const one = { name: a.name ?? '', classname: a.classname ?? null, file, suite: suite || null, result: 'pass', message: null };
|
|
52
|
+
const one = { name: a.name ?? '', classname: a.classname ?? null, file, suite: suite || null, suites: names, result: 'pass', message: null };
|
|
50
53
|
cases.push(one);
|
|
51
54
|
current = selfClosing ? null : one;
|
|
52
55
|
continue;
|