flakiness 0.147.0 → 0.149.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 CHANGED
@@ -738,16 +738,16 @@ var Ranges;
738
738
 
739
739
  // src/cli/cli.ts
740
740
  import { TypedHTTP as TypedHTTP2 } from "@flakiness/shared/common/typedHttp.js";
741
- import assert3 from "assert";
741
+ import assert2 from "assert";
742
742
  import { Command, Option } from "commander";
743
- import fs8 from "fs";
743
+ import fs7 from "fs";
744
744
  import ora from "ora";
745
- import path8 from "path";
745
+ import path7 from "path";
746
746
 
747
747
  // ../package.json
748
748
  var package_default = {
749
749
  name: "flakiness",
750
- version: "0.147.0",
750
+ version: "0.149.0",
751
751
  private: true,
752
752
  scripts: {
753
753
  minor: "./version.mjs minor",
@@ -764,7 +764,6 @@ var package_default = {
764
764
  author: "Degu Labs, Inc",
765
765
  license: "Fair Source 100",
766
766
  workspaces: [
767
- "./report",
768
767
  "./sdk",
769
768
  "./cli",
770
769
  "./playwright",
@@ -791,7 +790,7 @@ var package_default = {
791
790
 
792
791
  // src/flakinessSession.ts
793
792
  import fs2 from "fs/promises";
794
- import os2 from "os";
793
+ import os from "os";
795
794
  import path2 from "path";
796
795
 
797
796
  // src/serverapi.ts
@@ -799,23 +798,11 @@ import { TypedHTTP } from "@flakiness/shared/common/typedHttp.js";
799
798
 
800
799
  // src/utils.ts
801
800
  import { ReportUtils } from "@flakiness/sdk";
802
- import assert from "assert";
803
- import { spawnSync } from "child_process";
804
801
  import crypto from "crypto";
805
802
  import fs from "fs";
806
803
  import http from "http";
807
804
  import https from "https";
808
- import os from "os";
809
- import path, { posix as posixPath, win32 as win32Path } from "path";
810
- async function existsAsync(aPath) {
811
- return fs.promises.stat(aPath).then(() => true).catch((e) => false);
812
- }
813
- function extractEnvConfiguration() {
814
- const ENV_PREFIX = "FK_ENV_";
815
- return Object.fromEntries(
816
- Object.entries(process.env).filter(([key]) => key.toUpperCase().startsWith(ENV_PREFIX.toUpperCase())).map(([key, value]) => [key.substring(ENV_PREFIX.length).toLowerCase(), (value ?? "").trim().toLowerCase()])
817
- );
818
- }
805
+ import path from "path";
819
806
  function sha1File(filePath) {
820
807
  return new Promise((resolve, reject) => {
821
808
  const hash = crypto.createHash("sha1");
@@ -919,96 +906,6 @@ var httpUtils;
919
906
  httpUtils2.postJSON = postJSON;
920
907
  })(httpUtils || (httpUtils = {}));
921
908
  var ansiRegex = new RegExp("[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))", "g");
922
- function stripAnsi(str) {
923
- return str.replace(ansiRegex, "");
924
- }
925
- async function saveReportAndAttachments(report, attachments, outputFolder) {
926
- const reportPath = path.join(outputFolder, "report.json");
927
- const attachmentsFolder = path.join(outputFolder, "attachments");
928
- await fs.promises.rm(outputFolder, { recursive: true, force: true });
929
- await fs.promises.mkdir(outputFolder, { recursive: true });
930
- await fs.promises.writeFile(reportPath, JSON.stringify(report), "utf-8");
931
- if (attachments.length)
932
- await fs.promises.mkdir(attachmentsFolder);
933
- const movedAttachments = [];
934
- for (const attachment of attachments) {
935
- const attachmentPath = path.join(attachmentsFolder, attachment.id);
936
- if (attachment.path)
937
- await fs.promises.cp(attachment.path, attachmentPath);
938
- else if (attachment.body)
939
- await fs.promises.writeFile(attachmentPath, attachment.body);
940
- movedAttachments.push({
941
- contentType: attachment.contentType,
942
- id: attachment.id,
943
- path: attachmentPath
944
- });
945
- }
946
- return movedAttachments;
947
- }
948
- function shell(command, args, options) {
949
- try {
950
- const result = spawnSync(command, args, { encoding: "utf-8", ...options });
951
- if (result.status !== 0) {
952
- return void 0;
953
- }
954
- return result.stdout.trim();
955
- } catch (e) {
956
- console.error(e);
957
- return void 0;
958
- }
959
- }
960
- function readLinuxOSRelease() {
961
- const osReleaseText = fs.readFileSync("/etc/os-release", "utf-8");
962
- return new Map(osReleaseText.toLowerCase().split("\n").filter((line) => line.includes("=")).map((line) => {
963
- line = line.trim();
964
- let [key, value] = line.split("=");
965
- if (value.startsWith('"') && value.endsWith('"'))
966
- value = value.substring(1, value.length - 1);
967
- return [key, value];
968
- }));
969
- }
970
- function osLinuxInfo() {
971
- const arch = shell(`uname`, [`-m`]);
972
- const osReleaseMap = readLinuxOSRelease();
973
- const name = osReleaseMap.get("name") ?? shell(`uname`);
974
- const version = osReleaseMap.get("version_id");
975
- return { name, arch, version };
976
- }
977
- function osDarwinInfo() {
978
- const name = "macos";
979
- const arch = shell(`uname`, [`-m`]);
980
- const version = shell(`sw_vers`, [`-productVersion`]);
981
- return { name, arch, version };
982
- }
983
- function osWinInfo() {
984
- const name = "win";
985
- const arch = process.arch;
986
- const version = os.release();
987
- return { name, arch, version };
988
- }
989
- function getOSInfo() {
990
- if (process.platform === "darwin")
991
- return osDarwinInfo();
992
- if (process.platform === "win32")
993
- return osWinInfo();
994
- return osLinuxInfo();
995
- }
996
- function inferRunUrl() {
997
- if (process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID)
998
- return `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
999
- return void 0;
1000
- }
1001
- function parseStringDate(dateString) {
1002
- return +new Date(dateString);
1003
- }
1004
- function gitCommitInfo(gitRepo) {
1005
- const sha = shell(`git`, ["rev-parse", "HEAD"], {
1006
- cwd: gitRepo,
1007
- encoding: "utf-8"
1008
- });
1009
- assert(sha, `FAILED: git rev-parse HEAD @ ${gitRepo}`);
1010
- return sha.trim();
1011
- }
1012
909
  async function resolveAttachmentPaths(report, attachmentsDir) {
1013
910
  const attachmentFiles = await listFilesRecursively(attachmentsDir);
1014
911
  const filenameToPath = new Map(attachmentFiles.map((file) => [path.basename(file), file]));
@@ -1024,7 +921,8 @@ async function resolveAttachmentPaths(report, attachmentsDir) {
1024
921
  attachmentIdToPath.set(attachment.id, {
1025
922
  contentType: attachment.contentType,
1026
923
  id: attachment.id,
1027
- path: attachmentPath
924
+ path: attachmentPath,
925
+ type: "file"
1028
926
  });
1029
927
  }
1030
928
  }
@@ -1043,65 +941,6 @@ async function listFilesRecursively(dir, result = []) {
1043
941
  }
1044
942
  return result;
1045
943
  }
1046
- function computeGitRoot(somePathInsideGitRepo) {
1047
- const root = shell(`git`, ["rev-parse", "--show-toplevel"], {
1048
- cwd: somePathInsideGitRepo,
1049
- encoding: "utf-8"
1050
- });
1051
- assert(root, `FAILED: git rev-parse --show-toplevel HEAD @ ${somePathInsideGitRepo}`);
1052
- return normalizePath(root);
1053
- }
1054
- var IS_WIN32_PATH = new RegExp("^[a-zA-Z]:\\\\", "i");
1055
- var IS_ALMOST_POSIX_PATH = new RegExp("^[a-zA-Z]:/", "i");
1056
- function normalizePath(aPath) {
1057
- if (IS_WIN32_PATH.test(aPath)) {
1058
- aPath = aPath.split(win32Path.sep).join(posixPath.sep);
1059
- }
1060
- if (IS_ALMOST_POSIX_PATH.test(aPath))
1061
- return "/" + aPath[0] + aPath.substring(2);
1062
- return aPath;
1063
- }
1064
- function gitFilePath(gitRoot, absolutePath) {
1065
- return posixPath.relative(gitRoot, absolutePath);
1066
- }
1067
- function parseDurationMS(value) {
1068
- if (isNaN(value))
1069
- throw new Error("Duration cannot be NaN");
1070
- if (value < 0)
1071
- throw new Error(`Duration cannot be less than 0, found ${value}`);
1072
- return value | 0;
1073
- }
1074
- function createEnvironments(projects) {
1075
- const envConfiguration = extractEnvConfiguration();
1076
- const osInfo = getOSInfo();
1077
- let uniqueNames = /* @__PURE__ */ new Set();
1078
- const result = /* @__PURE__ */ new Map();
1079
- for (const project of projects) {
1080
- let defaultName = project.name;
1081
- if (!defaultName.trim())
1082
- defaultName = "anonymous";
1083
- let name = defaultName;
1084
- for (let i = 2; uniqueNames.has(name); ++i)
1085
- name = `${defaultName}-${i}`;
1086
- uniqueNames.add(defaultName);
1087
- result.set(project, {
1088
- name,
1089
- systemData: {
1090
- osArch: osInfo.arch,
1091
- osName: osInfo.name,
1092
- osVersion: osInfo.version
1093
- },
1094
- userSuppliedData: {
1095
- ...envConfiguration,
1096
- ...project.metadata
1097
- },
1098
- opaqueData: {
1099
- project
1100
- }
1101
- });
1102
- }
1103
- return result;
1104
- }
1105
944
 
1106
945
  // src/serverapi.ts
1107
946
  function createServerAPI(endpoint, options) {
@@ -1120,7 +959,7 @@ function createServerAPI(endpoint, options) {
1120
959
 
1121
960
  // src/flakinessSession.ts
1122
961
  var CONFIG_DIR = (() => {
1123
- const configDir = process.platform === "darwin" ? path2.join(os2.homedir(), "Library", "Application Support", "flakiness") : process.platform === "win32" ? path2.join(os2.homedir(), "AppData", "Roaming", "flakiness") : path2.join(os2.homedir(), ".config", "flakiness");
962
+ const configDir = process.platform === "darwin" ? path2.join(os.homedir(), "Library", "Application Support", "flakiness") : process.platform === "win32" ? path2.join(os.homedir(), "AppData", "Roaming", "flakiness") : path2.join(os.homedir(), ".config", "flakiness");
1124
963
  return configDir;
1125
964
  })();
1126
965
  var CONFIG_PATH = path2.join(CONFIG_DIR, "config.json");
@@ -1162,13 +1001,14 @@ var FlakinessSession = class _FlakinessSession {
1162
1001
  };
1163
1002
 
1164
1003
  // src/cli/cmd-convert.ts
1004
+ import { GitWorktree, writeReport } from "@flakiness/sdk";
1165
1005
  import fs4 from "fs/promises";
1166
1006
  import path4 from "path";
1167
1007
 
1168
1008
  // src/junit.ts
1169
1009
  import { ReportUtils as ReportUtils2 } from "@flakiness/sdk";
1170
1010
  import { parseXml, XmlElement, XmlText } from "@rgrove/parse-xml";
1171
- import assert2 from "assert";
1011
+ import assert from "assert";
1172
1012
  import fs3 from "fs";
1173
1013
  import path3 from "path";
1174
1014
  function getProperties(element) {
@@ -1218,13 +1058,15 @@ async function parseAttachment(value) {
1218
1058
  return {
1219
1059
  contentType: "image/png",
1220
1060
  path: absolutePath,
1221
- id
1061
+ id,
1062
+ type: "file"
1222
1063
  };
1223
1064
  }
1224
1065
  return {
1225
1066
  contentType: "text/plain",
1226
1067
  id: sha1Buffer(value),
1227
- body: Buffer.from(value)
1068
+ body: Buffer.from(value),
1069
+ type: "buffer"
1228
1070
  };
1229
1071
  }
1230
1072
  async function traverseJUnitReport(context, node) {
@@ -1265,7 +1107,7 @@ async function traverseJUnitReport(context, node) {
1265
1107
  currentEnvIndex = report.environments.push(currentEnv) - 1;
1266
1108
  }
1267
1109
  } else if (element.name === "testcase") {
1268
- assert2(currentSuite);
1110
+ assert(currentSuite);
1269
1111
  const file = element.attributes["file"];
1270
1112
  const name = element.attributes["name"];
1271
1113
  const line = parseInt(element.attributes["line"], 10);
@@ -1285,7 +1127,7 @@ async function traverseJUnitReport(context, node) {
1285
1127
  id: attachment.id,
1286
1128
  contentType: attachment.contentType,
1287
1129
  //TODO: better default names for attachments?
1288
- name: attachment.path ? path3.basename(attachment.path) : `attachment`
1130
+ name: attachment.type === "file" ? path3.basename(attachment.path) : `attachment`
1289
1131
  });
1290
1132
  } else {
1291
1133
  annotations.push({
@@ -1390,7 +1232,8 @@ async function cmdConvert(junitPath, options) {
1390
1232
  commitId = options.commitId;
1391
1233
  } else {
1392
1234
  try {
1393
- commitId = gitCommitInfo(process.cwd());
1235
+ const worktree = GitWorktree.create(process.cwd());
1236
+ commitId = worktree.headCommitId();
1394
1237
  } catch (e) {
1395
1238
  console.error("Failed to get git commit info. Please provide --commit-id option.");
1396
1239
  process.exit(1);
@@ -1402,7 +1245,7 @@ async function cmdConvert(junitPath, options) {
1402
1245
  runStartTimestamp: Date.now(),
1403
1246
  runDuration: 0
1404
1247
  });
1405
- await saveReportAndAttachments(report, attachments, options.outputDir);
1248
+ await writeReport(report, attachments, options.outputDir);
1406
1249
  console.log(`\u2713 Saved to ${options.outputDir}`);
1407
1250
  }
1408
1251
  async function findXmlFiles(dir, result = []) {
@@ -1482,7 +1325,7 @@ var KNOWN_CLIENT_IDS = {
1482
1325
 
1483
1326
  // src/cli/cmd-login.ts
1484
1327
  import open from "open";
1485
- import os3 from "os";
1328
+ import os2 from "os";
1486
1329
 
1487
1330
  // src/cli/cmd-logout.ts
1488
1331
  async function cmdLogout() {
@@ -1502,7 +1345,7 @@ async function cmdLogin(endpoint = DEFAULT_FLAKINESS_ENDPOINT) {
1502
1345
  const api = createServerAPI(endpoint);
1503
1346
  const data = await api.deviceauth.createRequest.POST({
1504
1347
  clientId: KNOWN_CLIENT_IDS.OFFICIAL_CLI,
1505
- name: os3.hostname()
1348
+ name: os2.hostname()
1506
1349
  });
1507
1350
  await open(new URL(data.verificationUrl, endpoint).href);
1508
1351
  console.log(`Please navigate to ${new URL(data.verificationUrl, endpoint)}`);
@@ -1567,246 +1410,31 @@ async function cmdUnlink() {
1567
1410
  await config.save();
1568
1411
  }
1569
1412
 
1570
- // src/cli/cmd-upload-playwright-json.ts
1571
- import { ReportUploader } from "@flakiness/sdk";
1572
- import fs6 from "fs/promises";
1573
- import path6 from "path";
1574
-
1575
- // src/playwrightJSONReport.ts
1576
- import { FlakinessReport as FK2, ReportUtils as ReportUtils3 } from "@flakiness/sdk";
1577
- import debug from "debug";
1578
- import { posix as posixPath2 } from "path";
1579
- var dlog = debug("flakiness:json-report");
1580
- var PlaywrightJSONReport;
1581
- ((PlaywrightJSONReport2) => {
1582
- function collectMetadata(somePathInsideProject = process.cwd()) {
1583
- const commitId = gitCommitInfo(somePathInsideProject);
1584
- const osInfo = getOSInfo();
1585
- const metadata = {
1586
- gitRoot: computeGitRoot(somePathInsideProject),
1587
- commitId,
1588
- osName: osInfo.name,
1589
- arch: osInfo.arch,
1590
- osVersion: osInfo.version,
1591
- runURL: inferRunUrl()
1592
- };
1593
- dlog(`metadata directory: ${somePathInsideProject}`);
1594
- dlog(`metadata: ${JSON.stringify(metadata)}`);
1595
- dlog(`commit info: ${JSON.stringify(commitId)}`);
1596
- dlog(`os info: ${JSON.stringify(osInfo)}`);
1597
- return metadata;
1598
- }
1599
- PlaywrightJSONReport2.collectMetadata = collectMetadata;
1600
- async function parse(metadata, jsonReport, options) {
1601
- const context = {
1602
- projectId2environmentIdx: /* @__PURE__ */ new Map(),
1603
- testBaseDir: normalizePath(jsonReport.config.rootDir),
1604
- gitRoot: metadata.gitRoot,
1605
- attachments: /* @__PURE__ */ new Map(),
1606
- unaccessibleAttachmentPaths: [],
1607
- extractAttachments: options.extractAttachments
1608
- };
1609
- const configPath = jsonReport.config.configFile ? gitFilePath(context.gitRoot, normalizePath(jsonReport.config.configFile)) : void 0;
1610
- const report = {
1611
- category: FK2.CATEGORY_PLAYWRIGHT,
1612
- commitId: metadata.commitId,
1613
- configPath,
1614
- url: metadata.runURL,
1615
- environments: [],
1616
- suites: [],
1617
- opaqueData: jsonReport.config,
1618
- unattributedErrors: jsonReport.errors.map((error) => parseJSONError(context, error)),
1619
- // The report.stats is a releatively new addition to Playwright's JSONReport,
1620
- // so we have to polyfill with some reasonable values when it's missing.
1621
- duration: jsonReport.stats?.duration && jsonReport.stats?.duration > 0 ? parseDurationMS(jsonReport.stats.duration) : 0,
1622
- startTimestamp: jsonReport.stats && jsonReport.stats.startTime ? parseStringDate(jsonReport.stats.startTime) : Date.now()
1623
- };
1624
- report.environments = [...createEnvironments(jsonReport.config.projects).values()];
1625
- for (let envIdx = 0; envIdx < report.environments.length; ++envIdx)
1626
- context.projectId2environmentIdx.set(jsonReport.config.projects[envIdx].id, envIdx);
1627
- report.suites = await Promise.all(jsonReport.suites.map((suite) => parseJSONSuite(context, suite)));
1628
- return {
1629
- report: ReportUtils3.normalizeReport(report),
1630
- attachments: [...context.attachments.values()],
1631
- unaccessibleAttachmentPaths: context.unaccessibleAttachmentPaths
1632
- };
1633
- }
1634
- PlaywrightJSONReport2.parse = parse;
1635
- })(PlaywrightJSONReport || (PlaywrightJSONReport = {}));
1636
- async function parseJSONSuite(context, jsonSuite) {
1637
- let type = "suite";
1638
- if (jsonSuite.column === 0 && jsonSuite.line === 0)
1639
- type = "file";
1640
- else if (!jsonSuite.title)
1641
- type = "anonymous suite";
1642
- const suite = {
1643
- type,
1644
- title: jsonSuite.title,
1645
- location: {
1646
- file: gitFilePath(context.gitRoot, normalizePath(jsonSuite.file)),
1647
- line: jsonSuite.line,
1648
- column: jsonSuite.column
1649
- }
1650
- };
1651
- if (jsonSuite.suites && jsonSuite.suites.length)
1652
- suite.suites = await Promise.all(jsonSuite.suites.map((suite2) => parseJSONSuite(context, suite2)));
1653
- if (jsonSuite.specs && jsonSuite.specs.length)
1654
- suite.tests = await Promise.all(jsonSuite.specs.map((spec) => parseJSONSpec(context, spec)));
1655
- return suite;
1656
- }
1657
- async function parseJSONSpec(context, jsonSpec) {
1658
- const test = {
1659
- title: jsonSpec.title,
1660
- tags: jsonSpec.tags,
1661
- location: {
1662
- file: gitFilePath(context.gitRoot, normalizePath(posixPath2.join(context.testBaseDir, normalizePath(jsonSpec.file)))),
1663
- line: jsonSpec.line,
1664
- column: jsonSpec.column
1665
- },
1666
- attempts: []
1667
- };
1668
- for (const jsonTest of jsonSpec.tests) {
1669
- const environmentIdx = context.projectId2environmentIdx.get(jsonTest.projectId);
1670
- if (environmentIdx === void 0)
1671
- throw new Error("Inconsistent report - no project for a test found!");
1672
- const testResults = jsonTest.results.filter((result) => result.status !== void 0);
1673
- if (!testResults.length)
1674
- continue;
1675
- test.attempts.push(...await Promise.all(testResults.map((jsonTestResult) => parseJSONTestResult(context, jsonTest, environmentIdx, jsonTestResult))));
1676
- }
1677
- return test;
1678
- }
1679
- function createLocation(context, location) {
1680
- return {
1681
- file: gitFilePath(context.gitRoot, normalizePath(location.file)),
1682
- line: location.line,
1683
- column: location.column
1684
- };
1685
- }
1686
- async function parseJSONTestResult(context, jsonTest, environmentIdx, jsonTestResult) {
1687
- const attachments = [];
1688
- const attempt = {
1689
- timeout: parseDurationMS(jsonTest.timeout),
1690
- annotations: jsonTest.annotations.map((annotation) => ({
1691
- type: annotation.type,
1692
- description: annotation.description,
1693
- location: annotation.location ? createLocation(context, annotation.location) : void 0
1694
- })),
1695
- environmentIdx,
1696
- expectedStatus: jsonTest.expectedStatus,
1697
- parallelIndex: jsonTestResult.parallelIndex,
1698
- status: jsonTestResult.status,
1699
- errors: jsonTestResult.errors && jsonTestResult.errors.length ? jsonTestResult.errors.map((error) => parseJSONError(context, error)) : void 0,
1700
- stdout: jsonTestResult.stdout && jsonTestResult.stdout.length ? jsonTestResult.stdout : void 0,
1701
- stderr: jsonTestResult.stderr && jsonTestResult.stderr.length ? jsonTestResult.stderr : void 0,
1702
- steps: jsonTestResult.steps ? jsonTestResult.steps.map((jsonTestStep) => parseJSONTestStep(context, jsonTestStep)) : void 0,
1703
- startTimestamp: parseStringDate(jsonTestResult.startTime),
1704
- duration: jsonTestResult.duration && jsonTestResult.duration > 0 ? parseDurationMS(jsonTestResult.duration) : 0,
1705
- attachments
1706
- };
1707
- if (context.extractAttachments) {
1708
- await Promise.all((jsonTestResult.attachments ?? []).map(async (jsonAttachment) => {
1709
- if (jsonAttachment.path && !await existsAsync(jsonAttachment.path)) {
1710
- context.unaccessibleAttachmentPaths.push(jsonAttachment.path);
1711
- return;
1712
- }
1713
- const id = jsonAttachment.path ? await sha1File(jsonAttachment.path) : sha1Buffer(jsonAttachment.body ?? "");
1714
- context.attachments.set(id, {
1715
- contentType: jsonAttachment.contentType,
1716
- id,
1717
- body: jsonAttachment.body ? Buffer.from(jsonAttachment.body) : void 0,
1718
- path: jsonAttachment.path
1719
- });
1720
- attachments.push({
1721
- id,
1722
- name: jsonAttachment.name,
1723
- contentType: jsonAttachment.contentType
1724
- });
1725
- }));
1726
- }
1727
- return attempt;
1728
- }
1729
- function parseJSONTestStep(context, jsonStep) {
1730
- const step = {
1731
- // NOTE: jsonStep.duration was -1 in some playwright versions
1732
- duration: parseDurationMS(Math.max(jsonStep.duration, 0)),
1733
- title: jsonStep.title
1734
- };
1735
- if (jsonStep.error)
1736
- step.error = parseJSONError(context, jsonStep.error);
1737
- if (jsonStep.steps)
1738
- step.steps = jsonStep.steps.map((childJSONStep) => parseJSONTestStep(context, childJSONStep));
1739
- return step;
1740
- }
1741
- function parseJSONError(context, error) {
1742
- return {
1743
- location: error.location ? createLocation(context, error.location) : void 0,
1744
- message: error.message ? stripAnsi(error.message).split("\n")[0] : void 0,
1745
- stack: error.stack,
1746
- value: error.value
1747
- };
1748
- }
1749
-
1750
- // src/cli/cmd-upload-playwright-json.ts
1751
- async function cmdUploadPlaywrightJson(relativePath, options) {
1752
- const fullPath = path6.resolve(relativePath);
1753
- if (!await fs6.access(fullPath, fs6.constants.F_OK).then(() => true).catch(() => false)) {
1754
- console.error(`Error: path ${fullPath} is not accessible`);
1755
- process.exit(1);
1756
- }
1757
- const text = await fs6.readFile(fullPath, "utf-8");
1758
- const playwrightJson = JSON.parse(text);
1759
- const { attachments, report, unaccessibleAttachmentPaths } = await PlaywrightJSONReport.parse(PlaywrightJSONReport.collectMetadata(), playwrightJson, {
1760
- extractAttachments: true
1761
- });
1762
- for (const unaccessibleAttachment of unaccessibleAttachmentPaths)
1763
- console.warn(`WARN: cannot access attachment ${unaccessibleAttachment}`);
1764
- const uploader = new ReportUploader({
1765
- flakinessAccessToken: options.accessToken,
1766
- flakinessEndpoint: options.endpoint
1767
- });
1768
- const upload = uploader.createUpload(report, attachments);
1769
- const uploadResult = await upload.upload();
1770
- if (!uploadResult.success) {
1771
- console.log(`[flakiness.io] X Failed to upload to ${options.endpoint}: ${uploadResult.message}`);
1772
- } else {
1773
- console.log(`[flakiness.io] \u2713 Report uploaded ${uploadResult.reportUrl ?? uploadResult.message ?? ""}`);
1774
- }
1775
- }
1776
-
1777
1413
  // src/cli/cmd-upload.ts
1778
- import { ReportUploader as ReportUploader2 } from "@flakiness/sdk";
1414
+ import { uploadReport } from "@flakiness/sdk";
1779
1415
  import chalk from "chalk";
1780
- import fs7 from "fs/promises";
1781
- import path7 from "path";
1416
+ import fs6 from "fs/promises";
1417
+ import path6 from "path";
1782
1418
  var warn = (txt) => console.warn(chalk.yellow(`[flakiness.io] WARN: ${txt}`));
1783
1419
  var err = (txt) => console.error(chalk.red(`[flakiness.io] Error: ${txt}`));
1784
- var log = (txt) => console.log(`[flakiness.io] ${txt}`);
1785
1420
  async function cmdUpload(relativePaths, options) {
1786
- const uploader = new ReportUploader2({
1787
- flakinessAccessToken: options.accessToken,
1788
- flakinessEndpoint: options.endpoint
1789
- });
1790
1421
  for (const relativePath of relativePaths) {
1791
- const fullPath = path7.resolve(relativePath);
1792
- if (!await fs7.access(fullPath, fs7.constants.F_OK).then(() => true).catch(() => false)) {
1422
+ const fullPath = path6.resolve(relativePath);
1423
+ if (!await fs6.access(fullPath, fs6.constants.F_OK).then(() => true).catch(() => false)) {
1793
1424
  err(`Path ${fullPath} is not accessible!`);
1794
1425
  process.exit(1);
1795
1426
  }
1796
- const text = await fs7.readFile(fullPath, "utf-8");
1427
+ const text = await fs6.readFile(fullPath, "utf-8");
1797
1428
  const report = JSON.parse(text);
1798
- const attachmentsDir = options.attachmentsDir ?? path7.dirname(fullPath);
1429
+ const attachmentsDir = options.attachmentsDir ?? path6.dirname(fullPath);
1799
1430
  const { attachmentIdToPath, missingAttachments } = await resolveAttachmentPaths(report, attachmentsDir);
1800
1431
  if (missingAttachments.length) {
1801
1432
  warn(`Missing ${missingAttachments.length} attachments`);
1802
1433
  }
1803
- const upload = uploader.createUpload(report, Array.from(attachmentIdToPath.values()));
1804
- const uploadResult = await upload.upload();
1805
- if (!uploadResult.success) {
1806
- err(`Failed to upload to ${options.endpoint}: ${uploadResult.message}`);
1807
- } else {
1808
- log(`\u2713 Uploaded ${uploadResult.reportUrl ?? uploadResult.message ?? ""}`);
1809
- }
1434
+ await uploadReport(report, Array.from(attachmentIdToPath.values()), {
1435
+ flakinessAccessToken: options.accessToken,
1436
+ flakinessEndpoint: options.endpoint
1437
+ });
1810
1438
  }
1811
1439
  }
1812
1440
 
@@ -1854,15 +1482,12 @@ async function ensureAccessToken(options) {
1854
1482
  }
1855
1483
  }
1856
1484
  }
1857
- assert3(accessToken, `Please either pass FLAKINESS_ACCESS_TOKEN or run login + link`);
1485
+ assert2(accessToken, `Please either pass FLAKINESS_ACCESS_TOKEN or run login + link`);
1858
1486
  return {
1859
1487
  ...options,
1860
1488
  accessToken
1861
1489
  };
1862
1490
  }
1863
- program.command("upload-playwright-json", { hidden: true }).description("Upload Playwright Test JSON report to the flakiness.io service").argument("<relative-path-to-json>", "Path to the Playwright JSON report file").addOption(optAccessToken).addOption(optEndpoint).action(async (relativePath, options) => runCommand(async () => {
1864
- await cmdUploadPlaywrightJson(relativePath, await ensureAccessToken(options));
1865
- }));
1866
1491
  program.command("login").description("Login to the Flakiness.io service").addOption(optEndpoint).action(async (options) => runCommand(async () => {
1867
1492
  await cmdLogin(options.endpoint);
1868
1493
  }));
@@ -1939,7 +1564,7 @@ program.command("download").description("Download run").addOption(optSince).addO
1939
1564
  });
1940
1565
  console.log(`Found ${Ranges.cardinality(runIds)} reports uploaded since ${options.since}`);
1941
1566
  }
1942
- const alreadyExisting = fs8.readdirSync(process.cwd());
1567
+ const alreadyExisting = fs7.readdirSync(process.cwd());
1943
1568
  const downloadedRuns = alreadyExisting.filter((entry) => entry.startsWith("fkrun-")).map((run) => parseInt(run.substring(`fkrun-`.length), 10)).filter((runId) => !isNaN(runId));
1944
1569
  console.log(`Found ${downloadedRuns.length} locally downloaded reports`);
1945
1570
  const toBeDownloaded = Ranges.subtract(runIds, Ranges.fromList(downloadedRuns));
@@ -1968,7 +1593,7 @@ program.command("upload").description("Upload Flakiness report to the flakiness.
1968
1593
  });
1969
1594
  });
1970
1595
  program.command("show").description("Show flakiness report").argument("[relative-path]", "Path to the Flakiness report file or folder that contains `report.json`. (default: flakiness-report)").action(async (arg) => runCommand(async () => {
1971
- const dir = path8.resolve(arg ?? "flakiness-report");
1596
+ const dir = path7.resolve(arg ?? "flakiness-report");
1972
1597
  await showReport(dir);
1973
1598
  }));
1974
1599
  program.command("convert-junit").description("Convert JUnit XML report(s) to Flakiness report format").argument("<junit-root-dir-path>", "Path to JUnit XML file or directory containing XML files").option("--env-name <name>", "Environment name for the report", "junit").option("--commit-id <id>", "Git commit ID (auto-detected if not provided)").option("--output-dir <dir>", "Output directory for the report", "flakiness-report").action(async (junitPath, options) => {