flakiness 0.219.0 → 0.221.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/lib/cli/cli.js +52 -14
- package/lib/junit.js +35 -6
- package/package.json +6 -4
- package/types/tsconfig.tsbuildinfo +1 -1
package/lib/cli/cli.js
CHANGED
|
@@ -1021,14 +1021,16 @@ var WireTypes;
|
|
|
1021
1021
|
})(WireTypes || (WireTypes = {}));
|
|
1022
1022
|
|
|
1023
1023
|
// src/cli/cli.ts
|
|
1024
|
+
import chalk5 from "chalk";
|
|
1024
1025
|
import { Command, Option } from "commander";
|
|
1025
1026
|
import debug2 from "debug";
|
|
1027
|
+
import fs7 from "fs";
|
|
1026
1028
|
import path7 from "path";
|
|
1027
1029
|
|
|
1028
1030
|
// ../package.json
|
|
1029
1031
|
var package_default = {
|
|
1030
1032
|
name: "@flakiness/monorepo",
|
|
1031
|
-
version: "0.
|
|
1033
|
+
version: "0.221.0",
|
|
1032
1034
|
type: "module",
|
|
1033
1035
|
private: true,
|
|
1034
1036
|
scripts: {
|
|
@@ -1519,6 +1521,35 @@ import assert from "assert";
|
|
|
1519
1521
|
import fs2 from "fs";
|
|
1520
1522
|
import mime from "mime";
|
|
1521
1523
|
import path2 from "path";
|
|
1524
|
+
import { Temporal } from "temporal-polyfill";
|
|
1525
|
+
var gTZAbbreviationToIANATimezone;
|
|
1526
|
+
function tzAbbreviationToIANA(tz) {
|
|
1527
|
+
if (!gTZAbbreviationToIANATimezone) {
|
|
1528
|
+
gTZAbbreviationToIANATimezone = /* @__PURE__ */ new Map();
|
|
1529
|
+
const probes = [/* @__PURE__ */ new Date("2026-06-15T12:00:00Z"), /* @__PURE__ */ new Date("2026-01-15T12:00:00Z")];
|
|
1530
|
+
for (const tz2 of Intl.supportedValuesOf("timeZone")) {
|
|
1531
|
+
for (const date of probes) {
|
|
1532
|
+
const parts = new Intl.DateTimeFormat("en-US", { timeZone: tz2, timeZoneName: "short" }).formatToParts(date);
|
|
1533
|
+
const abbr = parts.find((p) => p.type === "timeZoneName")?.value;
|
|
1534
|
+
if (abbr)
|
|
1535
|
+
gTZAbbreviationToIANATimezone.set(abbr, tz2);
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
return gTZAbbreviationToIANATimezone.get(tz);
|
|
1540
|
+
}
|
|
1541
|
+
function parseTimestamp(timestamp) {
|
|
1542
|
+
const native = new Date(timestamp).getTime();
|
|
1543
|
+
if (!isNaN(native))
|
|
1544
|
+
return native;
|
|
1545
|
+
const parts = timestamp.split(/\s+/);
|
|
1546
|
+
const iana = parts.length === 2 ? tzAbbreviationToIANA(parts[1]) : void 0;
|
|
1547
|
+
if (iana) {
|
|
1548
|
+
const d = Temporal.PlainDateTime.from(parts[0]);
|
|
1549
|
+
return d.toZonedDateTime(iana).epochMilliseconds;
|
|
1550
|
+
}
|
|
1551
|
+
throw new Error(`failed to parse timestamp: ${timestamp}`);
|
|
1552
|
+
}
|
|
1522
1553
|
function getProperties(element) {
|
|
1523
1554
|
const propertiesNodes = element.children.filter((node) => node instanceof XmlElement).filter((node) => node.name === "properties");
|
|
1524
1555
|
if (!propertiesNodes.length)
|
|
@@ -1583,7 +1614,7 @@ async function traverseJUnitReport(context, node) {
|
|
|
1583
1614
|
return;
|
|
1584
1615
|
let { currentEnv, currentEnvIndex, currentSuite, report, currentTimeMs } = context;
|
|
1585
1616
|
if (element.attributes["timestamp"])
|
|
1586
|
-
currentTimeMs =
|
|
1617
|
+
currentTimeMs = parseTimestamp(element.attributes["timestamp"]);
|
|
1587
1618
|
if (element.name === "testsuite") {
|
|
1588
1619
|
const file = element.attributes["file"];
|
|
1589
1620
|
const line = parseInt(element.attributes["line"], 10);
|
|
@@ -1607,12 +1638,12 @@ async function traverseJUnitReport(context, node) {
|
|
|
1607
1638
|
report.suites.push(newSuite);
|
|
1608
1639
|
}
|
|
1609
1640
|
currentSuite = newSuite;
|
|
1610
|
-
const
|
|
1611
|
-
if (
|
|
1641
|
+
const metadata = getProperties(element);
|
|
1642
|
+
if (metadata.length) {
|
|
1612
1643
|
currentEnv = structuredClone(currentEnv);
|
|
1613
|
-
currentEnv.
|
|
1614
|
-
for (const [key, value] of
|
|
1615
|
-
currentEnv.
|
|
1644
|
+
currentEnv.metadata ??= {};
|
|
1645
|
+
for (const [key, value] of metadata)
|
|
1646
|
+
currentEnv.metadata[key] = value;
|
|
1616
1647
|
currentEnvIndex = report.environments.push(currentEnv) - 1;
|
|
1617
1648
|
}
|
|
1618
1649
|
} else if (element.name === "testcase") {
|
|
@@ -2192,7 +2223,7 @@ var warn = (txt) => console.warn(chalk4.yellow(`[flakiness.io] WARN: ${txt}`));
|
|
|
2192
2223
|
var err = (txt) => console.error(chalk4.red(`[flakiness.io] Error: ${txt}`));
|
|
2193
2224
|
async function cmdUpload(relativePaths, options) {
|
|
2194
2225
|
const total = relativePaths.length;
|
|
2195
|
-
const spinner =
|
|
2226
|
+
const spinner = options.progress ? ora2("Uploading reports:").start() : void 0;
|
|
2196
2227
|
let uploaded = 0;
|
|
2197
2228
|
let sharedAuth;
|
|
2198
2229
|
async function ensureAuth(flakinessProject) {
|
|
@@ -2223,12 +2254,12 @@ async function cmdUpload(relativePaths, options) {
|
|
|
2223
2254
|
const status = await uploadReport(report, attachments, {
|
|
2224
2255
|
flakinessAccessToken: auth2.accessToken,
|
|
2225
2256
|
flakinessEndpoint: auth2.endpoint,
|
|
2226
|
-
logger: {
|
|
2257
|
+
logger: spinner ? {
|
|
2227
2258
|
log: () => {
|
|
2228
2259
|
},
|
|
2229
2260
|
error: console.error,
|
|
2230
2261
|
warn: console.warn
|
|
2231
|
-
}
|
|
2262
|
+
} : void 0
|
|
2232
2263
|
});
|
|
2233
2264
|
if (status.status === "failed" || status.status === "skipped") {
|
|
2234
2265
|
spinner?.stop();
|
|
@@ -2238,7 +2269,9 @@ async function cmdUpload(relativePaths, options) {
|
|
|
2238
2269
|
if (spinner)
|
|
2239
2270
|
spinner.text = `Uploaded ${Math.floor(uploaded / total * 100)}% [${uploaded}/${total}] reports`;
|
|
2240
2271
|
}
|
|
2241
|
-
spinner
|
|
2272
|
+
if (spinner) {
|
|
2273
|
+
spinner.succeed(`Uploaded ${total} report${total === 1 ? "" : "s"}`);
|
|
2274
|
+
}
|
|
2242
2275
|
}
|
|
2243
2276
|
|
|
2244
2277
|
// src/cli/cli.ts
|
|
@@ -2371,18 +2404,23 @@ program.command("download").description("Download run").addOption(mustFlakinessP
|
|
|
2371
2404
|
accessToken: options.accessToken
|
|
2372
2405
|
});
|
|
2373
2406
|
}));
|
|
2374
|
-
program.command("upload").description("Upload Flakiness report to the flakiness.io service").argument("<relative-paths...>", "Paths to Flakiness report files or directories").addOption(optAccessToken).addOption(optFlakinessProject).addOption(optEndpoint).action(async (relativePaths, options) => {
|
|
2407
|
+
program.command("upload").description("Upload Flakiness report to the flakiness.io service").argument("<relative-paths...>", "Paths to Flakiness report files or directories").addOption(optAccessToken).addOption(optFlakinessProject).addOption(optEndpoint).option("--progress", "Show upload progress spinner instead of printing run URLs").action(async (relativePaths, options) => {
|
|
2375
2408
|
await runCommand(async () => {
|
|
2376
2409
|
await cmdUpload(relativePaths, {
|
|
2377
2410
|
endpoint: options.endpoint,
|
|
2378
2411
|
accessToken: options.accessToken,
|
|
2379
|
-
flakinessProject: options.project
|
|
2412
|
+
flakinessProject: options.project,
|
|
2413
|
+
progress: options.progress
|
|
2380
2414
|
});
|
|
2381
2415
|
});
|
|
2382
2416
|
});
|
|
2383
2417
|
var optViewerUrl = new Option("-e, --viewer-url <url>", "A URL where report viewer is deployed").default("https://report.flakiness.io").env("FLAKINESS_ENDPOINT");
|
|
2384
|
-
program.command("show").description("Show flakiness report").argument("[relative-path]", "Path to the
|
|
2418
|
+
program.command("show").description("Show flakiness report").argument("[relative-path]", "Path to the folder that contains `report.json`", "flakiness-report").addOption(optViewerUrl).action(async (arg, options) => runCommand(async () => {
|
|
2385
2419
|
const dir = path7.resolve(arg ?? "flakiness-report");
|
|
2420
|
+
if (!fs7.existsSync(dir))
|
|
2421
|
+
throw new Error(`Directory ${chalk5.bold(dir)} does not exist`);
|
|
2422
|
+
if (!fs7.existsSync(path7.join(dir, "report.json")))
|
|
2423
|
+
throw new Error(`The folder ${chalk5.bold(dir)} does not contain report.json - is this a Flakiness report folder?`);
|
|
2386
2424
|
await showReport(dir, {
|
|
2387
2425
|
reportViewerUrl: options.viewerUrl
|
|
2388
2426
|
});
|
package/lib/junit.js
CHANGED
|
@@ -6,6 +6,35 @@ import assert from "assert";
|
|
|
6
6
|
import fs from "fs";
|
|
7
7
|
import mime from "mime";
|
|
8
8
|
import path from "path";
|
|
9
|
+
import { Temporal } from "temporal-polyfill";
|
|
10
|
+
var gTZAbbreviationToIANATimezone;
|
|
11
|
+
function tzAbbreviationToIANA(tz) {
|
|
12
|
+
if (!gTZAbbreviationToIANATimezone) {
|
|
13
|
+
gTZAbbreviationToIANATimezone = /* @__PURE__ */ new Map();
|
|
14
|
+
const probes = [/* @__PURE__ */ new Date("2026-06-15T12:00:00Z"), /* @__PURE__ */ new Date("2026-01-15T12:00:00Z")];
|
|
15
|
+
for (const tz2 of Intl.supportedValuesOf("timeZone")) {
|
|
16
|
+
for (const date of probes) {
|
|
17
|
+
const parts = new Intl.DateTimeFormat("en-US", { timeZone: tz2, timeZoneName: "short" }).formatToParts(date);
|
|
18
|
+
const abbr = parts.find((p) => p.type === "timeZoneName")?.value;
|
|
19
|
+
if (abbr)
|
|
20
|
+
gTZAbbreviationToIANATimezone.set(abbr, tz2);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return gTZAbbreviationToIANATimezone.get(tz);
|
|
25
|
+
}
|
|
26
|
+
function parseTimestamp(timestamp) {
|
|
27
|
+
const native = new Date(timestamp).getTime();
|
|
28
|
+
if (!isNaN(native))
|
|
29
|
+
return native;
|
|
30
|
+
const parts = timestamp.split(/\s+/);
|
|
31
|
+
const iana = parts.length === 2 ? tzAbbreviationToIANA(parts[1]) : void 0;
|
|
32
|
+
if (iana) {
|
|
33
|
+
const d = Temporal.PlainDateTime.from(parts[0]);
|
|
34
|
+
return d.toZonedDateTime(iana).epochMilliseconds;
|
|
35
|
+
}
|
|
36
|
+
throw new Error(`failed to parse timestamp: ${timestamp}`);
|
|
37
|
+
}
|
|
9
38
|
function getProperties(element) {
|
|
10
39
|
const propertiesNodes = element.children.filter((node) => node instanceof XmlElement).filter((node) => node.name === "properties");
|
|
11
40
|
if (!propertiesNodes.length)
|
|
@@ -70,7 +99,7 @@ async function traverseJUnitReport(context, node) {
|
|
|
70
99
|
return;
|
|
71
100
|
let { currentEnv, currentEnvIndex, currentSuite, report, currentTimeMs } = context;
|
|
72
101
|
if (element.attributes["timestamp"])
|
|
73
|
-
currentTimeMs =
|
|
102
|
+
currentTimeMs = parseTimestamp(element.attributes["timestamp"]);
|
|
74
103
|
if (element.name === "testsuite") {
|
|
75
104
|
const file = element.attributes["file"];
|
|
76
105
|
const line = parseInt(element.attributes["line"], 10);
|
|
@@ -94,12 +123,12 @@ async function traverseJUnitReport(context, node) {
|
|
|
94
123
|
report.suites.push(newSuite);
|
|
95
124
|
}
|
|
96
125
|
currentSuite = newSuite;
|
|
97
|
-
const
|
|
98
|
-
if (
|
|
126
|
+
const metadata = getProperties(element);
|
|
127
|
+
if (metadata.length) {
|
|
99
128
|
currentEnv = structuredClone(currentEnv);
|
|
100
|
-
currentEnv.
|
|
101
|
-
for (const [key, value] of
|
|
102
|
-
currentEnv.
|
|
129
|
+
currentEnv.metadata ??= {};
|
|
130
|
+
for (const [key, value] of metadata)
|
|
131
|
+
currentEnv.metadata[key] = value;
|
|
103
132
|
currentEnvIndex = report.environments.push(currentEnv) - 1;
|
|
104
133
|
}
|
|
105
134
|
} else if (element.name === "testcase") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flakiness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.221.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"bin": {
|
|
6
6
|
"flakiness": "./lib/cli/cli.js"
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"@types/debug": "^4.1.12",
|
|
24
24
|
"@types/express": "^4.17.20",
|
|
25
25
|
"gray-matter": "^4.0.3",
|
|
26
|
-
"@flakiness/
|
|
27
|
-
"@flakiness/
|
|
26
|
+
"@flakiness/server": "0.221.0",
|
|
27
|
+
"@flakiness/shared": "0.221.0"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@flakiness/flakiness-report": "^0.28.0",
|
|
@@ -35,9 +35,11 @@
|
|
|
35
35
|
"debug": "^4.4.3",
|
|
36
36
|
"mime": "^4.1.0",
|
|
37
37
|
"open": "^10.2.0",
|
|
38
|
-
"ora": "^8.2.0"
|
|
38
|
+
"ora": "^8.2.0",
|
|
39
|
+
"temporal-polyfill": "^0.3.0"
|
|
39
40
|
},
|
|
40
41
|
"scripts": {
|
|
42
|
+
"test": "pnpm playwright test",
|
|
41
43
|
"build:all": "pnpm build:win && pnpm build:linux && pnpm build:mac && pnpm build:alpine && pnpm build:mac_intel",
|
|
42
44
|
"build:win": "bun build ./lib/cli/cli.js --compile --minify --target=bun-windows-x64 --outfile dist/flakiness-win-x64.exe",
|
|
43
45
|
"build:linux": "bun build ./lib/cli/cli.js --compile --minify --target=bun-linux-x64 --outfile dist/flakiness-linux-x64",
|