@bigbinary/neeto-playwright-reporter 1.3.24 → 1.4.1

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 CHANGED
@@ -1,38 +1,136 @@
1
1
  'use strict';
2
2
 
3
- var require$$6 = require('fs');
3
+ var crypto = require('crypto');
4
+ var neetoCist = require('@bigbinary/neeto-cist');
4
5
  var require$$1 = require('util');
5
6
  var stream = require('stream');
6
7
  var require$$1$1 = require('path');
7
8
  var require$$3 = require('http');
8
9
  var require$$4 = require('https');
9
10
  var require$$0$1 = require('url');
11
+ var require$$6 = require('fs');
10
12
  var require$$4$1 = require('assert');
11
13
  var require$$1$2 = require('tty');
12
14
  var require$$0$2 = require('os');
13
15
  var zlib = require('zlib');
14
16
  var EventEmitter = require('events');
15
- var crypto = require('crypto');
16
17
  var ramda = require('ramda');
17
18
  var childProcess = require('child_process');
18
19
 
19
20
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
20
21
 
21
- var require$$6__default = /*#__PURE__*/_interopDefaultLegacy(require$$6);
22
+ var crypto__default = /*#__PURE__*/_interopDefaultLegacy(crypto);
22
23
  var require$$1__default = /*#__PURE__*/_interopDefaultLegacy(require$$1);
23
24
  var stream__default = /*#__PURE__*/_interopDefaultLegacy(stream);
24
25
  var require$$1__default$1 = /*#__PURE__*/_interopDefaultLegacy(require$$1$1);
25
26
  var require$$3__default = /*#__PURE__*/_interopDefaultLegacy(require$$3);
26
27
  var require$$4__default = /*#__PURE__*/_interopDefaultLegacy(require$$4);
27
28
  var require$$0__default = /*#__PURE__*/_interopDefaultLegacy(require$$0$1);
29
+ var require$$6__default = /*#__PURE__*/_interopDefaultLegacy(require$$6);
28
30
  var require$$4__default$1 = /*#__PURE__*/_interopDefaultLegacy(require$$4$1);
29
31
  var require$$1__default$2 = /*#__PURE__*/_interopDefaultLegacy(require$$1$2);
30
32
  var require$$0__default$1 = /*#__PURE__*/_interopDefaultLegacy(require$$0$2);
31
33
  var zlib__default = /*#__PURE__*/_interopDefaultLegacy(zlib);
32
34
  var EventEmitter__default = /*#__PURE__*/_interopDefaultLegacy(EventEmitter);
33
- var crypto__default = /*#__PURE__*/_interopDefaultLegacy(crypto);
34
35
  var childProcess__default = /*#__PURE__*/_interopDefaultLegacy(childProcess);
35
36
 
37
+ const ERRORS = {
38
+ onBegin: {
39
+ noTestsToReport: "No tests to report",
40
+ failedToGetCommitSha: "Failed to get current commit SHA.",
41
+ failedToGetAuthor: "Failed to get current author.",
42
+ failedToGetBranch: "Failed to get current branch.",
43
+ failedToGetCommitMessage: "Failed to get current commit message.",
44
+ failedToInitializeRun: "Failed to initialize run in reporter",
45
+ },
46
+ onTestBegin: {
47
+ failedToReportTest: (testTitle, historyId) => `Failed to report test "${testTitle}" with history ID ${historyId}`,
48
+ },
49
+ onEnd: {
50
+ failedToReportRunStatus: "Failed to report run status",
51
+ },
52
+ onTestEnd: {
53
+ attemptAlreadyReported: "Attempt was already reported",
54
+ },
55
+ heartbeat: {
56
+ stopped: "Run was stopped at the reporter",
57
+ },
58
+ common: {
59
+ timeoutExceeded: "Timed out waiting for condition.",
60
+ }
61
+ };
62
+
63
+ const LOG_LEVEL_FORMATTERS = {
64
+ bold: "\x1b[1m",
65
+ dim: "\x1b[2m",
66
+ underline: "\x1b[4m",
67
+ invertBackground: "\x1b[7m",
68
+ hidden: "\x1b[8m",
69
+ error: "\u001b[31m\x1b[1m",
70
+ redText: "\u001b[31m\x1b[1m", // Same ANSI style as error but in log level printing important errors
71
+ warning: "\u001b[33m\x1b[1m",
72
+ };
73
+
74
+ class ConsoleLogFormatted {
75
+ constructor() {
76
+ this.LOG_LEVELS = {
77
+ info: ["bold", "dim", "underline", "invertBackground", "hidden", "redText"],
78
+ get warning() {
79
+ return [...this.info, "warning"];
80
+ },
81
+ get error() {
82
+ return [...this.warning, "error"];
83
+ },
84
+ };
85
+ this.shouldPrintMessage = (type) => {
86
+ var _a;
87
+ const logLevel = ((_a = process.env.PLAYDASH_LOG_LEVEL) !== null && _a !== void 0 ? _a : "info");
88
+ const validMethods = this.LOG_LEVELS[logLevel] || [];
89
+ return validMethods.includes(type);
90
+ };
91
+ Object.entries(LOG_LEVEL_FORMATTERS).forEach(([logLevel, ansiFormatter]) => {
92
+ this.createMethod(logLevel, ansiFormatter);
93
+ });
94
+ }
95
+ createMethod(logLevel, ansiFormatter) {
96
+ this[logLevel] = (message) => {
97
+ if (this.shouldPrintMessage(logLevel)) {
98
+ console.log(`${ansiFormatter}${message}\x1b[0m\u001b[0m`);
99
+ }
100
+ };
101
+ }
102
+ }
103
+ var consoleLogFormatted = new ConsoleLogFormatted();
104
+
105
+ const createShardObject = ({ currentShard = 0, status = "running", duration = null, }) => ({
106
+ [currentShard]: { status, duration },
107
+ });
108
+ const waitUntilTimeout = (timeout) => new Promise(resolve => setTimeout(resolve, timeout));
109
+ const waitUntilCondition = async (condition, timeout = 100 * 1000 // Default timeout after 100 seconds
110
+ ) => {
111
+ const startTime = new Date();
112
+ const timeoutTime = new Date(startTime.getTime() + timeout);
113
+ while (!condition()) {
114
+ if (new Date().getTime() >= timeoutTime.getTime())
115
+ return consoleLogFormatted.redText(ERRORS.common.timeoutExceeded);
116
+ await waitUntilTimeout(100); // Poll every 100 milliseconds until timeout
117
+ }
118
+ };
119
+ const convertToSha1 = (text) => crypto__default["default"].createHash("sha1").update(text).digest("hex").slice(0, 20);
120
+ const generateHistoryId = (projectId, specName, describeTitles, testTitle) => {
121
+ const titles = [...describeTitles, testTitle];
122
+ const targetString = `[project=${projectId}]${specName}\x1e${titles.join("\x1e")}`;
123
+ const targetStringSha = convertToSha1(targetString);
124
+ const pathNameSha = convertToSha1(specName);
125
+ return `${pathNameSha}-${targetStringSha}`;
126
+ };
127
+ const joinHyphenCase = (...strings) => strings.join("-");
128
+ const convertTopLevelKeysToSnakeCase = (object) => Object.entries(object).reduce((acc, curr) => {
129
+ const snakeCaseKey = neetoCist.camelToSnakeCase(curr[0]);
130
+ acc[snakeCaseKey] = curr[1];
131
+ return acc;
132
+ }, {});
133
+
36
134
  function bind(fn, thisArg) {
37
135
  return function wrap() {
38
136
  return fn.apply(thisArg, arguments);
@@ -13429,10 +13527,10 @@ const hasStandardBrowserWebWorkerEnv = (() => {
13429
13527
  })();
13430
13528
 
13431
13529
  var utils = /*#__PURE__*/Object.freeze({
13432
- __proto__: null,
13433
- hasBrowserEnv: hasBrowserEnv,
13434
- hasStandardBrowserWebWorkerEnv: hasStandardBrowserWebWorkerEnv,
13435
- hasStandardBrowserEnv: hasStandardBrowserEnv
13530
+ __proto__: null,
13531
+ hasBrowserEnv: hasBrowserEnv,
13532
+ hasStandardBrowserWebWorkerEnv: hasStandardBrowserWebWorkerEnv,
13533
+ hasStandardBrowserEnv: hasStandardBrowserEnv
13436
13534
  });
13437
13535
 
13438
13536
  var platform = {
@@ -18451,49 +18549,26 @@ axios.HttpStatusCode = HttpStatusCode;
18451
18549
 
18452
18550
  axios.default = axios;
18453
18551
 
18454
- const HEADERS_KEYS = {
18455
- applicationKey: "Application-Key",
18456
- contentType: "Content-Type",
18457
- accept: "Accept",
18458
- apiKey: "X-Api-Key",
18459
- projectKey: "Project-Key",
18460
- };
18461
18552
  const API_BASE_URL = "/api/v1";
18462
18553
 
18463
- const create$2 = (ciBuildId, history_id, payload) => axios.post(`${API_BASE_URL}/reporter/runs/${ciBuildId}/test_entities/${history_id}/attempts`, payload);
18464
- const update$1 = (ciBuildId, history_id, id, payload) => axios.put(`${API_BASE_URL}/reporter/runs/${ciBuildId}/test_entities/${history_id}/attempts/${id}`, payload);
18465
- const attemptsApi = { create: create$2, update: update$1 };
18466
-
18467
- const getDirectUploadURL = (payload) => axios.post(`${API_BASE_URL}/reporter/direct_uploads`, {
18468
- blob: payload,
18554
+ const create$2 = (payload) => {
18555
+ const snakeCasedPayload = convertTopLevelKeysToSnakeCase(payload);
18556
+ return axios.post(`${API_BASE_URL}/reporter/runs`, {
18557
+ run: snakeCasedPayload,
18558
+ }, { skipSnakeCaseConversion: true });
18559
+ };
18560
+ const update$1 = (ciBuildId, payload) => axios.put(`${API_BASE_URL}/reporter/runs/${ciBuildId}`, {
18561
+ run: payload,
18469
18562
  });
18470
- const uploadToS3 = (data, { url, headers }) => axios.put(url, data, { headers });
18471
-
18472
- const VALID_ASSET_TYPES = ["screenshot", "video", "trace"];
18563
+ const heartbeat = (ciBuildId) => axios.get(`${API_BASE_URL}/reporter/runs/${ciBuildId}/heartbeat`);
18564
+ const runsApi = { create: create$2, update: update$1, heartbeat };
18473
18565
 
18474
- const ERRORS = {
18475
- onBegin: {
18476
- noTestsToReport: "No tests to report",
18477
- failedToGetCommitSha: "Failed to get current commit SHA.",
18478
- failedToGetAuthor: "Failed to get current author.",
18479
- failedToGetBranch: "Failed to get current branch.",
18480
- failedToGetCommitMessage: "Failed to get current commit message.",
18481
- failedToInitializeRun: "Failed to initialize run in reporter",
18482
- },
18483
- onTestBegin: {
18484
- failedToReportTest: (testTitle, historyId) => `Failed to report test "${testTitle}" with history ID ${historyId}`,
18485
- },
18486
- onEnd: {
18487
- failedToReportRunStatus: "Failed to report run status",
18488
- },
18489
- onTestEnd: {
18490
- attemptAlreadyReported: "Attempt was already reported",
18491
- },
18492
- heartbeat: {
18493
- stopped: "Run was stopped at the reporter",
18494
- },
18495
- };
18566
+ const create$1 = (ciBuildId, payload) => axios.post(`${API_BASE_URL}/reporter/runs/${ciBuildId}/test_entities`, {
18567
+ testEntity: payload,
18568
+ });
18569
+ const testEntitiesApi = { create: create$1 };
18496
18570
 
18571
+ const EQUALS = "=".repeat(10);
18497
18572
  const MESSAGES = {
18498
18573
  onBegin: {
18499
18574
  testStarted: "Started reporting to NeetoPlaydash 🎭",
@@ -18502,86 +18577,18 @@ const MESSAGES = {
18502
18577
  currentShard: (currentShard) => `Current shard: ${currentShard}`,
18503
18578
  },
18504
18579
  onTestBegin: {
18505
- startingTest: (titlePath) => `Starting ${titlePath}`,
18580
+ startingTest: (titlePath, historyId) => `${EQUALS}Starting ${titlePath}${EQUALS}
18581
+ Time: ${new Date().toUTCString()}
18582
+ History ID: ${historyId}`,
18506
18583
  },
18507
18584
  onTestEnd: {
18508
- reportedTest: ({ title, status, id }) => `Reported ${title} with id: ${id} to NeetoPlaydash with status ${status}`,
18585
+ reportedTest: ({ title, status, historyId, completedAt, }) => `Reported ${title} with id: ${historyId} to NeetoPlaydash with status ${status} at ${completedAt}`,
18509
18586
  },
18510
18587
  onEnd: {
18511
18588
  runReported: "Run completed and reported to NeetoPlaydash 🎉",
18512
18589
  },
18513
18590
  };
18514
18591
 
18515
- const createShardObject = ({ currentShard = 0, status = "running", duration = null, }) => ({
18516
- [currentShard]: { status, duration },
18517
- });
18518
- const waitUntilTimeout = (timeout) => new Promise(resolve => setTimeout(resolve, timeout));
18519
- const convertToSha1 = (text) => crypto__default["default"].createHash("sha1").update(text).digest("hex").slice(0, 20);
18520
- const generateHistoryId = (projectId, specName, describeTitles, testTitle) => {
18521
- const titles = [...describeTitles, testTitle];
18522
- const targetString = `[project=${projectId}]${specName}\x1e${titles.join("\x1e")}`;
18523
- const targetStringSha = convertToSha1(targetString);
18524
- const pathNameSha = convertToSha1(specName);
18525
- return `${pathNameSha}-${targetStringSha}`;
18526
- };
18527
-
18528
- const LOG_LEVEL_FORMATTERS = {
18529
- bold: "\x1b[1m",
18530
- dim: "\x1b[2m",
18531
- underline: "\x1b[4m",
18532
- invertBackground: "\x1b[7m",
18533
- hidden: "\x1b[8m",
18534
- error: "\u001b[31m\x1b[1m",
18535
- redText: "\u001b[31m\x1b[1m", // Same ANSI style as error but in log level printing important errors
18536
- warning: "\u001b[33m\x1b[1m",
18537
- };
18538
-
18539
- class ConsoleLogFormatted {
18540
- constructor() {
18541
- this.LOG_LEVELS = {
18542
- info: ["bold", "dim", "underline", "invertBackground", "hidden", "redText"],
18543
- get warning() {
18544
- return [...this.info, "warning"];
18545
- },
18546
- get error() {
18547
- return [...this.warning, "error"];
18548
- },
18549
- };
18550
- this.shouldPrintMessage = (type) => {
18551
- var _a;
18552
- const logLevel = ((_a = process.env.PLAYDASH_LOG_LEVEL) !== null && _a !== void 0 ? _a : "info");
18553
- const validMethods = this.LOG_LEVELS[logLevel] || [];
18554
- return validMethods.includes(type);
18555
- };
18556
- Object.entries(LOG_LEVEL_FORMATTERS).forEach(([logLevel, ansiFormatter]) => {
18557
- this.createMethod(logLevel, ansiFormatter);
18558
- });
18559
- }
18560
- createMethod(logLevel, ansiFormatter) {
18561
- this[logLevel] = (message) => {
18562
- if (this.shouldPrintMessage(logLevel)) {
18563
- console.log(`${ansiFormatter}${message}\x1b[0m\u001b[0m`);
18564
- }
18565
- };
18566
- }
18567
- }
18568
- var consoleLogFormatted = new ConsoleLogFormatted();
18569
-
18570
- const getFileData = (file, contentType, filename) => {
18571
- const byte_size = file.byteLength;
18572
- const checksum = crypto__default["default"].createHash("md5").update(file).digest("base64");
18573
- return { file, filename, checksum, byte_size, content_type: contentType };
18574
- };
18575
-
18576
- const create$1 = (payload) => axios.post(`${API_BASE_URL}/reporter/runs`, {
18577
- run: payload,
18578
- });
18579
- const update = (ciBuildId, payload) => axios.put(`${API_BASE_URL}/reporter/runs/${ciBuildId}`, {
18580
- run: payload,
18581
- });
18582
- const heartbeat = (ciBuildId) => axios.get(`${API_BASE_URL}/reporter/runs/${ciBuildId}/heartbeat`);
18583
- const runsApi = { create: create$1, update, heartbeat };
18584
-
18585
18592
  const executeCommandLine = ({ command, messageOnError, shouldThrowError = false, logLevel = "warning", }) => {
18586
18593
  try {
18587
18594
  return childProcess__default["default"].execSync(command).toString().trim();
@@ -18621,7 +18628,7 @@ const getTestData = (test, rootDir) => {
18621
18628
  const spec = file.replace(`${rootDir}/`, "");
18622
18629
  const describe = getDescribePath({ titlePath, title, spec, project });
18623
18630
  const historyId = generateHistoryId(project !== null && project !== void 0 ? project : "", spec, describe, title);
18624
- return { title, describe, project, spec, history_id: historyId };
18631
+ return { title, describe, project, spec, historyId };
18625
18632
  };
18626
18633
  const getInitializerData = ({ rootDir }, rootSuite) => ramda.uniq(rootSuite.allTests().map(test => getTestData(test, rootDir)));
18627
18634
  const sendHeartBeatSignal = async (ciBuildId) => {
@@ -18635,13 +18642,209 @@ const sendHeartBeatSignal = async (ciBuildId) => {
18635
18642
  }
18636
18643
  };
18637
18644
 
18645
+ const onBegin = async (self, config, rootSuite) => {
18646
+ var _a, _b, _c, _d;
18647
+ const shard = config.shard;
18648
+ self.config = config;
18649
+ const currentShard = (self.currentShard = (_a = shard === null || shard === void 0 ? void 0 : shard.current) !== null && _a !== void 0 ? _a : 0);
18650
+ let attempts = {};
18651
+ self.totalTestCount = rootSuite.allTests().length;
18652
+ self.rootDir = config.rootDir;
18653
+ try {
18654
+ const runDetails = {
18655
+ commitId: getCurrentCommitSha(),
18656
+ commitName: getCurrentCommitMessage(),
18657
+ author: getCurrentAuthor(),
18658
+ branch: getCurrentBranch(),
18659
+ tags: typeof self.tags === "string" ? self.tags.split(",") : self.tags,
18660
+ ciBuildId: self.ciBuildId,
18661
+ configuration: { ...config, baseDir: process.cwd() },
18662
+ shards: createShardObject({ currentShard }),
18663
+ };
18664
+ await runsApi.create(runDetails);
18665
+ self.hasRunStarted = true;
18666
+ if (self.totalTestCount === 0) {
18667
+ consoleLogFormatted.error(ERRORS.onBegin.noTestsToReport);
18668
+ return;
18669
+ }
18670
+ ({ data: attempts } = await testEntitiesApi.create(self.ciBuildId, {
18671
+ testEntities: getInitializerData(config, rootSuite),
18672
+ shard: currentShard,
18673
+ }));
18674
+ }
18675
+ catch (error) {
18676
+ const axiosError = error;
18677
+ const data = (_b = axiosError.response) === null || _b === void 0 ? void 0 : _b.data;
18678
+ consoleLogFormatted.error((_c = data === null || data === void 0 ? void 0 : data.error) !== null && _c !== void 0 ? _c : axiosError.message);
18679
+ consoleLogFormatted.error(ERRORS.onBegin.failedToInitializeRun);
18680
+ if (((_d = axiosError.response) === null || _d === void 0 ? void 0 : _d.status) === 422) {
18681
+ process.kill(process.pid, "SIGKILL");
18682
+ process.exit(1);
18683
+ }
18684
+ }
18685
+ consoleLogFormatted.underline(MESSAGES.onBegin.testStarted);
18686
+ consoleLogFormatted.bold(MESSAGES.onBegin.ciBuildId(self.ciBuildId));
18687
+ if (shard) {
18688
+ consoleLogFormatted.bold(MESSAGES.onBegin.totalShards(shard.total));
18689
+ consoleLogFormatted.bold(MESSAGES.onBegin.currentShard(currentShard));
18690
+ }
18691
+ await sendHeartBeatSignal(self.ciBuildId);
18692
+ self.heartbeatInterval = setInterval(async () => await sendHeartBeatSignal(self.ciBuildId), 45000);
18693
+ self.attempts = attempts;
18694
+ };
18695
+
18696
+ const onEnd = async (self, { status, duration }) => {
18697
+ var _a, _b;
18698
+ try {
18699
+ const { currentShard } = self;
18700
+ await waitUntilCondition(() => self.hasRunStarted && self.unreportedAttemptCount === 0);
18701
+ await Promise.allSettled(self.testResultCalls);
18702
+ await runsApi.update(self.ciBuildId, {
18703
+ shards: createShardObject({
18704
+ currentShard,
18705
+ status,
18706
+ duration,
18707
+ }),
18708
+ });
18709
+ }
18710
+ catch (error) {
18711
+ const data = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data;
18712
+ consoleLogFormatted.error((_b = data.error) !== null && _b !== void 0 ? _b : error.message);
18713
+ consoleLogFormatted.error(ERRORS.onEnd.failedToReportRunStatus);
18714
+ }
18715
+ finally {
18716
+ consoleLogFormatted.invertBackground(MESSAGES.onEnd.runReported);
18717
+ clearInterval(self.heartbeatInterval);
18718
+ }
18719
+ };
18720
+
18721
+ const create = (ciBuildId, historyId, payload) => axios.post(`${API_BASE_URL}/reporter/runs/${ciBuildId}/test_entities/${historyId}/attempts`, payload);
18722
+ const update = (ciBuildId, historyId, id, payload) => axios.put(`${API_BASE_URL}/reporter/runs/${ciBuildId}/test_entities/${historyId}/attempts/${id}`, payload);
18723
+ const attemptsApi = { create, update };
18724
+
18725
+ const FIRST_ATTEMPT_INDEX = "0-0";
18726
+
18727
+ const onTestBegin = async (self, test, { retry }) => {
18728
+ const { title, repeatEachIndex } = test;
18729
+ const { rootDir, currentShard, ciBuildId } = self;
18730
+ const { historyId } = getTestData(test, rootDir);
18731
+ const startedAt = new Date();
18732
+ const attemptIndex = joinHyphenCase(retry, repeatEachIndex);
18733
+ self.unreportedAttemptCount++;
18734
+ consoleLogFormatted.invertBackground(MESSAGES.onTestBegin.startingTest(title, historyId));
18735
+ try {
18736
+ await waitUntilCondition(() => { var _a, _b; return (_b = (_a = self.attempts) === null || _a === void 0 ? void 0 : _a[historyId]) === null || _b === void 0 ? void 0 : _b[FIRST_ATTEMPT_INDEX]; });
18737
+ const attemptsPayload = {
18738
+ status: "running",
18739
+ startedAt: startedAt.toString(),
18740
+ shard: currentShard,
18741
+ repeatEachIndex,
18742
+ };
18743
+ if (retry === 0 && repeatEachIndex === 0) {
18744
+ await attemptsApi.update(ciBuildId, historyId, self.attempts[historyId][FIRST_ATTEMPT_INDEX], attemptsPayload);
18745
+ }
18746
+ else {
18747
+ const { data: { attemptId: attemptId }, } = await attemptsApi.create(ciBuildId, historyId, attemptsPayload);
18748
+ self.attempts = ramda.mergeDeepRight(self.attempts, {
18749
+ [historyId]: { [attemptIndex]: attemptId },
18750
+ });
18751
+ }
18752
+ }
18753
+ catch (error) {
18754
+ consoleLogFormatted.error(error.message);
18755
+ consoleLogFormatted.error(ERRORS.onTestBegin.failedToReportTest(title, historyId));
18756
+ await sendHeartBeatSignal(ciBuildId);
18757
+ }
18758
+ };
18759
+
18760
+ const getDirectUploadURL = (payload) => axios.post(`${API_BASE_URL}/reporter/direct_uploads`, {
18761
+ blob: payload,
18762
+ });
18763
+ const uploadToS3 = (data, { url, headers }) => axios.put(url, data, { headers });
18764
+
18765
+ const VALID_ASSET_TYPES = ["screenshot", "video", "trace"];
18766
+ const generateErrorReport = ({ location, snippet }) => {
18767
+ if (!location)
18768
+ return null;
18769
+ const { file, line, column } = location;
18770
+ return `# ${file} (${line}:${column}) \n\n\n ${snippet}`;
18771
+ };
18772
+
18773
+ const getFileData = (file, contentType, filename) => {
18774
+ const byteSize = file.byteLength;
18775
+ const checksum = crypto__default["default"].createHash("md5").update(file).digest("base64");
18776
+ return { file, filename, checksum, byteSize, contentType };
18777
+ };
18778
+ const generateFileName = (path, name, contentType) => path !== null && path !== void 0 ? path : `${name}.${contentType.split("/")[1]}`;
18779
+
18638
18780
  const evaluateStatus = (outcome, status) => {
18639
18781
  if (outcome === "flaky")
18640
18782
  return outcome;
18641
- else
18642
- return status;
18783
+ return status;
18643
18784
  };
18644
18785
 
18786
+ const onTestEnd = async (self, testCase, { status, duration, errors, error, retry, attachments }) => {
18787
+ const completedAt = new Date();
18788
+ const { currentShard, rootDir, ciBuildId } = self;
18789
+ const { title, repeatEachIndex } = testCase;
18790
+ const { historyId } = getTestData(testCase, rootDir);
18791
+ const testOutcome = testCase.outcome();
18792
+ const attemptIndex = joinHyphenCase(retry, repeatEachIndex);
18793
+ const outcome = {
18794
+ shard: currentShard,
18795
+ isExpected: testOutcome === "expected",
18796
+ repeatEachIndex: repeatEachIndex,
18797
+ status: evaluateStatus(testOutcome, status),
18798
+ };
18799
+ try {
18800
+ const testResult = {
18801
+ outcome,
18802
+ status,
18803
+ duration,
18804
+ log: errors.map(error => { var _a; return (_a = error.message) !== null && _a !== void 0 ? _a : ""; }).join("\n"),
18805
+ screenshots: [],
18806
+ videos: [],
18807
+ traces: [],
18808
+ completedAt: completedAt.toString(),
18809
+ errorSnippet: error && generateErrorReport(error),
18810
+ };
18811
+ consoleLogFormatted.underline(title);
18812
+ await Promise.all(attachments.map(async ({ name, path, body, contentType, }) => {
18813
+ consoleLogFormatted.bold(`${name}: ${path}`);
18814
+ if (VALID_ASSET_TYPES.includes(name)) {
18815
+ const buffer = path ? require$$6.readFileSync(path) : body;
18816
+ const fileName = generateFileName(path, name, contentType);
18817
+ const { file, ...metadata } = getFileData(buffer, contentType, fileName);
18818
+ const { data: { signedId, directUpload }, } = await getDirectUploadURL(metadata);
18819
+ testResult[`${name}s`].push(signedId);
18820
+ return uploadToS3(file, directUpload);
18821
+ }
18822
+ }));
18823
+ await waitUntilCondition(() => self.attempts[historyId][attemptIndex]);
18824
+ const reportToNeetoPlaydash = attemptsApi.update(ciBuildId, historyId, self.attempts[historyId][attemptIndex], testResult);
18825
+ self.testResultCalls.push(reportToNeetoPlaydash);
18826
+ await reportToNeetoPlaydash;
18827
+ consoleLogFormatted.bold(MESSAGES.onTestEnd.reportedTest({ title, status, historyId, completedAt }));
18828
+ }
18829
+ catch (error) {
18830
+ consoleLogFormatted.error(error.message);
18831
+ consoleLogFormatted.error(ERRORS.onTestBegin.failedToReportTest(title, historyId));
18832
+ await sendHeartBeatSignal(ciBuildId);
18833
+ }
18834
+ finally {
18835
+ self.unreportedAttemptCount--;
18836
+ }
18837
+ };
18838
+
18839
+ const HEADERS_KEYS = {
18840
+ applicationKey: "Application-Key",
18841
+ contentType: "Content-Type",
18842
+ accept: "Accept",
18843
+ apiKey: "X-Api-Key",
18844
+ projectKey: "Project-Key",
18845
+ };
18846
+
18847
+ const isValidObject = (data) => data === Object(data) && !(data instanceof Buffer);
18645
18848
  const setAuthHeaders = ({ projectKey, apiKey, }) => {
18646
18849
  axios.defaults.headers = {
18647
18850
  ...axios.defaults.headers,
@@ -18653,204 +18856,53 @@ const setAuthHeaders = ({ projectKey, apiKey, }) => {
18653
18856
  };
18654
18857
  function initializeAxios({ projectKey, baseURL, apiKey, }) {
18655
18858
  axios.defaults.baseURL = baseURL !== null && baseURL !== void 0 ? baseURL : "https://connect.neetoplaydash.com";
18859
+ axios.defaults.skipSnakeCaseConversion = false;
18656
18860
  setAuthHeaders({ projectKey, apiKey });
18861
+ axios.interceptors.request.use(config => {
18862
+ const data = config.data;
18863
+ if (isValidObject(data) && !config.skipSnakeCaseConversion) {
18864
+ config.data = neetoCist.keysToSnakeCase(data);
18865
+ }
18866
+ return config;
18867
+ });
18868
+ axios.interceptors.response.use(config => {
18869
+ const data = config.data;
18870
+ if (isValidObject(data)) {
18871
+ config.data = neetoCist.keysToCamelCase(data);
18872
+ }
18873
+ return config;
18874
+ });
18657
18875
  }
18658
18876
 
18659
- const create = (ciBuildId, payload) => axios.post(`${API_BASE_URL}/reporter/runs/${ciBuildId}/test_entities`, {
18660
- test_entity: payload,
18661
- });
18662
- const testEntitiesApi = { create };
18877
+ const initialize = (self, options) => {
18878
+ initializeAxios(options);
18879
+ process.on("unhandledRejection", async (error) => {
18880
+ var _a, _b, _c;
18881
+ const data = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data;
18882
+ await sendHeartBeatSignal(self.ciBuildId);
18883
+ consoleLogFormatted.error((_c = (_b = data === null || data === void 0 ? void 0 : data.error) !== null && _b !== void 0 ? _b : error.message) !== null && _c !== void 0 ? _c : JSON.stringify(error));
18884
+ });
18885
+ };
18663
18886
 
18664
- class MyReporter {
18887
+ class NeetoPlaywrightReporter {
18665
18888
  constructor(options) {
18666
- this.onBegin = async (config, rootSuite) => {
18667
- var _a, _b, _c, _d;
18668
- const shard = config.shard;
18669
- this.config = config;
18670
- this.currentShard = shard === null || shard === void 0 ? void 0 : shard.current;
18671
- let attempts = {};
18672
- this.totalTestCount = rootSuite.allTests().length;
18673
- this.rootDir = config.rootDir;
18674
- try {
18675
- const runDetails = {
18676
- commit_id: getCurrentCommitSha(),
18677
- commit_name: getCurrentCommitMessage(),
18678
- author: getCurrentAuthor(),
18679
- branch: getCurrentBranch(),
18680
- tags: typeof this.tags === "string" ? this.tags.split(",") : this.tags,
18681
- ci_build_id: this.ciBuildId,
18682
- configuration: config,
18683
- shards: createShardObject({ currentShard: shard === null || shard === void 0 ? void 0 : shard.current }),
18684
- };
18685
- await runsApi.create(runDetails);
18686
- this.hasRunStarted = true;
18687
- if (this.totalTestCount === 0) {
18688
- consoleLogFormatted.error(ERRORS.onBegin.noTestsToReport);
18689
- return;
18690
- }
18691
- ({ data: attempts } = await testEntitiesApi.create(this.ciBuildId, {
18692
- test_entities: getInitializerData(config, rootSuite),
18693
- shard: (_a = this.currentShard) !== null && _a !== void 0 ? _a : 0,
18694
- }));
18695
- }
18696
- catch (error) {
18697
- const axiosError = error;
18698
- const data = (_b = axiosError.response) === null || _b === void 0 ? void 0 : _b.data;
18699
- consoleLogFormatted.error((_c = data === null || data === void 0 ? void 0 : data.error) !== null && _c !== void 0 ? _c : axiosError.message);
18700
- consoleLogFormatted.error(ERRORS.onBegin.failedToInitializeRun);
18701
- process.kill(process.pid, "SIGKILL");
18702
- ((_d = axiosError.response) === null || _d === void 0 ? void 0 : _d.status) === 422 && process.exit(1);
18703
- }
18704
- consoleLogFormatted.underline(MESSAGES.onBegin.testStarted);
18705
- consoleLogFormatted.dim(MESSAGES.onBegin.ciBuildId(this.ciBuildId));
18706
- if (shard) {
18707
- consoleLogFormatted.dim(MESSAGES.onBegin.totalShards(shard.total));
18708
- consoleLogFormatted.dim(MESSAGES.onBegin.currentShard(shard.current));
18709
- }
18710
- await sendHeartBeatSignal(this.ciBuildId);
18711
- this.heartbeatInterval = setInterval(async () => await sendHeartBeatSignal(this.ciBuildId), 45000);
18712
- this.attempts = attempts;
18713
- };
18714
- this.onTestBegin = async (test, { retry }) => {
18715
- var _a, _b;
18716
- const { title, repeatEachIndex } = test;
18717
- const { history_id: id } = getTestData(test, this.rootDir);
18718
- this.retryAttemptStartedAt = new Date();
18719
- this.unreportedAttemptCount++;
18720
- consoleLogFormatted.invertBackground(MESSAGES.onTestBegin.startingTest(title));
18721
- try {
18722
- while (!((_b = (_a = this.attempts) === null || _a === void 0 ? void 0 : _a[id]) === null || _b === void 0 ? void 0 : _b["0-0"]))
18723
- await waitUntilTimeout(100); // Poll every 100 milliseconds
18724
- const attemptsPayload = {
18725
- status: "running",
18726
- started_at: this.retryAttemptStartedAt.toString(),
18727
- shard: this.currentShard,
18728
- repeat_each_index: repeatEachIndex,
18729
- };
18730
- if (retry === 0 && repeatEachIndex === 0) {
18731
- await attemptsApi.update(this.ciBuildId, id, this.attempts[id]["0-0"], attemptsPayload);
18732
- }
18733
- else {
18734
- const { data: { history_id, attempt_id }, } = await attemptsApi.create(this.ciBuildId, id, attemptsPayload);
18735
- this.attempts = {
18736
- ...this.attempts,
18737
- [history_id]: {
18738
- ...this.attempts[history_id],
18739
- [`${retry}-${repeatEachIndex}`]: attempt_id,
18740
- },
18741
- };
18742
- this.testAttemptIds.push(`${history_id}-${attempt_id}`);
18743
- }
18744
- }
18745
- catch (error) {
18746
- consoleLogFormatted.error(error.message);
18747
- consoleLogFormatted.error(ERRORS.onTestBegin.failedToReportTest(title, id));
18748
- await sendHeartBeatSignal(this.ciBuildId);
18749
- }
18750
- };
18751
- this.onTestEnd = async (testCase, { status, duration, errors, error, retry, attachments }) => {
18752
- var _a;
18753
- const completedAt = new Date();
18754
- const errorLocation = error === null || error === void 0 ? void 0 : error.location;
18755
- const { title, repeatEachIndex } = testCase;
18756
- const { history_id: id } = getTestData(testCase, this.rootDir);
18757
- const testOutcome = testCase.outcome();
18758
- const outcome = {
18759
- shard: (_a = this.currentShard) !== null && _a !== void 0 ? _a : 0,
18760
- is_expected: testOutcome === "expected",
18761
- repeat_each_index: repeatEachIndex,
18762
- status: evaluateStatus(testOutcome, status),
18763
- };
18764
- try {
18765
- const testResult = {
18766
- outcome,
18767
- status,
18768
- duration,
18769
- log: errors.map(error => { var _a; return (_a = error.message) !== null && _a !== void 0 ? _a : ""; }).join("\n"),
18770
- screenshots: [],
18771
- videos: [],
18772
- traces: [],
18773
- completed_at: completedAt.toString(),
18774
- error_snippet: errorLocation
18775
- ? `# ${errorLocation === null || errorLocation === void 0 ? void 0 : errorLocation.file} (${errorLocation === null || errorLocation === void 0 ? void 0 : errorLocation.line}:${errorLocation === null || errorLocation === void 0 ? void 0 : errorLocation.column}) \n\n\n ${error === null || error === void 0 ? void 0 : error.snippet}`
18776
- : null,
18777
- };
18778
- consoleLogFormatted.underline(title);
18779
- await Promise.all(attachments.map(async ({ name, path, body, contentType, }) => {
18780
- consoleLogFormatted.dim(`${name}: ${path}`);
18781
- if (VALID_ASSET_TYPES.includes(name)) {
18782
- const buffer = path ? require$$6__default["default"].readFileSync(path) : body;
18783
- const fileName = path
18784
- ? path.split("/").slice(-1)[0]
18785
- : `${name}.${contentType.split("/").slice(-1)[0]}`;
18786
- const { file, ...metadata } = getFileData(buffer, contentType, fileName);
18787
- const { data: { signed_id, direct_upload }, } = await getDirectUploadURL(metadata);
18788
- const pluralizedAsset = `${name}s`;
18789
- testResult[pluralizedAsset].push(signed_id);
18790
- return uploadToS3(file, direct_upload);
18791
- }
18792
- }));
18793
- while (!(this.attempts[id][`${retry}-${repeatEachIndex}`] ||
18794
- this.testAttemptIds.includes(`${id}-${this.attempts[id][`${retry}-${repeatEachIndex}`]}`)))
18795
- await waitUntilTimeout(100);
18796
- const reportToNeetoPlaydash = attemptsApi.update(this.ciBuildId, id, this.attempts[id][`${retry}-${repeatEachIndex}`], testResult);
18797
- this.testResultCalls.push(reportToNeetoPlaydash);
18798
- await reportToNeetoPlaydash;
18799
- consoleLogFormatted.invertBackground(MESSAGES.onTestEnd.reportedTest({ title, status, id }));
18800
- }
18801
- catch (error) {
18802
- consoleLogFormatted.error(error.message);
18803
- consoleLogFormatted.error(ERRORS.onTestBegin.failedToReportTest(title, id));
18804
- await sendHeartBeatSignal(this.ciBuildId);
18805
- }
18806
- finally {
18807
- this.unreportedAttemptCount--;
18808
- }
18809
- };
18810
- this.onEnd = async ({ status, duration }) => {
18811
- var _a, _b;
18812
- try {
18813
- while (!(this.hasRunStarted && this.unreportedAttemptCount === 0))
18814
- await waitUntilTimeout(100);
18815
- await Promise.allSettled(this.testResultCalls);
18816
- await runsApi.update(this.ciBuildId, {
18817
- shards: createShardObject({
18818
- currentShard: this.currentShard,
18819
- status,
18820
- duration,
18821
- }),
18822
- });
18823
- }
18824
- catch (error) {
18825
- const data = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data;
18826
- consoleLogFormatted.error((_b = data.error) !== null && _b !== void 0 ? _b : error.message);
18827
- consoleLogFormatted.error(ERRORS.onEnd.failedToReportRunStatus);
18828
- }
18829
- finally {
18830
- consoleLogFormatted.invertBackground(MESSAGES.onEnd.runReported);
18831
- clearInterval(this.heartbeatInterval);
18832
- }
18833
- };
18834
- initializeAxios(options);
18889
+ this.onBegin = (config, rootSuite) => onBegin(this, config, rootSuite);
18890
+ this.onTestBegin = (test, result) => onTestBegin(this, test, result);
18891
+ this.onTestEnd = async (testCase, testResult) => onTestEnd(this, testCase, testResult);
18892
+ this.onEnd = async (fullResult) => onEnd(this, fullResult);
18835
18893
  this.attempts = {};
18836
18894
  this.tags = options.tags;
18837
18895
  this.ciBuildId = options.ciBuildId;
18838
- this.retryAttemptStartedAt = new Date();
18839
18896
  this.testResultCalls = [];
18840
18897
  this.totalTestCount = 0;
18841
18898
  this.unreportedAttemptCount = 0;
18842
18899
  this.hasRunStarted = false;
18843
- this.testAttemptIds = [];
18844
18900
  this.heartbeatInterval = null;
18845
18901
  this.rootDir = "";
18846
- process.on("unhandledRejection", async (error) => {
18847
- var _a, _b, _c;
18848
- const data = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data;
18849
- await sendHeartBeatSignal(this.ciBuildId);
18850
- consoleLogFormatted.error((_c = (_b = data === null || data === void 0 ? void 0 : data.error) !== null && _b !== void 0 ? _b : error.message) !== null && _c !== void 0 ? _c : JSON.stringify(error));
18851
- });
18902
+ this.currentShard = 0;
18903
+ initialize(this, options);
18852
18904
  }
18853
18905
  }
18854
18906
 
18855
- module.exports = MyReporter;
18907
+ module.exports = NeetoPlaywrightReporter;
18856
18908
  //# sourceMappingURL=index.cjs.js.map