| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 3a0549d commit 260cf1a
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,6 +5,9 @@ const { | |||
| 5 | 5 | ArrayPrototypeMap, | |
| 6 | 6 | ArrayPrototypePush, | |
| 7 | 7 | ArrayPrototypeSome, | |
| 8 | + Date, | ||
| 9 | + DateNow, | ||
| 10 | + DatePrototypeToISOString, | ||
| 8 | 11 | NumberPrototypeToFixed, | |
| 9 | 12 | ObjectEntries, | |
| 10 | 13 | RegExpPrototypeSymbolReplace, | |
@@ -112,6 +115,11 @@ module.exports = async function* junitReporter(source) { | |||
| 112 | 115 | currentTest.attrs.tests = nonCommentChildren.length; | |
| 113 | 116 | currentTest.attrs.failures = ArrayPrototypeFilter(currentTest.children, isFailure).length; | |
| 114 | 117 | currentTest.attrs.skipped = ArrayPrototypeFilter(currentTest.children, isSkipped).length; | |
| 118 | + // A suite's `test:start` is emitted lazily (when its first subtest | ||
| 119 | + // reports), so derive the start time from the end minus the measured | ||
| 120 | + // duration rather than stamping the (late) test:start moment. | ||
| 121 | + currentTest.attrs.timestamp = | ||
| 122 | + DatePrototypeToISOString(new Date(DateNow() - event.data.details.duration_ms)); | ||
| 115 | 123 | currentTest.attrs.hostname = HOSTNAME; | |
| 116 | 124 | } else { | |
| 117 | 125 | currentTest.tag = 'testcase'; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -226,6 +226,7 @@ function replaceJunitDuration(str) { | |||
| 226 | 226 | .replaceAll(/time="[0-9.]+"/g, 'time="*"') | |
| 227 | 227 | .replaceAll(/duration_ms [0-9.]+/g, 'duration_ms *') | |
| 228 | 228 | .replaceAll(`hostname="${hostname()}"`, 'hostname="HOSTNAME"') | |
| 229 | + .replaceAll(/timestamp="[^"]*"/g, 'timestamp="*"') | ||
| 229 | 230 | .replaceAll(/file="[^"]*"/g, 'file="*"'); | |
| 230 | 231 | } | |
| 231 | 232 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -128,7 +128,7 @@ true !== false | |||
| 128 | 128 | <testcase name="immediate throw - passes but warns" time="*" classname="test" file="*"/> | |
| 129 | 129 | <testcase name="immediate reject - passes but warns" time="*" classname="test" file="*"/> | |
| 130 | 130 | <testcase name="immediate resolve pass" time="*" classname="test" file="*"/> | |
| 131 | - <testsuite name="subtest sync throw fail" time="*" disabled="0" errors="0" tests="1" failures="1" skipped="0" hostname="HOSTNAME"> | ||
| 131 | + <testsuite name="subtest sync throw fail" time="*" disabled="0" errors="0" tests="1" failures="1" skipped="0" timestamp="*" hostname="HOSTNAME"> | ||
| 132 | 132 | <testcase name="+sync throw fail" time="*" classname="test" file="*" failure="thrown from subtest sync throw fail"> | |
| 133 | 133 | <failure type="testCodeFailure" message="thrown from subtest sync throw fail"> | |
| 134 | 134 | Error [ERR_TEST_FAILURE]: thrown from subtest sync throw fail | |
@@ -151,15 +151,15 @@ Error [ERR_TEST_FAILURE]: thrown from subtest sync throw fail | |||
| 151 | 151 | [Error [ERR_TEST_FAILURE]: Symbol(thrown symbol from sync throw non-error fail)] { code: 'ERR_TEST_FAILURE', failureType: 'testCodeFailure', cause: Symbol(thrown symbol from sync throw non-error fail) } | |
| 152 | 152 | </failure> | |
| 153 | 153 | </testcase> | |
| 154 | - <testsuite name="level 0a" time="*" disabled="0" errors="0" tests="4" failures="0" skipped="0" hostname="HOSTNAME"> | ||
| 154 | + <testsuite name="level 0a" time="*" disabled="0" errors="0" tests="4" failures="0" skipped="0" timestamp="*" hostname="HOSTNAME"> | ||
| 155 | 155 | <testcase name="level 1a" time="*" classname="test" file="*"/> | |
| 156 | 156 | <testcase name="level 1b" time="*" classname="test" file="*"/> | |
| 157 | 157 | <testcase name="level 1c" time="*" classname="test" file="*"/> | |
| 158 | 158 | <testcase name="level 1d" time="*" classname="test" file="*"/> | |
| 159 | 159 | </testsuite> | |
| 160 | - <testsuite name="top level" time="*" disabled="0" errors="0" tests="2" failures="0" skipped="0" hostname="HOSTNAME"> | ||
| 160 | + <testsuite name="top level" time="*" disabled="0" errors="0" tests="2" failures="0" skipped="0" timestamp="*" hostname="HOSTNAME"> | ||
| 161 | 161 | <testcase name="+long running" time="*" classname="test" file="*"/> | |
| 162 | - <testsuite name="+short running" time="*" disabled="0" errors="0" tests="1" failures="0" skipped="0" hostname="HOSTNAME"> | ||
| 162 | + <testsuite name="+short running" time="*" disabled="0" errors="0" tests="1" failures="0" skipped="0" timestamp="*" hostname="HOSTNAME"> | ||
| 163 | 163 | <testcase name="++short running" time="*" classname="test" file="*"/> | |
| 164 | 164 | </testsuite> | |
| 165 | 165 | </testsuite> | |
@@ -266,7 +266,7 @@ Error [ERR_TEST_FAILURE]: thrown from callback async throw | |||
| 266 | 266 | </failure> | |
| 267 | 267 | </testcase> | |
| 268 | 268 | <testcase name="callback async throw after done" time="*" classname="test" file="*"/> | |
| 269 | - <testsuite name="only is set on subtests but not in only mode" time="*" disabled="0" errors="0" tests="3" failures="0" skipped="0" hostname="HOSTNAME"> | ||
| 269 | + <testsuite name="only is set on subtests but not in only mode" time="*" disabled="0" errors="0" tests="3" failures="0" skipped="0" timestamp="*" hostname="HOSTNAME"> | ||
| 270 | 270 | <testcase name="running subtest 1" time="*" classname="test" file="*"/> | |
| 271 | 271 | <testcase name="running subtest 3" time="*" classname="test" file="*"/> | |
| 272 | 272 | <testcase name="running subtest 4" time="*" classname="test" file="*"/> | |
@@ -288,7 +288,7 @@ Error [ERR_TEST_FAILURE]: thrown from callback async throw | |||
| 288 | 288 | } | |
| 289 | 289 | </failure> | |
| 290 | 290 | </testcase> | |
| 291 | - <testsuite name="subtest sync throw fails" time="*" disabled="0" errors="0" tests="2" failures="2" skipped="0" hostname="HOSTNAME"> | ||
| 291 | + <testsuite name="subtest sync throw fails" time="*" disabled="0" errors="0" tests="2" failures="2" skipped="0" timestamp="*" hostname="HOSTNAME"> | ||
| 292 | 292 | <testcase name="sync throw fails at first" time="*" classname="test" file="*" failure="thrown from subtest sync throw fails at first"> | |
| 293 | 293 | <failure type="testCodeFailure" message="thrown from subtest sync throw fails at first"> | |
| 294 | 294 | Error [ERR_TEST_FAILURE]: thrown from subtest sync throw fails at first | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -200,6 +200,12 @@ describe('node:test reporters', { concurrency: true }, () => { | |||
| 200 | 200 | assert.strictEqual(child.stdout.toString(), ''); | |
| 201 | 201 | const fileContents = fs.readFileSync(file, 'utf8'); | |
| 202 | 202 | assert.match(fileContents, /<testsuite .*name="nested".*tests="2".*failures="1".*skipped="0".*>/); | |
| 203 | + // The exact timestamp format is intentionally not pinned here (still under | ||
| 204 | + // discussion); assert only that the value is present and a real date. | ||
| 205 | + const { 1: timestamp } = fileContents.match(/<testsuite [^>]*timestamp="([^"]+)"/) ?? []; | ||
| 206 | + assert.ok(timestamp, 'testsuite should have a timestamp attribute'); | ||
| 207 | + assert.match(timestamp, /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/); | ||
| 208 | + assert.ok(!Number.isNaN(Date.parse(timestamp)), `expected a valid date, got ${timestamp}`); | ||
| 203 | 209 | assert.match(fileContents, /<testcase .*name="failing".*>\s*<failure .*type="testCodeFailure".*message="error".*>/); | |
| 204 | 210 | assert.match(fileContents, /<testcase .*name="ok".*classname="test".*\/>/); | |
| 205 | 211 | assert.match(fileContents, /<testcase .*name="top level".*classname="test".*\/>/); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments