@bigbinary/neeto-playwright-reporter 1.0.1 → 1.1.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/index.cjs.js +188 -33
- package/index.cjs.js.map +1 -1
- package/index.d.ts +15 -3
- package/index.js +186 -32
- package/index.js.map +1 -1
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
|
-
import { Reporter, FullConfig, Suite } from '@playwright/test/reporter';
|
|
1
|
+
import { Reporter, FullConfig, Suite, TestCase, TestResult, FullResult } from '@playwright/test/reporter';
|
|
2
2
|
|
|
3
|
+
interface ReporterOptionParams {
|
|
4
|
+
projectKey: string;
|
|
5
|
+
baseURL: string;
|
|
6
|
+
ciBuildId: string;
|
|
7
|
+
}
|
|
3
8
|
declare class MyReporter implements Reporter {
|
|
4
|
-
|
|
5
|
-
|
|
9
|
+
attempts: Record<string, string>;
|
|
10
|
+
ciBuildId: string;
|
|
11
|
+
config: FullConfig | undefined;
|
|
12
|
+
currentShard: number | undefined;
|
|
13
|
+
constructor(options: ReporterOptionParams);
|
|
14
|
+
onBegin: (config: FullConfig, rootSuite: Suite) => Promise<void>;
|
|
15
|
+
onTestBegin: ({ id, title }: TestCase, { retry }: TestResult) => Promise<void>;
|
|
16
|
+
onTestEnd: ({ id, title }: TestCase, { status, duration, errors, retry, attachments }: TestResult) => Promise<void>;
|
|
17
|
+
onEnd: ({ status, duration }: FullResult) => Promise<void>;
|
|
6
18
|
}
|
|
7
19
|
|
|
8
20
|
export { MyReporter as default };
|
package/index.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
+
import require$$6 from 'fs';
|
|
1
2
|
import require$$1, { TextEncoder } from 'util';
|
|
2
3
|
import stream, { Readable } from 'stream';
|
|
3
4
|
import require$$1$1 from 'path';
|
|
4
5
|
import require$$3 from 'http';
|
|
5
6
|
import require$$4 from 'https';
|
|
6
7
|
import require$$0$1 from 'url';
|
|
7
|
-
import require$$6 from 'fs';
|
|
8
8
|
import require$$4$1 from 'assert';
|
|
9
9
|
import require$$1$2 from 'tty';
|
|
10
10
|
import require$$0$2 from 'os';
|
|
11
11
|
import zlib from 'zlib';
|
|
12
12
|
import EventEmitter from 'events';
|
|
13
|
+
import childProcess from 'child_process';
|
|
13
14
|
|
|
14
15
|
function bind(fn, thisArg) {
|
|
15
16
|
return function wrap() {
|
|
@@ -18429,54 +18430,207 @@ axios.HttpStatusCode = HttpStatusCode;
|
|
|
18429
18430
|
|
|
18430
18431
|
axios.default = axios;
|
|
18431
18432
|
|
|
18432
|
-
// dummy endpoint until neeto-playwright-reporter dashboard is up
|
|
18433
|
-
const create = (payload) => axios.post(`https://webhook.site/1bff83d0-03a0-412f-a70a-2f11f3209534`, payload);
|
|
18434
|
-
const dummyApi = { create };
|
|
18435
|
-
|
|
18436
|
-
const getDescribePath = ({ titlePath, title, project, spec, }) => {
|
|
18437
|
-
const describePaths = titlePath.filter((item, index) => index !== 0 && item !== title && item !== project && item !== spec);
|
|
18438
|
-
return describePaths.join(" > ");
|
|
18439
|
-
};
|
|
18440
|
-
|
|
18441
18433
|
const HEADERS_KEYS = {
|
|
18442
18434
|
applicationKey: "Application-Key",
|
|
18443
|
-
xCsrfToken: "X-CSRF-TOKEN",
|
|
18444
18435
|
contentType: "Content-Type",
|
|
18445
18436
|
accept: "Accept",
|
|
18437
|
+
apiKey: "X-Api-Key",
|
|
18438
|
+
projectKey: "Project-Key",
|
|
18439
|
+
};
|
|
18440
|
+
const API_BASE_URL = "/api/v1";
|
|
18441
|
+
|
|
18442
|
+
const create$2 = (ciBuildId, history_id, payload) => axios.post(`${API_BASE_URL}/reporter/runs/${ciBuildId}/test_entities/${history_id}/attempts`, payload, { headers: { "Content-Type": "multipart/form-data" } });
|
|
18443
|
+
const update$1 = (ciBuildId, history_id, id, payload) => axios.put(`${API_BASE_URL}/reporter/runs/${ciBuildId}/test_entities/${history_id}/attempts/${id}`, payload, { headers: { "Content-Type": "multipart/form-data" } });
|
|
18444
|
+
const attemptsApi = { create: create$2, update: update$1 };
|
|
18445
|
+
|
|
18446
|
+
const ERRORS = {
|
|
18447
|
+
onBegin: {
|
|
18448
|
+
failedToGetCommitSha: "Failed to get current commit SHA.",
|
|
18449
|
+
failedToGetCommitMessage: "Failed to get current commit message.",
|
|
18450
|
+
failedToInitializeRun: "Failed to initialize run in reporter",
|
|
18451
|
+
},
|
|
18452
|
+
onTestBegin: {
|
|
18453
|
+
failedToReportTest: (testTitle, historyId) => `Failed to report test "${testTitle}" with history ID ${historyId}`,
|
|
18454
|
+
},
|
|
18455
|
+
onEnd: {
|
|
18456
|
+
failedToReportRunStatus: "Failed to report run status",
|
|
18457
|
+
},
|
|
18446
18458
|
};
|
|
18447
18459
|
|
|
18448
|
-
const
|
|
18460
|
+
const MESSAGES = {
|
|
18461
|
+
onBegin: {
|
|
18462
|
+
testStarted: "Test has started reporting to neetoPlaywrightReporter",
|
|
18463
|
+
ciBuildId: (currentCiBuildId) => `CI BUILD ID: ${currentCiBuildId}`,
|
|
18464
|
+
totalShards: (totalShards) => `Total shards: ${totalShards}`,
|
|
18465
|
+
currentShard: (currentShard) => `Current shard: ${currentShard}`,
|
|
18466
|
+
},
|
|
18467
|
+
};
|
|
18468
|
+
|
|
18469
|
+
const consoleLogFormatted = {
|
|
18470
|
+
bold: (message) => console.log(console.log("\x1b[1m", message, "\x1b[0m")),
|
|
18471
|
+
dim: (message) => console.log(console.log("\x1b[2m", message, "\x1b[0m")),
|
|
18472
|
+
underline: (message) => console.log(console.log("\x1b[4m", message, "\x1b[0m")),
|
|
18473
|
+
invertBackground: (message) => console.log(console.log("\x1b[7m", message, "\x1b[0m")),
|
|
18474
|
+
hidden: (message) => console.log(console.log("\x1b[8m", message, "\x1b[0m")),
|
|
18475
|
+
error: (message) => console.log("\u001b[31m", "\x1b[1m", message, "\x1b[0m", "\u001b[0m"),
|
|
18476
|
+
warning: (message) => console.log("\u001b[33m", "\x1b[1m", message, "\x1b[0m", "\u001b[0m"),
|
|
18477
|
+
};
|
|
18478
|
+
const executeCommandLine = ({ command, messageOnError, shouldThrowError = false, logLevel = "warning", }) => {
|
|
18479
|
+
try {
|
|
18480
|
+
return childProcess.execSync(command).toString().trim();
|
|
18481
|
+
}
|
|
18482
|
+
catch (err) {
|
|
18483
|
+
if (shouldThrowError) {
|
|
18484
|
+
throw err;
|
|
18485
|
+
}
|
|
18486
|
+
else {
|
|
18487
|
+
consoleLogFormatted[logLevel](messageOnError);
|
|
18488
|
+
}
|
|
18489
|
+
}
|
|
18490
|
+
};
|
|
18491
|
+
|
|
18492
|
+
const createShardObject = ({ currentShard = 0, status = "running", duration = null, }) => ({
|
|
18493
|
+
[currentShard]: { status, duration },
|
|
18494
|
+
});
|
|
18495
|
+
const convertBufferToBlob = (buffer, contentType) => new Blob([buffer], { type: contentType });
|
|
18496
|
+
|
|
18497
|
+
const getDescribePath = ({ titlePath, title, project, spec, }) => titlePath.filter((item, index) => index !== 0 && item !== title && item !== project && item !== spec);
|
|
18498
|
+
const getCurrentCommitSha = () => executeCommandLine({
|
|
18499
|
+
command: "git rev-parse HEAD",
|
|
18500
|
+
messageOnError: ERRORS.onBegin.failedToGetCommitSha,
|
|
18501
|
+
});
|
|
18502
|
+
const getCurrentCommitMessage = () => executeCommandLine({
|
|
18503
|
+
command: "git show-branch --no-name HEAD",
|
|
18504
|
+
messageOnError: ERRORS.onBegin.failedToGetCommitMessage,
|
|
18505
|
+
});
|
|
18506
|
+
const getInitializerData = ({ rootDir }, rootSuite) => rootSuite.allTests().map(test => {
|
|
18507
|
+
var _a;
|
|
18508
|
+
const { title, parent, id: history_id, location: { file }, } = test;
|
|
18509
|
+
const titlePath = test.titlePath();
|
|
18510
|
+
const project = (_a = parent.project()) === null || _a === void 0 ? void 0 : _a.name;
|
|
18511
|
+
const spec = file.replace(`${rootDir}/`, "");
|
|
18512
|
+
const describe = getDescribePath({ titlePath, title, spec, project });
|
|
18513
|
+
return { title, describe, project, spec, history_id };
|
|
18514
|
+
});
|
|
18515
|
+
|
|
18516
|
+
const setAuthHeaders = (projectKey) => {
|
|
18517
|
+
var _a;
|
|
18449
18518
|
axios.defaults.headers = {
|
|
18450
18519
|
...axios.defaults.headers,
|
|
18451
|
-
[HEADERS_KEYS.
|
|
18520
|
+
[HEADERS_KEYS.projectKey]: projectKey,
|
|
18521
|
+
[HEADERS_KEYS.apiKey]: (_a = process.env.API_KEY) !== null && _a !== void 0 ? _a : "",
|
|
18452
18522
|
[HEADERS_KEYS.accept]: "application/json",
|
|
18453
18523
|
[HEADERS_KEYS.contentType]: "application/json",
|
|
18454
18524
|
};
|
|
18455
18525
|
};
|
|
18456
|
-
function initializeAxios(
|
|
18457
|
-
|
|
18526
|
+
function initializeAxios({ projectKey, baseURL, }) {
|
|
18527
|
+
axios.defaults.baseURL = baseURL;
|
|
18528
|
+
setAuthHeaders(projectKey);
|
|
18458
18529
|
}
|
|
18459
18530
|
|
|
18531
|
+
const create$1 = (payload) => axios.post(`${API_BASE_URL}/reporter/runs`, {
|
|
18532
|
+
run: payload,
|
|
18533
|
+
});
|
|
18534
|
+
const update = (ciBuildId, payload) => axios.put(`${API_BASE_URL}/reporter/runs/${ciBuildId}`, {
|
|
18535
|
+
run: payload,
|
|
18536
|
+
});
|
|
18537
|
+
const runsApi = { create: create$1, update };
|
|
18538
|
+
|
|
18539
|
+
const create = (ciBuildId, payload) => axios.post(`${API_BASE_URL}/reporter/runs/${ciBuildId}/test_entities`, {
|
|
18540
|
+
test_entity: payload,
|
|
18541
|
+
});
|
|
18542
|
+
const testEntitiesApi = { create };
|
|
18543
|
+
|
|
18460
18544
|
class MyReporter {
|
|
18461
18545
|
constructor(options) {
|
|
18462
|
-
this.onBegin = async (config,
|
|
18463
|
-
|
|
18464
|
-
|
|
18465
|
-
|
|
18466
|
-
|
|
18467
|
-
|
|
18468
|
-
|
|
18469
|
-
|
|
18470
|
-
|
|
18471
|
-
|
|
18472
|
-
|
|
18473
|
-
|
|
18474
|
-
|
|
18475
|
-
|
|
18476
|
-
|
|
18477
|
-
|
|
18546
|
+
this.onBegin = async (config, rootSuite) => {
|
|
18547
|
+
const shard = config.shard;
|
|
18548
|
+
let attempts;
|
|
18549
|
+
try {
|
|
18550
|
+
const runDetails = {
|
|
18551
|
+
commit_id: getCurrentCommitSha(),
|
|
18552
|
+
commit_name: getCurrentCommitMessage(),
|
|
18553
|
+
ci_build_id: this.ciBuildId,
|
|
18554
|
+
configuration: config,
|
|
18555
|
+
shards: createShardObject({ currentShard: shard === null || shard === void 0 ? void 0 : shard.current }),
|
|
18556
|
+
};
|
|
18557
|
+
await runsApi.create(runDetails);
|
|
18558
|
+
({ data: attempts } = await testEntitiesApi.create(this.ciBuildId, {
|
|
18559
|
+
test_entities: getInitializerData(config, rootSuite),
|
|
18560
|
+
}));
|
|
18561
|
+
}
|
|
18562
|
+
catch (error) {
|
|
18563
|
+
consoleLogFormatted.error(error);
|
|
18564
|
+
throw new Error(ERRORS.onBegin.failedToInitializeRun);
|
|
18565
|
+
}
|
|
18566
|
+
consoleLogFormatted.underline(MESSAGES.onBegin.testStarted);
|
|
18567
|
+
consoleLogFormatted.dim(MESSAGES.onBegin.ciBuildId(this.ciBuildId));
|
|
18568
|
+
if (shard) {
|
|
18569
|
+
consoleLogFormatted.dim(MESSAGES.onBegin.totalShards(shard.total));
|
|
18570
|
+
consoleLogFormatted.dim(MESSAGES.onBegin.currentShard(shard.current));
|
|
18571
|
+
}
|
|
18572
|
+
this.attempts = attempts;
|
|
18573
|
+
this.config = config;
|
|
18574
|
+
this.currentShard = shard === null || shard === void 0 ? void 0 : shard.current;
|
|
18575
|
+
};
|
|
18576
|
+
this.onTestBegin = async ({ id, title }, { retry }) => {
|
|
18577
|
+
try {
|
|
18578
|
+
const formData = new FormData();
|
|
18579
|
+
formData.append("attempt[status]", "running");
|
|
18580
|
+
retry === 0 &&
|
|
18581
|
+
(await attemptsApi.update(this.ciBuildId, id, this.attempts[id], formData));
|
|
18582
|
+
}
|
|
18583
|
+
catch (error) {
|
|
18584
|
+
consoleLogFormatted.error(ERRORS.onTestBegin.failedToReportTest(title, id));
|
|
18585
|
+
console.log(error);
|
|
18586
|
+
}
|
|
18587
|
+
};
|
|
18588
|
+
this.onTestEnd = async ({ id, title }, { status, duration, errors, retry, attachments }) => {
|
|
18589
|
+
try {
|
|
18590
|
+
const testResult = {
|
|
18591
|
+
status,
|
|
18592
|
+
duration,
|
|
18593
|
+
log: errors.map(error => { var _a; return (_a = error.message) !== null && _a !== void 0 ? _a : ""; }).join("\n"),
|
|
18594
|
+
};
|
|
18595
|
+
consoleLogFormatted.underline(title);
|
|
18596
|
+
const formData = new FormData();
|
|
18597
|
+
attachments.map(({ name, path, body, contentType }) => {
|
|
18598
|
+
consoleLogFormatted.dim(`${name}: ${path}`);
|
|
18599
|
+
if (["screenshot", "video", "trace"].includes(name)) {
|
|
18600
|
+
const buffer = path ? require$$6.readFileSync(path) : body;
|
|
18601
|
+
formData.append(`attempt[${name}s][]`, convertBufferToBlob(buffer, contentType));
|
|
18602
|
+
}
|
|
18603
|
+
});
|
|
18604
|
+
Object.entries(testResult).map(([resultKey, resultValue]) => {
|
|
18605
|
+
formData.append(`attempt[${resultKey}]`, resultValue.toString());
|
|
18606
|
+
});
|
|
18607
|
+
retry === 0
|
|
18608
|
+
? await attemptsApi.update(this.ciBuildId, id, this.attempts[id], formData)
|
|
18609
|
+
: await attemptsApi.create(this.ciBuildId, id, formData);
|
|
18610
|
+
}
|
|
18611
|
+
catch (error) {
|
|
18612
|
+
consoleLogFormatted.error(ERRORS.onTestBegin.failedToReportTest(title, id));
|
|
18613
|
+
console.log(error);
|
|
18614
|
+
}
|
|
18615
|
+
};
|
|
18616
|
+
this.onEnd = async ({ status, duration }) => {
|
|
18617
|
+
try {
|
|
18618
|
+
await runsApi.update(this.ciBuildId, {
|
|
18619
|
+
shards: createShardObject({
|
|
18620
|
+
currentShard: this.currentShard,
|
|
18621
|
+
status,
|
|
18622
|
+
duration,
|
|
18623
|
+
}),
|
|
18624
|
+
});
|
|
18625
|
+
}
|
|
18626
|
+
catch (error) {
|
|
18627
|
+
console.log(error);
|
|
18628
|
+
throw new Error(ERRORS.onEnd.failedToReportRunStatus);
|
|
18629
|
+
}
|
|
18478
18630
|
};
|
|
18479
|
-
initializeAxios(options
|
|
18631
|
+
initializeAxios(options);
|
|
18632
|
+
this.attempts = {};
|
|
18633
|
+
this.ciBuildId = options.ciBuildId;
|
|
18480
18634
|
}
|
|
18481
18635
|
}
|
|
18482
18636
|
|