@flakiness/sdk 0.130.0 → 0.131.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 +71 -47
- package/lib/cli/cmd-convert.js +32 -21
- package/lib/cli/cmd-link.js +79 -4
- package/lib/cli/cmd-login.js +6 -3
- package/lib/playwright-test.js +26 -21
- package/lib/utils.js +24 -0
- package/package.json +4 -4
- package/types/tsconfig.tsbuildinfo +1 -1
package/lib/cli/cli.js
CHANGED
|
@@ -742,7 +742,7 @@ import path11 from "path";
|
|
|
742
742
|
// ../package.json
|
|
743
743
|
var package_default = {
|
|
744
744
|
name: "flakiness",
|
|
745
|
-
version: "0.
|
|
745
|
+
version: "0.131.0",
|
|
746
746
|
private: true,
|
|
747
747
|
scripts: {
|
|
748
748
|
minor: "./version.mjs minor",
|
|
@@ -911,6 +911,29 @@ var ansiRegex = new RegExp("[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:
|
|
|
911
911
|
function stripAnsi(str) {
|
|
912
912
|
return str.replace(ansiRegex, "");
|
|
913
913
|
}
|
|
914
|
+
async function saveReportAndAttachments(report, attachments, outputFolder) {
|
|
915
|
+
const reportPath = path.join(outputFolder, "report.json");
|
|
916
|
+
const attachmentsFolder = path.join(outputFolder, "attachments");
|
|
917
|
+
await fs.promises.rm(outputFolder, { recursive: true, force: true });
|
|
918
|
+
await fs.promises.mkdir(outputFolder, { recursive: true });
|
|
919
|
+
await fs.promises.writeFile(reportPath, JSON.stringify(report), "utf-8");
|
|
920
|
+
if (attachments.length)
|
|
921
|
+
await fs.promises.mkdir(attachmentsFolder);
|
|
922
|
+
const movedAttachments = [];
|
|
923
|
+
for (const attachment of attachments) {
|
|
924
|
+
const attachmentPath = path.join(attachmentsFolder, attachment.id);
|
|
925
|
+
if (attachment.path)
|
|
926
|
+
await fs.promises.cp(attachment.path, attachmentPath);
|
|
927
|
+
else if (attachment.body)
|
|
928
|
+
await fs.promises.writeFile(attachmentPath, attachment.body);
|
|
929
|
+
movedAttachments.push({
|
|
930
|
+
contentType: attachment.contentType,
|
|
931
|
+
id: attachment.id,
|
|
932
|
+
path: attachmentPath
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
return movedAttachments;
|
|
936
|
+
}
|
|
914
937
|
function shell(command, args, options) {
|
|
915
938
|
try {
|
|
916
939
|
const result = spawnSync(command, args, { encoding: "utf-8", ...options });
|
|
@@ -1436,21 +1459,8 @@ async function cmdConvert(junitPath, options) {
|
|
|
1436
1459
|
runStartTimestamp: Date.now(),
|
|
1437
1460
|
runDuration: 0
|
|
1438
1461
|
});
|
|
1439
|
-
await
|
|
1440
|
-
console.log(
|
|
1441
|
-
if (attachments.length > 0) {
|
|
1442
|
-
await fs5.mkdir("fkattachments", { recursive: true });
|
|
1443
|
-
for (const attachment of attachments) {
|
|
1444
|
-
if (attachment.path) {
|
|
1445
|
-
const destPath = path5.join("fkattachments", attachment.id);
|
|
1446
|
-
await fs5.copyFile(attachment.path, destPath);
|
|
1447
|
-
} else if (attachment.body) {
|
|
1448
|
-
const destPath = path5.join("fkattachments", attachment.id);
|
|
1449
|
-
await fs5.writeFile(destPath, attachment.body);
|
|
1450
|
-
}
|
|
1451
|
-
}
|
|
1452
|
-
console.log(`\u2713 Saved ${attachments.length} attachments to fkattachments/`);
|
|
1453
|
-
}
|
|
1462
|
+
await saveReportAndAttachments(report, attachments, options.outputDir);
|
|
1463
|
+
console.log(`\u2713 Saved to ${options.outputDir}`);
|
|
1454
1464
|
}
|
|
1455
1465
|
async function findXmlFiles(dir, result = []) {
|
|
1456
1466
|
const entries = await fs5.readdir(dir, { withFileTypes: true });
|
|
@@ -1505,28 +1515,6 @@ async function cmdDownload(session2, project, runId) {
|
|
|
1505
1515
|
console.log(`\u2714\uFE0F Saved as ${rootDir}`);
|
|
1506
1516
|
}
|
|
1507
1517
|
|
|
1508
|
-
// src/cli/cmd-link.ts
|
|
1509
|
-
async function cmdLink(slug) {
|
|
1510
|
-
const session2 = await FlakinessSession.load();
|
|
1511
|
-
if (!session2) {
|
|
1512
|
-
console.log(`Please login first`);
|
|
1513
|
-
process.exit(1);
|
|
1514
|
-
}
|
|
1515
|
-
const [orgSlug, projectSlug] = slug.split("/");
|
|
1516
|
-
const project = await session2.api.project.findProject.GET({
|
|
1517
|
-
orgSlug,
|
|
1518
|
-
projectSlug
|
|
1519
|
-
});
|
|
1520
|
-
if (!project) {
|
|
1521
|
-
console.log(`Failed to find project ${slug}`);
|
|
1522
|
-
process.exit(1);
|
|
1523
|
-
}
|
|
1524
|
-
const config = FlakinessConfig.createEmpty();
|
|
1525
|
-
config.setProjectPublicId(project.projectPublicId);
|
|
1526
|
-
await config.save();
|
|
1527
|
-
console.log(`\u2713 Linked to ${session2.endpoint()}/${project.org.orgSlug}/${project.projectSlug}`);
|
|
1528
|
-
}
|
|
1529
|
-
|
|
1530
1518
|
// ../server/lib/common/knownClientIds.js
|
|
1531
1519
|
var KNOWN_CLIENT_IDS = {
|
|
1532
1520
|
OFFICIAL_WEB: "flakiness-io-official-cli",
|
|
@@ -1549,7 +1537,8 @@ async function cmdLogout() {
|
|
|
1549
1537
|
}
|
|
1550
1538
|
|
|
1551
1539
|
// src/cli/cmd-login.ts
|
|
1552
|
-
|
|
1540
|
+
var DEFAULT_FLAKINESS_ENDPOINT = "https://flakiness.io";
|
|
1541
|
+
async function cmdLogin(endpoint = DEFAULT_FLAKINESS_ENDPOINT) {
|
|
1553
1542
|
await cmdLogout();
|
|
1554
1543
|
const api = createServerAPI(endpoint);
|
|
1555
1544
|
const data = await api.deviceauth.createRequest.POST({
|
|
@@ -1563,8 +1552,8 @@ async function cmdLogin(endpoint) {
|
|
|
1563
1552
|
await new Promise((x) => setTimeout(x, 2e3));
|
|
1564
1553
|
const result = await api.deviceauth.getToken.GET({ deviceCode: data.deviceCode }).catch((e) => void 0);
|
|
1565
1554
|
if (!result) {
|
|
1566
|
-
console.
|
|
1567
|
-
|
|
1555
|
+
console.error(`Authorization request was rejected.`);
|
|
1556
|
+
process.exit(1);
|
|
1568
1557
|
}
|
|
1569
1558
|
token = result.token;
|
|
1570
1559
|
if (token)
|
|
@@ -1586,6 +1575,41 @@ async function cmdLogin(endpoint) {
|
|
|
1586
1575
|
const message = e instanceof Error ? e.message : String(e);
|
|
1587
1576
|
console.error(`x Failed to login:`, message);
|
|
1588
1577
|
}
|
|
1578
|
+
return session2;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
// src/cli/cmd-link.ts
|
|
1582
|
+
async function cmdLink(slugOrUrl) {
|
|
1583
|
+
let slug = slugOrUrl;
|
|
1584
|
+
let endpoint = DEFAULT_FLAKINESS_ENDPOINT;
|
|
1585
|
+
if (slugOrUrl.startsWith("http://") || slugOrUrl.startsWith("https://")) {
|
|
1586
|
+
const url = URL.parse(slugOrUrl);
|
|
1587
|
+
if (!url) {
|
|
1588
|
+
console.error(`Invalid URL: ${slugOrUrl}`);
|
|
1589
|
+
process.exit(1);
|
|
1590
|
+
}
|
|
1591
|
+
slug = url.pathname.substring(1);
|
|
1592
|
+
endpoint = url.origin;
|
|
1593
|
+
} else if (slugOrUrl.startsWith("flakiness.io/")) {
|
|
1594
|
+
endpoint = "https://flakiness.io";
|
|
1595
|
+
slug = slugOrUrl.substring("flakiness.io/".length);
|
|
1596
|
+
}
|
|
1597
|
+
let session2 = await FlakinessSession.load();
|
|
1598
|
+
if (!session2 || session2.endpoint() !== endpoint)
|
|
1599
|
+
session2 = await cmdLogin(endpoint);
|
|
1600
|
+
const [orgSlug, projectSlug] = slug.split("/");
|
|
1601
|
+
const project = await session2.api.project.findProject.GET({
|
|
1602
|
+
orgSlug,
|
|
1603
|
+
projectSlug
|
|
1604
|
+
});
|
|
1605
|
+
if (!project) {
|
|
1606
|
+
console.log(`Failed to find project ${slug}`);
|
|
1607
|
+
process.exit(1);
|
|
1608
|
+
}
|
|
1609
|
+
const config = FlakinessConfig.createEmpty();
|
|
1610
|
+
config.setProjectPublicId(project.projectPublicId);
|
|
1611
|
+
await config.save();
|
|
1612
|
+
console.log(`\u2713 Linked to ${session2.endpoint()}/${project.org.orgSlug}/${project.projectSlug}`);
|
|
1589
1613
|
}
|
|
1590
1614
|
|
|
1591
1615
|
// src/cli/cmd-show-report.ts
|
|
@@ -2221,7 +2245,7 @@ async function cmdWhoami() {
|
|
|
2221
2245
|
// src/cli/cli.ts
|
|
2222
2246
|
var session = await FlakinessSession.load();
|
|
2223
2247
|
var optAccessToken = new Option("-t, --access-token <token>", "A read-write flakiness.io access token").env("FLAKINESS_ACCESS_TOKEN");
|
|
2224
|
-
var optEndpoint = new Option("-e, --endpoint <url>", "An endpoint where the service is deployed").default(session?.endpoint() ??
|
|
2248
|
+
var optEndpoint = new Option("-e, --endpoint <url>", "An endpoint where the service is deployed").default(session?.endpoint() ?? DEFAULT_FLAKINESS_ENDPOINT).env("FLAKINESS_ENDPOINT");
|
|
2225
2249
|
var optAttachmentsDir = new Option("--attachments-dir <dir>", "Directory containing attachments to upload. Defaults to the report directory");
|
|
2226
2250
|
async function runCommand(callback) {
|
|
2227
2251
|
try {
|
|
@@ -2260,7 +2284,7 @@ program.command("upload-playwright-json", { hidden: true }).description("Upload
|
|
|
2260
2284
|
await cmdUploadPlaywrightJson(relativePath, await ensureAccessToken(options));
|
|
2261
2285
|
}));
|
|
2262
2286
|
var optLink = new Option("--link <org/proj>", "A project to link to");
|
|
2263
|
-
program.command("login").description("Login to the
|
|
2287
|
+
program.command("login").description("Login to the Flakiness.io service").addOption(optEndpoint).addOption(optLink).action(async (options) => runCommand(async () => {
|
|
2264
2288
|
await cmdLogin(options.endpoint);
|
|
2265
2289
|
if (options.link)
|
|
2266
2290
|
await cmdLink(options.link);
|
|
@@ -2271,11 +2295,11 @@ program.command("logout").description("Logout from current session").action(asyn
|
|
|
2271
2295
|
program.command("whoami").description("Show current logged in user information").action(async () => runCommand(async () => {
|
|
2272
2296
|
await cmdWhoami();
|
|
2273
2297
|
}));
|
|
2274
|
-
program.command("link").description("Link repository to the flakiness project").addOption(optEndpoint).argument("org/project",
|
|
2298
|
+
program.command("link").description("Link repository to the flakiness project").addOption(optEndpoint).argument("flakiness.io/org/project", "A URL of the Flakiness.io project").action(async (slugOrUrl, options) => runCommand(async () => {
|
|
2275
2299
|
const session2 = await FlakinessSession.load();
|
|
2276
2300
|
if (!session2)
|
|
2277
2301
|
await cmdLogin(options.endpoint);
|
|
2278
|
-
await cmdLink(
|
|
2302
|
+
await cmdLink(slugOrUrl);
|
|
2279
2303
|
}));
|
|
2280
2304
|
program.command("unlink").description("Unlink repository from the flakiness project").action(async () => runCommand(async () => {
|
|
2281
2305
|
await cmdUnlink();
|
|
@@ -2333,11 +2357,11 @@ program.command("upload").description("Upload Flakiness report to the flakiness.
|
|
|
2333
2357
|
await cmdUpload(relativePath, await ensureAccessToken(options));
|
|
2334
2358
|
});
|
|
2335
2359
|
});
|
|
2336
|
-
program.command("show
|
|
2360
|
+
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 () => {
|
|
2337
2361
|
const dir = path11.resolve(arg ?? "flakiness-report");
|
|
2338
2362
|
await cmdShowReport(dir);
|
|
2339
2363
|
}));
|
|
2340
|
-
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", "
|
|
2364
|
+
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) => {
|
|
2341
2365
|
await runCommand(async () => {
|
|
2342
2366
|
await cmdConvert(junitPath, options);
|
|
2343
2367
|
});
|
package/lib/cli/cmd-convert.js
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli/cmd-convert.ts
|
|
4
4
|
import fs3 from "fs/promises";
|
|
5
|
-
import
|
|
5
|
+
import path3 from "path";
|
|
6
6
|
|
|
7
7
|
// src/junit.ts
|
|
8
8
|
import { ReportUtils as ReportUtils2 } from "@flakiness/report";
|
|
9
9
|
import { parseXml, XmlElement, XmlText } from "@rgrove/parse-xml";
|
|
10
10
|
import assert2 from "assert";
|
|
11
11
|
import fs2 from "fs";
|
|
12
|
-
import
|
|
12
|
+
import path2 from "path";
|
|
13
13
|
|
|
14
14
|
// src/utils.ts
|
|
15
15
|
import { ReportUtils } from "@flakiness/report";
|
|
@@ -19,6 +19,7 @@ import crypto from "crypto";
|
|
|
19
19
|
import fs from "fs";
|
|
20
20
|
import http from "http";
|
|
21
21
|
import https from "https";
|
|
22
|
+
import path, { posix as posixPath, win32 as win32Path } from "path";
|
|
22
23
|
function sha1File(filePath) {
|
|
23
24
|
return new Promise((resolve, reject) => {
|
|
24
25
|
const hash = crypto.createHash("sha1");
|
|
@@ -122,6 +123,29 @@ var httpUtils;
|
|
|
122
123
|
httpUtils2.postJSON = postJSON;
|
|
123
124
|
})(httpUtils || (httpUtils = {}));
|
|
124
125
|
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");
|
|
126
|
+
async function saveReportAndAttachments(report, attachments, outputFolder) {
|
|
127
|
+
const reportPath = path.join(outputFolder, "report.json");
|
|
128
|
+
const attachmentsFolder = path.join(outputFolder, "attachments");
|
|
129
|
+
await fs.promises.rm(outputFolder, { recursive: true, force: true });
|
|
130
|
+
await fs.promises.mkdir(outputFolder, { recursive: true });
|
|
131
|
+
await fs.promises.writeFile(reportPath, JSON.stringify(report), "utf-8");
|
|
132
|
+
if (attachments.length)
|
|
133
|
+
await fs.promises.mkdir(attachmentsFolder);
|
|
134
|
+
const movedAttachments = [];
|
|
135
|
+
for (const attachment of attachments) {
|
|
136
|
+
const attachmentPath = path.join(attachmentsFolder, attachment.id);
|
|
137
|
+
if (attachment.path)
|
|
138
|
+
await fs.promises.cp(attachment.path, attachmentPath);
|
|
139
|
+
else if (attachment.body)
|
|
140
|
+
await fs.promises.writeFile(attachmentPath, attachment.body);
|
|
141
|
+
movedAttachments.push({
|
|
142
|
+
contentType: attachment.contentType,
|
|
143
|
+
id: attachment.id,
|
|
144
|
+
path: attachmentPath
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
return movedAttachments;
|
|
148
|
+
}
|
|
125
149
|
function shell(command, args, options) {
|
|
126
150
|
try {
|
|
127
151
|
const result = spawnSync(command, args, { encoding: "utf-8", ...options });
|
|
@@ -187,7 +211,7 @@ function extractStdout(testcase, stdio) {
|
|
|
187
211
|
}));
|
|
188
212
|
}
|
|
189
213
|
async function parseAttachment(value) {
|
|
190
|
-
let absolutePath =
|
|
214
|
+
let absolutePath = path2.resolve(process.cwd(), value);
|
|
191
215
|
if (fs2.existsSync(absolutePath)) {
|
|
192
216
|
const id = await sha1File(absolutePath);
|
|
193
217
|
return {
|
|
@@ -260,7 +284,7 @@ async function traverseJUnitReport(context, node) {
|
|
|
260
284
|
id: attachment.id,
|
|
261
285
|
contentType: attachment.contentType,
|
|
262
286
|
//TODO: better default names for attachments?
|
|
263
|
-
name: attachment.path ?
|
|
287
|
+
name: attachment.path ? path2.basename(attachment.path) : `attachment`
|
|
264
288
|
});
|
|
265
289
|
} else {
|
|
266
290
|
annotations.push({
|
|
@@ -335,7 +359,7 @@ async function parseJUnit(xmls, options) {
|
|
|
335
359
|
|
|
336
360
|
// src/cli/cmd-convert.ts
|
|
337
361
|
async function cmdConvert(junitPath, options) {
|
|
338
|
-
const fullPath =
|
|
362
|
+
const fullPath = path3.resolve(junitPath);
|
|
339
363
|
if (!await fs3.access(fullPath, fs3.constants.F_OK).then(() => true).catch(() => false)) {
|
|
340
364
|
console.error(`Error: path ${fullPath} is not accessible`);
|
|
341
365
|
process.exit(1);
|
|
@@ -377,26 +401,13 @@ async function cmdConvert(junitPath, options) {
|
|
|
377
401
|
runStartTimestamp: Date.now(),
|
|
378
402
|
runDuration: 0
|
|
379
403
|
});
|
|
380
|
-
await
|
|
381
|
-
console.log(
|
|
382
|
-
if (attachments.length > 0) {
|
|
383
|
-
await fs3.mkdir("fkattachments", { recursive: true });
|
|
384
|
-
for (const attachment of attachments) {
|
|
385
|
-
if (attachment.path) {
|
|
386
|
-
const destPath = path2.join("fkattachments", attachment.id);
|
|
387
|
-
await fs3.copyFile(attachment.path, destPath);
|
|
388
|
-
} else if (attachment.body) {
|
|
389
|
-
const destPath = path2.join("fkattachments", attachment.id);
|
|
390
|
-
await fs3.writeFile(destPath, attachment.body);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
console.log(`\u2713 Saved ${attachments.length} attachments to fkattachments/`);
|
|
394
|
-
}
|
|
404
|
+
await saveReportAndAttachments(report, attachments, options.outputDir);
|
|
405
|
+
console.log(`\u2713 Saved to ${options.outputDir}`);
|
|
395
406
|
}
|
|
396
407
|
async function findXmlFiles(dir, result = []) {
|
|
397
408
|
const entries = await fs3.readdir(dir, { withFileTypes: true });
|
|
398
409
|
for (const entry of entries) {
|
|
399
|
-
const fullPath =
|
|
410
|
+
const fullPath = path3.join(dir, entry.name);
|
|
400
411
|
if (entry.isFile() && entry.name.toLowerCase().endsWith(".xml"))
|
|
401
412
|
result.push(fullPath);
|
|
402
413
|
else if (entry.isDirectory())
|
package/lib/cli/cmd-link.js
CHANGED
|
@@ -249,13 +249,88 @@ var FlakinessSession = class _FlakinessSession {
|
|
|
249
249
|
}
|
|
250
250
|
};
|
|
251
251
|
|
|
252
|
-
//
|
|
253
|
-
|
|
252
|
+
// ../server/lib/common/knownClientIds.js
|
|
253
|
+
var KNOWN_CLIENT_IDS = {
|
|
254
|
+
OFFICIAL_WEB: "flakiness-io-official-cli",
|
|
255
|
+
OFFICIAL_CLI: "flakiness-io-official-website"
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// src/cli/cmd-login.ts
|
|
259
|
+
import open from "open";
|
|
260
|
+
import os2 from "os";
|
|
261
|
+
|
|
262
|
+
// src/cli/cmd-logout.ts
|
|
263
|
+
async function cmdLogout() {
|
|
254
264
|
const session = await FlakinessSession.load();
|
|
255
|
-
if (!session)
|
|
256
|
-
|
|
265
|
+
if (!session)
|
|
266
|
+
return;
|
|
267
|
+
const currentSession = await session.api.user.currentSession.GET().catch((e) => void 0);
|
|
268
|
+
if (currentSession)
|
|
269
|
+
await session.api.user.logoutSession.POST({ sessionId: currentSession.sessionPublicId });
|
|
270
|
+
await FlakinessSession.remove();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// src/cli/cmd-login.ts
|
|
274
|
+
var DEFAULT_FLAKINESS_ENDPOINT = "https://flakiness.io";
|
|
275
|
+
async function cmdLogin(endpoint = DEFAULT_FLAKINESS_ENDPOINT) {
|
|
276
|
+
await cmdLogout();
|
|
277
|
+
const api = createServerAPI(endpoint);
|
|
278
|
+
const data = await api.deviceauth.createRequest.POST({
|
|
279
|
+
clientId: KNOWN_CLIENT_IDS.OFFICIAL_CLI,
|
|
280
|
+
name: os2.hostname()
|
|
281
|
+
});
|
|
282
|
+
await open(new URL(data.verificationUrl, endpoint).href);
|
|
283
|
+
console.log(`Please navigate to ${new URL(data.verificationUrl, endpoint)}`);
|
|
284
|
+
let token;
|
|
285
|
+
while (Date.now() < data.deadline) {
|
|
286
|
+
await new Promise((x) => setTimeout(x, 2e3));
|
|
287
|
+
const result = await api.deviceauth.getToken.GET({ deviceCode: data.deviceCode }).catch((e) => void 0);
|
|
288
|
+
if (!result) {
|
|
289
|
+
console.error(`Authorization request was rejected.`);
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
token = result.token;
|
|
293
|
+
if (token)
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
if (!token) {
|
|
297
|
+
console.log(`Failed to login.`);
|
|
257
298
|
process.exit(1);
|
|
258
299
|
}
|
|
300
|
+
const session = new FlakinessSession({
|
|
301
|
+
endpoint,
|
|
302
|
+
token
|
|
303
|
+
});
|
|
304
|
+
try {
|
|
305
|
+
const user = await session.api.user.whoami.GET();
|
|
306
|
+
await session.save();
|
|
307
|
+
console.log(`\u2713 Logged in as ${user.userName} (${user.userLogin})`);
|
|
308
|
+
} catch (e) {
|
|
309
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
310
|
+
console.error(`x Failed to login:`, message);
|
|
311
|
+
}
|
|
312
|
+
return session;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// src/cli/cmd-link.ts
|
|
316
|
+
async function cmdLink(slugOrUrl) {
|
|
317
|
+
let slug = slugOrUrl;
|
|
318
|
+
let endpoint = DEFAULT_FLAKINESS_ENDPOINT;
|
|
319
|
+
if (slugOrUrl.startsWith("http://") || slugOrUrl.startsWith("https://")) {
|
|
320
|
+
const url = URL.parse(slugOrUrl);
|
|
321
|
+
if (!url) {
|
|
322
|
+
console.error(`Invalid URL: ${slugOrUrl}`);
|
|
323
|
+
process.exit(1);
|
|
324
|
+
}
|
|
325
|
+
slug = url.pathname.substring(1);
|
|
326
|
+
endpoint = url.origin;
|
|
327
|
+
} else if (slugOrUrl.startsWith("flakiness.io/")) {
|
|
328
|
+
endpoint = "https://flakiness.io";
|
|
329
|
+
slug = slugOrUrl.substring("flakiness.io/".length);
|
|
330
|
+
}
|
|
331
|
+
let session = await FlakinessSession.load();
|
|
332
|
+
if (!session || session.endpoint() !== endpoint)
|
|
333
|
+
session = await cmdLogin(endpoint);
|
|
259
334
|
const [orgSlug, projectSlug] = slug.split("/");
|
|
260
335
|
const project = await session.api.project.findProject.GET({
|
|
261
336
|
orgSlug,
|
package/lib/cli/cmd-login.js
CHANGED
|
@@ -176,7 +176,8 @@ async function cmdLogout() {
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
// src/cli/cmd-login.ts
|
|
179
|
-
|
|
179
|
+
var DEFAULT_FLAKINESS_ENDPOINT = "https://flakiness.io";
|
|
180
|
+
async function cmdLogin(endpoint = DEFAULT_FLAKINESS_ENDPOINT) {
|
|
180
181
|
await cmdLogout();
|
|
181
182
|
const api = createServerAPI(endpoint);
|
|
182
183
|
const data = await api.deviceauth.createRequest.POST({
|
|
@@ -190,8 +191,8 @@ async function cmdLogin(endpoint) {
|
|
|
190
191
|
await new Promise((x) => setTimeout(x, 2e3));
|
|
191
192
|
const result = await api.deviceauth.getToken.GET({ deviceCode: data.deviceCode }).catch((e) => void 0);
|
|
192
193
|
if (!result) {
|
|
193
|
-
console.
|
|
194
|
-
|
|
194
|
+
console.error(`Authorization request was rejected.`);
|
|
195
|
+
process.exit(1);
|
|
195
196
|
}
|
|
196
197
|
token = result.token;
|
|
197
198
|
if (token)
|
|
@@ -213,8 +214,10 @@ async function cmdLogin(endpoint) {
|
|
|
213
214
|
const message = e instanceof Error ? e.message : String(e);
|
|
214
215
|
console.error(`x Failed to login:`, message);
|
|
215
216
|
}
|
|
217
|
+
return session;
|
|
216
218
|
}
|
|
217
219
|
export {
|
|
220
|
+
DEFAULT_FLAKINESS_ENDPOINT,
|
|
218
221
|
cmdLogin
|
|
219
222
|
};
|
|
220
223
|
//# sourceMappingURL=cmd-login.js.map
|
package/lib/playwright-test.js
CHANGED
|
@@ -139,6 +139,29 @@ var ansiRegex = new RegExp("[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:
|
|
|
139
139
|
function stripAnsi(str) {
|
|
140
140
|
return str.replace(ansiRegex, "");
|
|
141
141
|
}
|
|
142
|
+
async function saveReportAndAttachments(report, attachments, outputFolder) {
|
|
143
|
+
const reportPath = path.join(outputFolder, "report.json");
|
|
144
|
+
const attachmentsFolder = path.join(outputFolder, "attachments");
|
|
145
|
+
await fs.promises.rm(outputFolder, { recursive: true, force: true });
|
|
146
|
+
await fs.promises.mkdir(outputFolder, { recursive: true });
|
|
147
|
+
await fs.promises.writeFile(reportPath, JSON.stringify(report), "utf-8");
|
|
148
|
+
if (attachments.length)
|
|
149
|
+
await fs.promises.mkdir(attachmentsFolder);
|
|
150
|
+
const movedAttachments = [];
|
|
151
|
+
for (const attachment of attachments) {
|
|
152
|
+
const attachmentPath = path.join(attachmentsFolder, attachment.id);
|
|
153
|
+
if (attachment.path)
|
|
154
|
+
await fs.promises.cp(attachment.path, attachmentPath);
|
|
155
|
+
else if (attachment.body)
|
|
156
|
+
await fs.promises.writeFile(attachmentPath, attachment.body);
|
|
157
|
+
movedAttachments.push({
|
|
158
|
+
contentType: attachment.contentType,
|
|
159
|
+
id: attachment.id,
|
|
160
|
+
path: attachmentPath
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return movedAttachments;
|
|
164
|
+
}
|
|
142
165
|
function shell(command, args, options) {
|
|
143
166
|
try {
|
|
144
167
|
const result = spawnSync(command, args, { encoding: "utf-8", ...options });
|
|
@@ -1072,25 +1095,7 @@ var FlakinessReporter = class {
|
|
|
1072
1095
|
for (const unaccessibleAttachment of context.unaccessibleAttachmentPaths)
|
|
1073
1096
|
warn(`cannot access attachment ${unaccessibleAttachment}`);
|
|
1074
1097
|
this._report = report;
|
|
1075
|
-
|
|
1076
|
-
const attachmentsFolder = path6.join(this._outputFolder, "attachments");
|
|
1077
|
-
await fs7.promises.rm(this._outputFolder, { recursive: true, force: true });
|
|
1078
|
-
await fs7.promises.mkdir(this._outputFolder, { recursive: true });
|
|
1079
|
-
await fs7.promises.writeFile(reportPath, JSON.stringify(report), "utf-8");
|
|
1080
|
-
if (context.attachments.size)
|
|
1081
|
-
await fs7.promises.mkdir(attachmentsFolder);
|
|
1082
|
-
for (const attachment of context.attachments.values()) {
|
|
1083
|
-
const attachmentPath = path6.join(attachmentsFolder, attachment.id);
|
|
1084
|
-
if (attachment.path)
|
|
1085
|
-
await fs7.promises.cp(attachment.path, attachmentPath);
|
|
1086
|
-
else if (attachment.body)
|
|
1087
|
-
await fs7.promises.writeFile(attachmentPath, attachment.body);
|
|
1088
|
-
this._attachments.push({
|
|
1089
|
-
contentType: attachment.contentType,
|
|
1090
|
-
id: attachment.id,
|
|
1091
|
-
path: attachmentPath
|
|
1092
|
-
});
|
|
1093
|
-
}
|
|
1098
|
+
this._attachments = await saveReportAndAttachments(report, Array.from(context.attachments.values()), this._outputFolder);
|
|
1094
1099
|
this._result = result;
|
|
1095
1100
|
}
|
|
1096
1101
|
async onExit() {
|
|
@@ -1111,9 +1116,9 @@ var FlakinessReporter = class {
|
|
|
1111
1116
|
const defaultOutputFolder = path6.join(process.cwd(), "flakiness-report");
|
|
1112
1117
|
const folder = defaultOutputFolder === this._outputFolder ? "" : path6.relative(process.cwd(), this._outputFolder);
|
|
1113
1118
|
console.log(`
|
|
1114
|
-
To open last Flakiness report run:
|
|
1119
|
+
To open last Flakiness report, install Flakiness CLI tool and run:
|
|
1115
1120
|
|
|
1116
|
-
${chalk2.cyan(`
|
|
1121
|
+
${chalk2.cyan(`flakiness show ${folder}`)}
|
|
1117
1122
|
`);
|
|
1118
1123
|
}
|
|
1119
1124
|
}
|
package/lib/utils.js
CHANGED
|
@@ -123,6 +123,29 @@ var ansiRegex = new RegExp("[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:
|
|
|
123
123
|
function stripAnsi(str) {
|
|
124
124
|
return str.replace(ansiRegex, "");
|
|
125
125
|
}
|
|
126
|
+
async function saveReportAndAttachments(report, attachments, outputFolder) {
|
|
127
|
+
const reportPath = path.join(outputFolder, "report.json");
|
|
128
|
+
const attachmentsFolder = path.join(outputFolder, "attachments");
|
|
129
|
+
await fs.promises.rm(outputFolder, { recursive: true, force: true });
|
|
130
|
+
await fs.promises.mkdir(outputFolder, { recursive: true });
|
|
131
|
+
await fs.promises.writeFile(reportPath, JSON.stringify(report), "utf-8");
|
|
132
|
+
if (attachments.length)
|
|
133
|
+
await fs.promises.mkdir(attachmentsFolder);
|
|
134
|
+
const movedAttachments = [];
|
|
135
|
+
for (const attachment of attachments) {
|
|
136
|
+
const attachmentPath = path.join(attachmentsFolder, attachment.id);
|
|
137
|
+
if (attachment.path)
|
|
138
|
+
await fs.promises.cp(attachment.path, attachmentPath);
|
|
139
|
+
else if (attachment.body)
|
|
140
|
+
await fs.promises.writeFile(attachmentPath, attachment.body);
|
|
141
|
+
movedAttachments.push({
|
|
142
|
+
contentType: attachment.contentType,
|
|
143
|
+
id: attachment.id,
|
|
144
|
+
path: attachmentPath
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
return movedAttachments;
|
|
148
|
+
}
|
|
126
149
|
function shell(command, args, options) {
|
|
127
150
|
try {
|
|
128
151
|
const result = spawnSync(command, args, { encoding: "utf-8", ...options });
|
|
@@ -342,6 +365,7 @@ export {
|
|
|
342
365
|
parseStringDate,
|
|
343
366
|
resolveAttachmentPaths,
|
|
344
367
|
retryWithBackoff,
|
|
368
|
+
saveReportAndAttachments,
|
|
345
369
|
sha1Buffer,
|
|
346
370
|
sha1File,
|
|
347
371
|
shell,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flakiness/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.131.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"bin": {
|
|
6
6
|
"flakiness": "./lib/cli/cli.js"
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"author": "Degu Labs, Inc",
|
|
51
51
|
"license": "Fair Source 100",
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@flakiness/server": "0.
|
|
53
|
+
"@flakiness/server": "0.131.0",
|
|
54
54
|
"@playwright/test": "^1.54.0",
|
|
55
55
|
"@types/babel__code-frame": "^7.0.6",
|
|
56
56
|
"@types/compression": "^1.8.1",
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"@babel/code-frame": "^7.26.2",
|
|
61
|
-
"@flakiness/report": "0.
|
|
62
|
-
"@flakiness/shared": "0.
|
|
61
|
+
"@flakiness/report": "0.131.0",
|
|
62
|
+
"@flakiness/shared": "0.131.0",
|
|
63
63
|
"@rgrove/parse-xml": "^4.2.0",
|
|
64
64
|
"body-parser": "^1.20.3",
|
|
65
65
|
"chalk": "^5.6.2",
|