@brillout/test-e2e 0.4.10 → 0.4.12

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/dist/cli.js CHANGED
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const source_map_support_1 = __importDefault(require("source-map-support"));
7
7
  const utils_1 = require("./utils");
8
- const runTests_1 = require("./runTests");
8
+ const runAll_1 = require("./runAll");
9
9
  const buildTs_1 = require("./buildTs");
10
10
  const Logs_1 = require("./Logs");
11
11
  initSourceMap();
@@ -14,7 +14,7 @@ initUserExitHandler();
14
14
  cli();
15
15
  function cli() {
16
16
  const filter = (0, utils_1.findFilesParseCliArgs)();
17
- (0, runTests_1.runTests)(filter);
17
+ (0, runAll_1.runAll)(filter);
18
18
  }
19
19
  function initPromiseRejectionHandler() {
20
20
  process.on('unhandledRejection', function (err) {
@@ -19,6 +19,7 @@ declare type TestInfo = {
19
19
  inspect: boolean;
20
20
  doNotFailOnWarning: boolean;
21
21
  serverUrl: string;
22
+ isFlaky: boolean;
22
23
  };
23
24
  hasStartedRunning?: boolean;
24
25
  skipped?: string;
@@ -0,0 +1,8 @@
1
+ export { logPass };
2
+ export { logWarn };
3
+ export { logFail };
4
+ export { hasFail };
5
+ declare function logPass(): void;
6
+ declare function logWarn(warning: string): void;
7
+ declare function logFail(reason: string, isFinalAttempt: boolean): void;
8
+ declare function hasFail(): boolean;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.hasFail = exports.logFail = exports.logWarn = exports.logPass = void 0;
7
+ const getCurrentTest_1 = require("./getCurrentTest");
8
+ const utils_1 = require("./utils");
9
+ const picocolors_1 = __importDefault(require("picocolors"));
10
+ function logPass() {
11
+ logStatus(false);
12
+ }
13
+ exports.logPass = logPass;
14
+ function logWarn(warning) {
15
+ logStatus(false, warning);
16
+ }
17
+ exports.logWarn = logWarn;
18
+ function logFail(reason, isFinalAttempt) {
19
+ if (isFinalAttempt) {
20
+ logStatus(true);
21
+ }
22
+ else {
23
+ logStatus(false, "Failed but marked as `isFlaky`; it'll be re-tried at the end.");
24
+ }
25
+ const { FAIL } = getStatusTags();
26
+ const color = (s) => picocolors_1.default.red(picocolors_1.default.bold(s));
27
+ const msg = `Test ${isFinalAttempt ? FAIL : 'failed'} because ${reason}, see below.`;
28
+ console.log(color(msg));
29
+ }
30
+ exports.logFail = logFail;
31
+ function logStatus(isFail, warning) {
32
+ const { testFile } = (0, getCurrentTest_1.getCurrentTest)();
33
+ const { PASS, FAIL, WARN } = getStatusTags();
34
+ if (warning) {
35
+ (0, utils_1.assert)(!isFail);
36
+ console.log(`${WARN} ${testFile} (${warning})`);
37
+ }
38
+ else if (isFail) {
39
+ hasFailLog = true;
40
+ console.log(`${FAIL} ${testFile}`);
41
+ }
42
+ else {
43
+ console.log(`${PASS} ${testFile}`);
44
+ }
45
+ }
46
+ function getStatusTags() {
47
+ const PASS = picocolors_1.default.bold(picocolors_1.default.white(picocolors_1.default.bgGreen(' PASS ')));
48
+ const FAIL = picocolors_1.default.bold(picocolors_1.default.white(picocolors_1.default.bgRed(' FAIL ')));
49
+ const WARN = picocolors_1.default.bold(picocolors_1.default.white(picocolors_1.default.bgYellow(' WARN ')));
50
+ return { PASS, FAIL, WARN };
51
+ }
52
+ let hasFailLog = false;
53
+ function hasFail() {
54
+ return hasFailLog;
55
+ }
56
+ exports.hasFail = hasFail;
@@ -0,0 +1,3 @@
1
+ export { runAll };
2
+ import { type FindFilter } from './utils';
3
+ declare function runAll(filter: null | FindFilter): Promise<void>;
@@ -1,14 +1,10 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.logFailure = exports.runTests = void 0;
3
+ exports.runAll = void 0;
7
4
  const getCurrentTest_1 = require("./getCurrentTest");
8
5
  const Logs_1 = require("./Logs");
9
6
  const utils_1 = require("./utils");
10
7
  const utils_2 = require("./utils");
11
- const picocolors_1 = __importDefault(require("picocolors"));
12
8
  const github_action_1 = require("./github-action");
13
9
  const getCurrentTest_2 = require("./getCurrentTest");
14
10
  const getBrowser_1 = require("./getBrowser");
@@ -16,54 +12,68 @@ const buildTs_1 = require("./buildTs");
16
12
  const findTestFiles_1 = require("./findTestFiles");
17
13
  const getConfig_1 = require("./getConfig");
18
14
  const logError_1 = require("./logError");
19
- let hasFailure = false;
20
- async function runTests(filter) {
15
+ const logTestStatus_1 = require("./logTestStatus");
16
+ async function runAll(filter) {
21
17
  await (0, getConfig_1.loadConfig)();
22
18
  const testFiles = await (0, findTestFiles_1.findTestFiles)(filter);
23
19
  const browser = await (0, getBrowser_1.getBrowser)();
24
- let hasFailedTest = false;
20
+ const failedFirstAttempt = [];
25
21
  for (const testFile of testFiles) {
26
- (0, utils_1.assert)(testFile.endsWith('.ts'));
27
- const testFileJs = testFile.replace('.ts', '.mjs');
28
- const clean = await (0, buildTs_1.buildTs)(testFile, testFileJs);
29
- (0, getCurrentTest_2.setCurrentTest)(testFile);
30
- try {
31
- await import((0, utils_2.fsWindowsBugWorkaround)(testFileJs));
32
- }
33
- finally {
34
- clean();
22
+ const success = await buildAndTest(testFile, browser, false);
23
+ if (!success) {
24
+ failedFirstAttempt.push(testFile);
35
25
  }
36
- const success = await runTestFile(browser);
26
+ }
27
+ const failedSecondAttempt = [];
28
+ for (const testFile of failedFirstAttempt) {
29
+ const success = await buildAndTest(testFile, browser, true);
37
30
  if (!success) {
38
- hasFailedTest = true;
31
+ failedSecondAttempt.push(testFile);
39
32
  }
40
- (0, getCurrentTest_2.setCurrentTest)(null);
41
- (0, utils_1.assert)(testFileJs.endsWith('.mjs'));
42
33
  }
43
34
  await browser.close();
44
- if (hasFailedTest || hasFailure) {
45
- // hasFailedTest and hasFailure are redundant
46
- // - When assert.ts calls logFailure() this code block isn't run
47
- (0, utils_1.assert)(hasFailedTest && hasFailure);
48
- throw new Error('A test failed. See messages above.');
35
+ const hasFailLog = (0, logTestStatus_1.hasFail)();
36
+ const hasFailedTestFile = failedSecondAttempt.length > 0;
37
+ if (hasFailedTestFile || hasFailLog) {
38
+ // hasFailedTestFile and hasFailLog are redundant
39
+ // - When assert.ts calls logFail() this code block isn't run
40
+ (0, utils_1.assert)(hasFailedTestFile && hasFailLog);
41
+ throw new Error('Following tests failed, see logs above for more information.');
42
+ }
43
+ }
44
+ exports.runAll = runAll;
45
+ async function buildAndTest(testFile, browser, isSecondAttempt) {
46
+ (0, utils_1.assert)(testFile.endsWith('.ts'));
47
+ const testFileJs = testFile.replace('.ts', '.mjs');
48
+ const cleanBuild = await (0, buildTs_1.buildTs)(testFile, testFileJs);
49
+ (0, getCurrentTest_2.setCurrentTest)(testFile);
50
+ try {
51
+ await import((0, utils_2.fsWindowsBugWorkaround)(testFileJs) + `?cacheBuster=${Date.now()}`);
52
+ }
53
+ finally {
54
+ cleanBuild();
49
55
  }
56
+ const { success, clean } = await runTests(browser, isSecondAttempt);
57
+ await clean();
58
+ (0, getCurrentTest_2.setCurrentTest)(null);
59
+ (0, utils_1.assert)(testFileJs.endsWith('.mjs'));
60
+ return success;
50
61
  }
51
- exports.runTests = runTests;
52
- async function runTestFile(browser) {
62
+ async function runTests(browser, isSecondAttempt) {
53
63
  const testInfo = (0, getCurrentTest_1.getCurrentTest)();
54
64
  if (utils_1.isTTY) {
55
65
  console.log();
56
66
  console.log(testInfo.testFile);
57
67
  }
58
- const page = await browser.newPage();
59
- testInfo.page = page;
60
68
  // Set when user calls `skip()`
61
69
  if (testInfo.skipped) {
62
- logTestStatus(false);
70
+ (0, logTestStatus_1.logWarn)(testInfo.skipped);
63
71
  (0, utils_1.assertUsage)(!testInfo.runInfo, 'You cannot call `run()` after calling `skip()`');
64
72
  (0, utils_1.assertUsage)(testInfo.tests === undefined, 'You cannot call `test()` after calling `skip()`');
65
- return true;
73
+ return { success: true, clean: async () => { } };
66
74
  }
75
+ const page = await browser.newPage();
76
+ testInfo.page = page;
67
77
  // Set when user calls `run()`
68
78
  (0, utils_1.assert)(testInfo.runInfo);
69
79
  (0, utils_1.assert)(testInfo.startServer);
@@ -71,24 +81,23 @@ async function runTestFile(browser) {
71
81
  (0, utils_1.assert)(testInfo.afterEach);
72
82
  // Set when user calls `test()`
73
83
  (0, utils_1.assert)(testInfo.tests);
84
+ const isFinalAttempt = isSecondAttempt || !testInfo.runInfo.isFlaky;
74
85
  const clean = async () => {
75
86
  await testInfo.terminateServer?.();
76
87
  await page.close();
77
88
  };
78
- const abort = async () => {
79
- await clean();
89
+ const failure = () => {
80
90
  (0, github_action_1.abortIfGitHubAction)();
91
+ return { success: false, clean };
81
92
  };
82
- // TODO: resolve a success flag instead rejecting
83
93
  try {
84
94
  await testInfo.startServer();
85
95
  }
86
96
  catch (err) {
87
- logFailure('an error occurred while starting the server');
97
+ (0, logTestStatus_1.logFail)('an error occurred while starting the server', isFinalAttempt);
88
98
  (0, logError_1.logError)(err);
89
99
  Logs_1.Logs.flushLogs();
90
- await abort();
91
- return false;
100
+ return failure();
92
101
  }
93
102
  for (const { testDesc, testFn } of testInfo.tests) {
94
103
  Logs_1.Logs.add({
@@ -111,40 +120,37 @@ async function runTestFile(browser) {
111
120
  const isFailure = err || hasErrorLog;
112
121
  if (isFailure) {
113
122
  if (err) {
114
- logFailure(`the test "${testDesc}" threw an error`);
123
+ (0, logTestStatus_1.logFail)(`the test "${testDesc}" threw an error`, isFinalAttempt);
115
124
  (0, logError_1.logError)(err);
116
125
  }
117
126
  else if (hasErrorLog) {
118
- logFailure(`${getErrorType(failOnWarning)} occurred while running the test "${testDesc}"`);
127
+ (0, logTestStatus_1.logFail)(`${getErrorType(failOnWarning)} occurred while running the test "${testDesc}"`, isFinalAttempt);
119
128
  }
120
129
  else {
121
130
  (0, utils_1.assert)(false);
122
131
  }
123
132
  Logs_1.Logs.logErrors(failOnWarning);
124
133
  Logs_1.Logs.flushLogs();
125
- await abort();
126
- return false;
134
+ return failure();
127
135
  }
128
136
  }
129
137
  Logs_1.Logs.clearLogs();
130
138
  }
131
- await clean();
132
139
  // Check whether stderr emitted during testInfo.terminateServer()
133
140
  {
134
141
  const failOnWarning = true;
135
142
  if (Logs_1.Logs.hasErrorLogs(failOnWarning) &&
136
143
  // See comments about taskkill in src/setup.ts
137
144
  !(0, utils_1.isWindows)()) {
138
- logFailure(`${getErrorType(failOnWarning)} occurred during server termination`);
145
+ (0, logTestStatus_1.logFail)(`${getErrorType(failOnWarning)} occurred during server termination`, isFinalAttempt);
139
146
  Logs_1.Logs.logErrors(failOnWarning);
140
147
  Logs_1.Logs.flushLogs();
141
- await abort();
142
- return false;
148
+ return failure();
143
149
  }
144
150
  }
145
151
  Logs_1.Logs.clearLogs();
146
- logTestStatus(true);
147
- return true;
152
+ (0, logTestStatus_1.logPass)();
153
+ return { success: true, clean };
148
154
  }
149
155
  function getErrorType(failOnWarning) {
150
156
  return !failOnWarning ? 'error(s)' : 'error(s)/warning(s)';
@@ -174,33 +180,3 @@ function runTest(testFn, testFunctionTimeout) {
174
180
  })();
175
181
  return promise;
176
182
  }
177
- function logTestStatus(success) {
178
- const testInfo = (0, getCurrentTest_1.getCurrentTest)();
179
- const { PASS, FAIL, WARN } = getStatusTags();
180
- if (success) {
181
- (0, utils_1.assert)(!testInfo.skipped);
182
- console.log(`${PASS} ${testInfo.testFile}`);
183
- return;
184
- }
185
- if (testInfo.skipped) {
186
- console.log(`${WARN} ${testInfo.testFile} (${testInfo.skipped})`);
187
- }
188
- else {
189
- console.log(`${FAIL} ${testInfo.testFile}`);
190
- }
191
- }
192
- function logFailure(reason) {
193
- hasFailure = true;
194
- logTestStatus(false);
195
- const { FAIL } = getStatusTags();
196
- const color = (s) => picocolors_1.default.red(picocolors_1.default.bold(s));
197
- const msg = `Test ${FAIL} because ${reason}, see below.`;
198
- console.log(color(msg));
199
- }
200
- exports.logFailure = logFailure;
201
- function getStatusTags() {
202
- const PASS = picocolors_1.default.bold(picocolors_1.default.bgGreen(picocolors_1.default.white(' PASS ')));
203
- const FAIL = picocolors_1.default.bold(picocolors_1.default.bgRed(picocolors_1.default.white(' FAIL ')));
204
- const WARN = picocolors_1.default.bold(picocolors_1.default.bgYellow(picocolors_1.default.white(' WARN ')));
205
- return { PASS, FAIL, WARN };
206
- }
package/dist/setup.d.ts CHANGED
@@ -15,7 +15,7 @@ export { isMac };
15
15
  export { sleep };
16
16
  export { getServerUrl };
17
17
  declare function skip(reason: string): void;
18
- declare function run(cmd: string, { additionalTimeout, serverIsReadyMessage, serverIsReadyDelay, inspect, cwd, doNotFailOnWarning, serverUrl, }?: {
18
+ declare function run(cmd: string, { additionalTimeout, serverIsReadyMessage, serverIsReadyDelay, inspect, cwd, doNotFailOnWarning, serverUrl, isFlaky, }?: {
19
19
  additionalTimeout?: number;
20
20
  serverIsReadyMessage?: string | ((log: string) => boolean);
21
21
  serverIsReadyDelay?: number;
@@ -23,6 +23,7 @@ declare function run(cmd: string, { additionalTimeout, serverIsReadyMessage, ser
23
23
  cwd?: string;
24
24
  doNotFailOnWarning?: boolean;
25
25
  serverUrl?: string;
26
+ isFlaky?: boolean;
26
27
  }): void;
27
28
  declare function autoRetry(test: () => void | Promise<void>, { timeout }?: {
28
29
  timeout?: number;
package/dist/setup.js CHANGED
@@ -36,7 +36,7 @@ function skip(reason) {
36
36
  exports.skip = skip;
37
37
  function run(cmd, {
38
38
  //baseUrl = '',
39
- additionalTimeout = 0, serverIsReadyMessage, serverIsReadyDelay = 1000, inspect = utils_1.cliConfig.inspect, cwd, doNotFailOnWarning = false, serverUrl = serverUrlDefault, } = {}) {
39
+ additionalTimeout = 0, serverIsReadyMessage, serverIsReadyDelay = 1000, inspect = utils_1.cliConfig.inspect, cwd, doNotFailOnWarning = false, serverUrl = serverUrlDefault, isFlaky = false, } = {}) {
40
40
  additionalTimeout += serverIsReadyDelay;
41
41
  const testInfo = (0, getCurrentTest_1.getCurrentTest)();
42
42
  testInfo.runInfo = {
@@ -49,6 +49,7 @@ additionalTimeout = 0, serverIsReadyMessage, serverIsReadyDelay = 1000, inspect
49
49
  inspect,
50
50
  doNotFailOnWarning,
51
51
  serverUrl,
52
+ isFlaky,
52
53
  };
53
54
  if (inspect) {
54
55
  Logs_1.Logs.logEagerly = true;
@@ -254,8 +255,13 @@ function stopProcess({ proc, cwd, cmd, force, }) {
254
255
  // There doesn't seem to be an option to suppress these errors: https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/taskkill#parameters
255
256
  (0, child_process_1.spawn)('taskkill', ['/pid', String(proc.pid), '/f', '/t'], {
256
257
  stdio: [
257
- 'inherit',
258
- 'inherit',
258
+ 'ignore',
259
+ // Ignore:
260
+ // ```
261
+ // SUCCESS: The process with PID 6932 (child process of PID 2744) has been terminated.
262
+ // ```
263
+ // (Setting 'inherit' instead of 'ignore' attaches stdout to the root process and not to `proc` which is why the stdout of taskkill isn't intercepted by `proc.stdout.on('data', () => /* ... */)`.
264
+ 'ignore',
259
265
  // Ignoring stderr doesn't solve the problem that taskkill makes the process exit with code 1
260
266
  'inherit', // stderr
261
267
  ],
@@ -1,19 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.projectErrorPrefix = exports.getProjectError = exports.assertInfo = exports.assertWarning = exports.assertUsage = exports.assert = void 0;
4
- const runTests_1 = require("../runTests");
5
4
  const projectInfo_1 = require("./projectInfo");
6
5
  const Logs_1 = require("../Logs");
7
6
  const getCurrentTest_1 = require("../getCurrentTest");
8
7
  const logSection_1 = require("../logSection");
9
8
  const logError_1 = require("../logError");
9
+ const logTestStatus_1 = require("../logTestStatus");
10
10
  const errorPrefix = `[${projectInfo_1.projectInfo.npmPackageName}]`;
11
11
  exports.projectErrorPrefix = errorPrefix;
12
12
  const internalErrorPrefix = `${errorPrefix}[Bug]`;
13
13
  const usageErrorPrefix = `${errorPrefix}[Wrong Usage]`;
14
14
  const warningPrefix = `${errorPrefix}[Warning]`;
15
15
  const infoPrefix = `${errorPrefix}[Info]`;
16
- const numberOfStackTraceLinesToRemove = 2;
17
16
  function assert(condition, debugInfo) {
18
17
  if (condition) {
19
18
  return;
@@ -32,7 +31,7 @@ function assert(condition, debugInfo) {
32
31
  `Do not hesitate to reach out as it makes ${projectInfo_1.projectInfo.projectName} more robust.`,
33
32
  debugStr,
34
33
  ].join(' '));
35
- (0, runTests_1.logFailure)('a bug occurred in @brillout/test-e2e');
34
+ (0, logTestStatus_1.logFail)('a bug occurred in @brillout/test-e2e', true);
36
35
  (0, logError_1.logError)(internalError, 'BUG');
37
36
  logDebugInfo();
38
37
  Logs_1.Logs.logErrors(true);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brillout/test-e2e",
3
- "version": "0.4.10",
3
+ "version": "0.4.12",
4
4
  "bin": "./bin.js",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -1,5 +0,0 @@
1
- export { runTests };
2
- export { logFailure };
3
- import { type FindFilter } from './utils';
4
- declare function runTests(filter: null | FindFilter): Promise<void>;
5
- declare function logFailure(reason: string): void;