@empiricalrun/test-run 0.8.5 → 0.9.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/CHANGELOG.md +11 -0
- package/README.md +0 -37
- package/dist/bin/index.js +9 -9
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -47
- package/dist/lib/cmd.d.ts +17 -0
- package/dist/lib/cmd.d.ts.map +1 -0
- package/dist/lib/cmd.js +106 -0
- package/dist/lib/memfs/read-hello-world.d.ts +3 -0
- package/dist/lib/memfs/read-hello-world.d.ts.map +1 -0
- package/dist/lib/memfs/read-hello-world.js +14 -0
- package/dist/lib/run-all-tests.d.ts +10 -4
- package/dist/lib/run-all-tests.d.ts.map +1 -1
- package/dist/lib/run-all-tests.js +18 -17
- package/dist/lib/run-specific-test.d.ts +9 -4
- package/dist/lib/run-specific-test.d.ts.map +1 -1
- package/dist/lib/run-specific-test.js +42 -64
- package/dist/types/index.d.ts +5 -8
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/config-parser.d.ts +0 -1
- package/dist/utils/config-parser.d.ts.map +1 -1
- package/dist/utils/config-parser.js +0 -22
- package/dist/utils/config.d.ts +11 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +43 -0
- package/dist/utils/index.d.ts +1 -30
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +18 -127
- package/package.json +3 -2
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @empiricalrun/test-run
|
|
2
2
|
|
|
3
|
+
## 0.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 72f4577: feat: stop marking files as only and move to cmd builder approach
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- 97cef16: test: add memfs mocks for fs, remove unused config utils
|
|
12
|
+
- 28682e2: feat: use test-run cmd builder in test-gen, remove test.only marking
|
|
13
|
+
|
|
3
14
|
## 0.8.5
|
|
4
15
|
|
|
5
16
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -104,26 +104,6 @@ This is useful for omitting teardown steps during certain runs or local developm
|
|
|
104
104
|
|
|
105
105
|
---
|
|
106
106
|
|
|
107
|
-
## 8. Using a “.rc” File or Token
|
|
108
|
-
|
|
109
|
-
You can optionally define a configuration in `empiricalrc.json` or pass the same data as a base64-encoded `--token`. For example:
|
|
110
|
-
|
|
111
|
-
```json
|
|
112
|
-
{
|
|
113
|
-
"tests": [
|
|
114
|
-
{
|
|
115
|
-
"name": "some-test",
|
|
116
|
-
"filePath": "tests/abc.spec.ts",
|
|
117
|
-
"suites": ["suiteA"]
|
|
118
|
-
}
|
|
119
|
-
]
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
If both are missing or do not define tests, the CLI uses command-line parameters (`-n`, `--file`, etc.).
|
|
124
|
-
|
|
125
|
-
---
|
|
126
|
-
|
|
127
107
|
## 9. Environment & Build (Optional)
|
|
128
108
|
|
|
129
109
|
If `TEST_RUN_ENVIRONMENT` is defined:
|
|
@@ -172,23 +152,6 @@ This flow makes sure tests run against the relevant environment or a fresh build
|
|
|
172
152
|
|
|
173
153
|
---
|
|
174
154
|
|
|
175
|
-
## 12. Changelog
|
|
176
|
-
|
|
177
|
-
For version history and patch notes, see [CHANGELOG.md]
|
|
178
|
-
|
|
179
|
-
---
|
|
180
|
-
|
|
181
|
-
## 13. Contributing
|
|
182
|
-
|
|
183
|
-
- **Lint** with `pnpm lint`.
|
|
184
|
-
- **Test** with `pnpm test`.
|
|
185
|
-
- **Build** with `pnpm build`.
|
|
186
|
-
- **Dev** watch mode with `pnpm dev`.
|
|
187
|
-
|
|
188
|
-
Feel free to open issues or pull requests for enhancements or clarifications.
|
|
189
|
-
|
|
190
|
-
---
|
|
191
|
-
|
|
192
155
|
## 14. Conclusion
|
|
193
156
|
|
|
194
157
|
@test-run simplifies orchestrating Playwright tests, especially for:
|
package/dist/bin/index.js
CHANGED
|
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
const commander_1 = require("commander");
|
|
8
8
|
const dotenv_1 = __importDefault(require("dotenv"));
|
|
9
9
|
const dashboard_1 = require("../dashboard");
|
|
10
|
+
const cmd_1 = require("../lib/cmd");
|
|
10
11
|
const run_all_tests_1 = require("../lib/run-all-tests");
|
|
11
12
|
const run_specific_test_1 = require("../lib/run-specific-test");
|
|
12
13
|
const types_1 = require("../types");
|
|
@@ -16,7 +17,6 @@ dotenv_1.default.config({
|
|
|
16
17
|
path: [".env.local", ".env"],
|
|
17
18
|
});
|
|
18
19
|
(async function main() {
|
|
19
|
-
// TODO: add documentation of the test run
|
|
20
20
|
commander_1.program
|
|
21
21
|
.option("-n, --name <test-name>", "Name of the test to run")
|
|
22
22
|
.option("-s, --suites <suites>", "Suites under which the test is defined")
|
|
@@ -67,8 +67,7 @@ dotenv_1.default.config({
|
|
|
67
67
|
const suites = options.suites && options.suites.trim() !== ""
|
|
68
68
|
? options.suites?.split(",")
|
|
69
69
|
: undefined;
|
|
70
|
-
let tests = (
|
|
71
|
-
(0, config_parser_1.parseToken)(options.payload)?.tests ||
|
|
70
|
+
let tests = (0, config_parser_1.parseToken)(options.payload)?.tests ||
|
|
72
71
|
(options.name
|
|
73
72
|
? [
|
|
74
73
|
{
|
|
@@ -101,23 +100,24 @@ dotenv_1.default.config({
|
|
|
101
100
|
await (0, utils_1.handleTeardownSkipFlag)(directory);
|
|
102
101
|
}
|
|
103
102
|
const hasTestsFilter = tests && tests.length > 0;
|
|
104
|
-
let
|
|
103
|
+
let commandToRun;
|
|
105
104
|
if (hasTestsFilter) {
|
|
106
|
-
|
|
105
|
+
commandToRun = await (0, run_specific_test_1.runSpecificTestsCmd)({
|
|
107
106
|
tests,
|
|
108
107
|
projects: projectFilters,
|
|
109
|
-
|
|
108
|
+
passthroughArgs: pwOptions.join(" "),
|
|
110
109
|
platform,
|
|
111
110
|
});
|
|
112
111
|
}
|
|
113
112
|
else {
|
|
114
|
-
|
|
113
|
+
commandToRun = (0, run_all_tests_1.runAllTestsCmd)({
|
|
115
114
|
projects: projectFilters,
|
|
116
|
-
|
|
115
|
+
passthroughArgs: pwOptions.join(" "),
|
|
117
116
|
platform,
|
|
118
117
|
});
|
|
119
118
|
}
|
|
120
|
-
|
|
119
|
+
const { hasTestPassed } = await (0, cmd_1.runTestsForCmd)(commandToRun);
|
|
120
|
+
if (!hasTestPassed) {
|
|
121
121
|
process.exit(1);
|
|
122
122
|
}
|
|
123
123
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { runSpecificTestsCmd } from "./lib/run-specific-test";
|
|
2
|
+
export { runSpecificTestsCmd };
|
|
2
3
|
export declare function runSingleTest({ testName, suites, fileName, projects, envOverrides, }: {
|
|
3
4
|
testName: string;
|
|
4
5
|
suites: string[];
|
|
@@ -9,5 +10,4 @@ export declare function runSingleTest({ testName, suites, fileName, projects, en
|
|
|
9
10
|
hasTestPassed: boolean;
|
|
10
11
|
summaryJson: any;
|
|
11
12
|
}>;
|
|
12
|
-
export declare function listAllTests(cwd?: string): Promise<Record<string, TestCase>>;
|
|
13
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAO9D,OAAO,EAAE,mBAAmB,EAAE,CAAC;AAE/B,wBAAsB,aAAa,CAAC,EAClC,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,YAAY,GACb,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;;;GAqBA"}
|
package/dist/index.js
CHANGED
|
@@ -3,73 +3,32 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runSpecificTestsCmd = void 0;
|
|
6
7
|
exports.runSingleTest = runSingleTest;
|
|
7
|
-
exports.listAllTests = listAllTests;
|
|
8
8
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const cmd_1 = require("./lib/cmd");
|
|
10
11
|
const run_specific_test_1 = require("./lib/run-specific-test");
|
|
12
|
+
Object.defineProperty(exports, "runSpecificTestsCmd", { enumerable: true, get: function () { return run_specific_test_1.runSpecificTestsCmd; } });
|
|
11
13
|
const types_1 = require("./types");
|
|
12
|
-
const utils_1 = require("./utils");
|
|
13
14
|
// For test-run package, the library entrypoint, we only support web platform
|
|
14
15
|
// The bin entrypoint has support for mobile also
|
|
15
16
|
const supportedPlatform = types_1.Platform.WEB;
|
|
16
17
|
async function runSingleTest({ testName, suites, fileName, projects, envOverrides, }) {
|
|
17
18
|
const testDir = "tests";
|
|
18
19
|
const filePath = path_1.default.relative(process.cwd(), fileName);
|
|
19
|
-
const
|
|
20
|
+
const commandToRun = await (0, run_specific_test_1.runSpecificTestsCmd)({
|
|
20
21
|
tests: [{ name: testName, dir: testDir, filePath, suites }],
|
|
21
|
-
pwOptions: "",
|
|
22
22
|
projects,
|
|
23
23
|
envOverrides,
|
|
24
24
|
platform: supportedPlatform,
|
|
25
25
|
});
|
|
26
|
+
const { hasTestPassed } = await (0, cmd_1.runTestsForCmd)(commandToRun);
|
|
26
27
|
const jsonFilePath = path_1.default.join(process.cwd(), "playwright-report", `summary.json`);
|
|
27
28
|
const jsonFileContents = await promises_1.default.readFile(jsonFilePath, "utf8");
|
|
28
29
|
const summaryJson = JSON.parse(jsonFileContents);
|
|
29
30
|
return {
|
|
30
|
-
hasTestPassed
|
|
31
|
+
hasTestPassed,
|
|
31
32
|
summaryJson,
|
|
32
33
|
};
|
|
33
34
|
}
|
|
34
|
-
async function listAllTests(cwd) {
|
|
35
|
-
const testRunner = (0, utils_1.getTestRunner)(types_1.Platform.WEB);
|
|
36
|
-
const env = Object({ ...process.env });
|
|
37
|
-
const command = ["npx", testRunner, "test", "--list"];
|
|
38
|
-
const { output } = await (0, utils_1.cmdWithOutput)(command, {
|
|
39
|
-
env,
|
|
40
|
-
cwd,
|
|
41
|
-
});
|
|
42
|
-
// Parse the output to extract test information
|
|
43
|
-
// Example format: "[chromium] › home.spec.ts:9:9 › describe 1 › describe 2 › has title"
|
|
44
|
-
const result = {};
|
|
45
|
-
output
|
|
46
|
-
.split("\n")
|
|
47
|
-
.filter((line) => line.trim() &&
|
|
48
|
-
line.trim().startsWith("[") &&
|
|
49
|
-
line.includes(".spec.ts:"))
|
|
50
|
-
.forEach((line) => {
|
|
51
|
-
// Extract project, file, line, and test name with suites
|
|
52
|
-
const match = line.match(/\[(.*?)\]\s+›\s+(.*?):(\d+):\d+\s+›\s+(.*)/);
|
|
53
|
-
if (!match || !match[1] || !match[2] || !match[3] || !match[4]) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
const file = match[2];
|
|
57
|
-
const testPath = match[4];
|
|
58
|
-
const parts = testPath.split("›").map((p) => p.trim());
|
|
59
|
-
if (parts.length === 0) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
const name = parts[parts.length - 1];
|
|
63
|
-
if (!name) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
const suites = parts.length > 1 ? parts.slice(0, -1) : undefined;
|
|
67
|
-
const testCase = {
|
|
68
|
-
name,
|
|
69
|
-
filePath: file,
|
|
70
|
-
suites,
|
|
71
|
-
};
|
|
72
|
-
result[line.trim()] = testCase;
|
|
73
|
-
});
|
|
74
|
-
return result;
|
|
75
|
-
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { CommandToRun } from "../types";
|
|
2
|
+
export declare function getCommandFromString(command: string): {
|
|
3
|
+
command: string;
|
|
4
|
+
args: string[];
|
|
5
|
+
};
|
|
6
|
+
export declare function runTestsForCmd({ command, args, env }: CommandToRun): Promise<{
|
|
7
|
+
hasTestPassed: boolean;
|
|
8
|
+
}>;
|
|
9
|
+
export declare function spawnCmd(command: string, args: string[], options: {
|
|
10
|
+
env?: Record<string, string>;
|
|
11
|
+
cwd?: string;
|
|
12
|
+
captureOutput?: boolean;
|
|
13
|
+
}): Promise<number | {
|
|
14
|
+
code: number;
|
|
15
|
+
output: string;
|
|
16
|
+
}>;
|
|
17
|
+
//# sourceMappingURL=cmd.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cmd.d.ts","sourceRoot":"","sources":["../../src/lib/cmd.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,CAeA;AAED,wBAAsB,cAAc,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,YAAY;;GASxE;AAED,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE;IACP,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,GACA,OAAO,CAAC,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CA+CpD"}
|
package/dist/lib/cmd.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCommandFromString = getCommandFromString;
|
|
4
|
+
exports.runTestsForCmd = runTestsForCmd;
|
|
5
|
+
exports.spawnCmd = spawnCmd;
|
|
6
|
+
const child_process_1 = require("child_process");
|
|
7
|
+
function getCommandFromString(command) {
|
|
8
|
+
const regex = /[^\s"']+|"([^"]*)"|'([^']*)'/g;
|
|
9
|
+
const matches = command.match(regex) || [];
|
|
10
|
+
return {
|
|
11
|
+
command: matches[0],
|
|
12
|
+
args: matches.slice(1).map((match) => {
|
|
13
|
+
if ((match.startsWith('"') && match.endsWith('"')) ||
|
|
14
|
+
(match.startsWith("'") && match.endsWith("'"))) {
|
|
15
|
+
return match.slice(1, -1);
|
|
16
|
+
}
|
|
17
|
+
return match;
|
|
18
|
+
}),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
async function runTestsForCmd({ command, args, env }) {
|
|
22
|
+
console.log(`Running cmd: ${command} with args: ${args}`);
|
|
23
|
+
let hasTestPassed = true;
|
|
24
|
+
try {
|
|
25
|
+
await spawnCmd(command, args, { env });
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
hasTestPassed = false;
|
|
29
|
+
}
|
|
30
|
+
return { hasTestPassed };
|
|
31
|
+
}
|
|
32
|
+
async function spawnCmd(command, args, options) {
|
|
33
|
+
let output = options.captureOutput ? "" : undefined;
|
|
34
|
+
let errorLogs = [];
|
|
35
|
+
return new Promise((resolveFunc, rejectFunc) => {
|
|
36
|
+
const p = (0, child_process_1.spawn)(command, args, {
|
|
37
|
+
env: { ...process.env, ...options.env },
|
|
38
|
+
cwd: options.cwd,
|
|
39
|
+
// Ensure child process receives signals
|
|
40
|
+
detached: false,
|
|
41
|
+
});
|
|
42
|
+
// Setup signal handlers and get cleanup function
|
|
43
|
+
const cleanupSignalHandlers = setupProcessSignalHandlers(p);
|
|
44
|
+
p.stdout.on("data", (x) => {
|
|
45
|
+
const log = x.toString();
|
|
46
|
+
if (options.captureOutput) {
|
|
47
|
+
output += log;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
process.stdout.write(log);
|
|
51
|
+
}
|
|
52
|
+
if (log.includes("Error")) {
|
|
53
|
+
errorLogs.push(log);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
p.stderr.on("data", (x) => {
|
|
57
|
+
const log = x.toString();
|
|
58
|
+
process.stderr.write(log);
|
|
59
|
+
errorLogs.push(log);
|
|
60
|
+
});
|
|
61
|
+
p.on("exit", (code) => {
|
|
62
|
+
// Clean up signal handlers when the process exits
|
|
63
|
+
cleanupSignalHandlers();
|
|
64
|
+
if (code != 0) {
|
|
65
|
+
// assuming last log is the error message before exiting
|
|
66
|
+
rejectFunc(errorLogs.slice(-3).join("\n"));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
resolveFunc(options.captureOutput ? { code: code, output: output } : code);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function setupProcessSignalHandlers(proc) {
|
|
75
|
+
const handleSignal = async (signal) => {
|
|
76
|
+
console.log(`\nReceived ${signal}, gracefully shutting down...`);
|
|
77
|
+
if (proc && !proc.killed) {
|
|
78
|
+
// Forward the signal to the child process
|
|
79
|
+
proc.kill(signal);
|
|
80
|
+
// Wait for the child process to exit
|
|
81
|
+
await new Promise((resolve) => {
|
|
82
|
+
proc.once("exit", () => {
|
|
83
|
+
resolve();
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
process.exit(0);
|
|
88
|
+
};
|
|
89
|
+
const sigintHandler = async () => await handleSignal("SIGINT");
|
|
90
|
+
const sigtermHandler = async () => await handleSignal("SIGTERM");
|
|
91
|
+
const otherSignalHandlers = ["SIGABRT", "SIGHUP", "SIGQUIT"].map((signal) => {
|
|
92
|
+
const handler = () => console.log(`Received ${signal}, which is a no-op`);
|
|
93
|
+
process.once(signal, handler);
|
|
94
|
+
return { signal, handler };
|
|
95
|
+
});
|
|
96
|
+
process.once("SIGINT", sigintHandler);
|
|
97
|
+
process.once("SIGTERM", sigtermHandler);
|
|
98
|
+
// Return a cleanup function that removes all signal handlers
|
|
99
|
+
return () => {
|
|
100
|
+
process.removeListener("SIGINT", sigintHandler);
|
|
101
|
+
process.removeListener("SIGTERM", sigtermHandler);
|
|
102
|
+
otherSignalHandlers.forEach(({ signal, handler }) => {
|
|
103
|
+
process.removeListener(signal, handler);
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-hello-world.d.ts","sourceRoot":"","sources":["../../../src/lib/memfs/read-hello-world.ts"],"names":[],"mappings":"AAEA,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,UAE1C;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,WAE3C"}
|
|
@@ -0,0 +1,14 @@
|
|
|
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.readHelloWorld = readHelloWorld;
|
|
7
|
+
exports.verifyDirectory = verifyDirectory;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
function readHelloWorld(path) {
|
|
10
|
+
return fs_1.default.readFileSync(path, "utf-8");
|
|
11
|
+
}
|
|
12
|
+
function verifyDirectory(path) {
|
|
13
|
+
return fs_1.default.existsSync(path) && fs_1.default.statSync(path).isDirectory();
|
|
14
|
+
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare function
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { CommandToRun, Platform, TestCase } from "../types";
|
|
2
|
+
export declare function runAllTestsCmd({ projects, passthroughArgs, patternsToGrep, filesFilter, envOverrides, platform, }: {
|
|
3
|
+
tests?: TestCase[];
|
|
4
|
+
projects: string[];
|
|
5
|
+
passthroughArgs?: string;
|
|
6
|
+
platform: Platform;
|
|
7
|
+
filesFilter?: string;
|
|
8
|
+
envOverrides?: Record<string, string>;
|
|
9
|
+
patternsToGrep?: string[];
|
|
10
|
+
}): CommandToRun;
|
|
5
11
|
//# sourceMappingURL=run-all-tests.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-all-tests.d.ts","sourceRoot":"","sources":["../../src/lib/run-all-tests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"run-all-tests.d.ts","sourceRoot":"","sources":["../../src/lib/run-all-tests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAQ5D,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,eAAe,EACf,cAAc,EACd,WAAW,EACX,YAAY,EACZ,QAAQ,GACT,EAAE;IACD,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,QAAQ,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B,GAAG,YAAY,CAmBf"}
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.runAllTestsCmd = runAllTestsCmd;
|
|
4
4
|
const utils_1 = require("../utils");
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
5
|
+
const cmd_1 = require("./cmd");
|
|
6
|
+
function normalizeSpaces(command) {
|
|
7
|
+
return command.trim().replace(/ +/g, " ");
|
|
8
|
+
}
|
|
9
|
+
function runAllTestsCmd({ projects, passthroughArgs, patternsToGrep, filesFilter, envOverrides, platform, }) {
|
|
10
|
+
const testRunner = (0, utils_1.getTestRunner)(platform);
|
|
11
|
+
const projectsArg = (projects || [])
|
|
12
|
+
.map((project) => `--project ${project}`)
|
|
13
|
+
.join(" ");
|
|
14
|
+
const grepArgs = (patternsToGrep || [])
|
|
15
|
+
.map((pattern) => ` -g "${pattern}"`)
|
|
16
|
+
.join(" ");
|
|
17
|
+
let args = ` ${filesFilter || ""} ${grepArgs} ${projectsArg} ${passthroughArgs || ""}`;
|
|
18
|
+
const env = Object.assign({ ...process.env, PW_TEST_HTML_REPORT_OPEN: "never" }, envOverrides);
|
|
19
|
+
const testRunCmd = normalizeSpaces(`npx ${testRunner} test ${args}`);
|
|
20
20
|
return {
|
|
21
|
-
|
|
21
|
+
...(0, cmd_1.getCommandFromString)(testRunCmd),
|
|
22
|
+
env,
|
|
22
23
|
};
|
|
23
24
|
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare function
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { CommandToRun, Platform, TestCase } from "../types";
|
|
2
|
+
export declare function runSpecificTestsCmd({ tests, projects, passthroughArgs, platform, envOverrides, }: {
|
|
3
|
+
tests?: TestCase[];
|
|
4
|
+
projects: string[];
|
|
5
|
+
passthroughArgs?: string;
|
|
6
|
+
platform: Platform;
|
|
7
|
+
filesFilter?: string;
|
|
8
|
+
envOverrides?: Record<string, string>;
|
|
9
|
+
}): Promise<CommandToRun>;
|
|
5
10
|
//# sourceMappingURL=run-specific-test.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-specific-test.d.ts","sourceRoot":"","sources":["../../src/lib/run-specific-test.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"run-specific-test.d.ts","sourceRoot":"","sources":["../../src/lib/run-specific-test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAY5D,wBAAsB,mBAAmB,CAAC,EACxC,KAAU,EACV,QAAQ,EACR,eAAe,EACf,QAAQ,EACR,YAAY,GACb,EAAE;IACD,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,QAAQ,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC,GAAG,OAAO,CAAC,YAAY,CAAC,CAiFxB"}
|
|
@@ -1,50 +1,14 @@
|
|
|
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.
|
|
7
|
-
const fs_1 = require("fs");
|
|
8
|
-
const promises_1 = __importDefault(require("fs/promises"));
|
|
3
|
+
exports.runSpecificTestsCmd = runSpecificTestsCmd;
|
|
9
4
|
const utils_1 = require("../utils");
|
|
10
5
|
const run_all_tests_1 = require("./run-all-tests");
|
|
11
|
-
function
|
|
12
|
-
const events = ["beforeExit", "exit", "SIGINT", "SIGTERM"];
|
|
13
|
-
events.forEach((event) => process.on(event, cleanup));
|
|
14
|
-
return () => {
|
|
15
|
-
events.forEach((event) => process.removeListener(event, cleanup));
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
async function runSpecificTests({ tests = [], projects, pwOptions, platform, envOverrides, }) {
|
|
6
|
+
async function runSpecificTestsCmd({ tests = [], projects, passthroughArgs, platform, envOverrides, }) {
|
|
19
7
|
if (!tests || tests.length === 0) {
|
|
20
8
|
throw new Error("No tests found");
|
|
21
9
|
}
|
|
22
|
-
let
|
|
23
|
-
|
|
24
|
-
const isRunningForTeardownProjectOnly = teardownLabels && teardownLabels.every((label) => label.isTeardown);
|
|
25
|
-
if (isRunningForTeardownProjectOnly) {
|
|
26
|
-
// To run teardown projects, we need to run the `setup` project first, and playwright runs
|
|
27
|
-
// teardown automatically, when the corresponding setup project is run.
|
|
28
|
-
// We also disable marking files with test.only, because we want all of the setup to run
|
|
29
|
-
// before running the teardown tests.
|
|
30
|
-
disableTestOnlyMarking = true;
|
|
31
|
-
projects = teardownLabels
|
|
32
|
-
.map((label) => label.correspondingSetupProject)
|
|
33
|
-
.filter((project) => project !== undefined);
|
|
34
|
-
}
|
|
35
|
-
const touchedFiles = {};
|
|
36
|
-
// revert the changes made to the file to mark tests as only
|
|
37
|
-
// these needs to handle by listening to process exit/kill events
|
|
38
|
-
const revertFileContent = () => {
|
|
39
|
-
try {
|
|
40
|
-
Object.keys(touchedFiles).forEach((filePath) => {
|
|
41
|
-
(0, fs_1.writeFileSync)(filePath, touchedFiles[filePath], "utf-8");
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
catch (error) {
|
|
45
|
-
console.error("Error while cleaning up test file:", error);
|
|
46
|
-
}
|
|
47
|
-
};
|
|
10
|
+
let patternsToGrep = [];
|
|
11
|
+
let filesToRun = [];
|
|
48
12
|
for (const testCase of tests) {
|
|
49
13
|
const files = await (0, utils_1.getAllFilePaths)(testCase.dir, {
|
|
50
14
|
filePath: testCase.filePath,
|
|
@@ -62,41 +26,55 @@ async function runSpecificTests({ tests = [], projects, pwOptions, platform, env
|
|
|
62
26
|
}
|
|
63
27
|
}
|
|
64
28
|
if (!matchingFilePath) {
|
|
65
|
-
const
|
|
66
|
-
|
|
29
|
+
const suitesPrefix = testCase.suites
|
|
30
|
+
? `${testCase.suites.join(" > ")} > `
|
|
31
|
+
: "";
|
|
32
|
+
const fullTestName = `${suitesPrefix}${testCase.name}`;
|
|
33
|
+
const message = `No test block found for name: "${fullTestName}" in "${testCase.filePath}"`;
|
|
67
34
|
throw Error(message);
|
|
68
35
|
}
|
|
69
|
-
|
|
36
|
+
else {
|
|
37
|
+
filesToRun.push(matchingFilePath);
|
|
38
|
+
}
|
|
39
|
+
const { testCaseNode } = await (0, utils_1.getTestCaseNode)({
|
|
70
40
|
filePath: matchingFilePath,
|
|
71
41
|
scenarioName: testCase.name,
|
|
72
42
|
suites: testCase.suites,
|
|
73
43
|
});
|
|
74
|
-
const parentDescribe = (0, utils_1.findFirstSerialDescribeBlock)(testCaseNode);
|
|
75
44
|
const isFileMarkedSerial = await (0, utils_1.hasTopLevelDescribeConfigureWithSerialMode)(matchingFilePath);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
filePath: matchingFilePath,
|
|
88
|
-
});
|
|
45
|
+
if (!isFileMarkedSerial && testCaseNode) {
|
|
46
|
+
const parentDescribe = (0, utils_1.findFirstSerialDescribeBlock)(testCaseNode);
|
|
47
|
+
if (!parentDescribe) {
|
|
48
|
+
patternsToGrep.push(testCase.name);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
const parentDescribeName = (0, utils_1.getDescribeBlockName)(parentDescribe);
|
|
52
|
+
if (parentDescribeName) {
|
|
53
|
+
patternsToGrep.push(parentDescribeName);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
89
56
|
}
|
|
90
57
|
}
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
58
|
+
const teardownLabels = await (0, utils_1.labelTeardownProjects)(projects, platform);
|
|
59
|
+
const isRunningForTeardownProjectOnly = teardownLabels && teardownLabels.every((label) => label.isTeardown);
|
|
60
|
+
if (isRunningForTeardownProjectOnly) {
|
|
61
|
+
// To run teardown projects, we need to run the `setup` project first, and playwright runs
|
|
62
|
+
// teardown automatically, when the corresponding setup project is run.
|
|
63
|
+
projects = teardownLabels
|
|
64
|
+
.map((label) => label.correspondingSetupProject)
|
|
65
|
+
.filter((project) => project !== undefined);
|
|
66
|
+
// We also remove any files or grep filters, because we want setup and teardown to run completely
|
|
67
|
+
// We could add a grep invert flag for other teardown tests to reduce the number of tests that run
|
|
68
|
+
filesToRun = [];
|
|
69
|
+
patternsToGrep = [];
|
|
70
|
+
}
|
|
71
|
+
const commandToRun = (0, run_all_tests_1.runAllTestsCmd)({
|
|
72
|
+
filesFilter: [...new Set(filesToRun)].join(" "),
|
|
73
|
+
passthroughArgs,
|
|
95
74
|
projects,
|
|
96
75
|
envOverrides,
|
|
97
76
|
platform,
|
|
77
|
+
patternsToGrep,
|
|
98
78
|
});
|
|
99
|
-
|
|
100
|
-
revertFileContent();
|
|
101
|
-
return result;
|
|
79
|
+
return commandToRun;
|
|
102
80
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -7,14 +7,6 @@ export type TestCase = {
|
|
|
7
7
|
export type Config = {
|
|
8
8
|
tests: TestCase[];
|
|
9
9
|
} | undefined;
|
|
10
|
-
export type TestRunParameters = {
|
|
11
|
-
tests?: TestCase[];
|
|
12
|
-
projects: string[];
|
|
13
|
-
pwOptions: string;
|
|
14
|
-
platform: Platform;
|
|
15
|
-
filesFilter?: string;
|
|
16
|
-
envOverrides?: Record<string, string>;
|
|
17
|
-
};
|
|
18
10
|
export type Environment = {
|
|
19
11
|
id: number;
|
|
20
12
|
project_id: number;
|
|
@@ -44,4 +36,9 @@ export type PlaywProjectConfig = {
|
|
|
44
36
|
teardown?: string;
|
|
45
37
|
testMatch: string | RegExp;
|
|
46
38
|
};
|
|
39
|
+
export type CommandToRun = {
|
|
40
|
+
command: string;
|
|
41
|
+
args: string[];
|
|
42
|
+
env: Record<string, string>;
|
|
43
|
+
};
|
|
47
44
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IAAE,KAAK,EAAE,QAAQ,EAAE,CAAA;CAAE,GAAG,SAAS,CAAC;AAEvD,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,QAAQ,EAAE,QAAQ,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,CAAC;AAEF,oBAAY,QAAQ;IAClB,GAAG,QAAQ;IACX,OAAO,YAAY;IACnB,GAAG,QAAQ;CACZ;AAED,oBAAY,aAAa;IACvB,UAAU,eAAe;IACzB,SAAS,cAAc;CACxB;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-parser.d.ts","sourceRoot":"","sources":["../../src/utils/config-parser.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config-parser.d.ts","sourceRoot":"","sources":["../../src/utils/config-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAchD;AAED,wBAAgB,sBAAsB,SAAK"}
|
|
@@ -1,29 +1,7 @@
|
|
|
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.parseRcConfig = parseRcConfig;
|
|
7
3
|
exports.parseToken = parseToken;
|
|
8
4
|
exports.buildConfigFromCliArgs = buildConfigFromCliArgs;
|
|
9
|
-
const fs_1 = require("fs");
|
|
10
|
-
const promises_1 = __importDefault(require("fs/promises"));
|
|
11
|
-
async function parseRcConfig() {
|
|
12
|
-
// check if file exists
|
|
13
|
-
if (!(0, fs_1.existsSync)("empiricalrc.json")) {
|
|
14
|
-
return undefined;
|
|
15
|
-
}
|
|
16
|
-
try {
|
|
17
|
-
const rcConfig = await promises_1.default.readFile("empiricalrc.json", "utf-8");
|
|
18
|
-
const json = JSON.parse(rcConfig);
|
|
19
|
-
// TODO: validate the json
|
|
20
|
-
return json;
|
|
21
|
-
}
|
|
22
|
-
catch (e) {
|
|
23
|
-
console.error("Error parsing rc file:", e);
|
|
24
|
-
}
|
|
25
|
-
return undefined;
|
|
26
|
-
}
|
|
27
5
|
function parseToken(token) {
|
|
28
6
|
if (!token) {
|
|
29
7
|
return undefined;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Platform } from "../types";
|
|
2
|
+
type PlaywrightProject = {
|
|
3
|
+
name: string;
|
|
4
|
+
use: any;
|
|
5
|
+
testMatch: string[] | string | undefined;
|
|
6
|
+
testIgnore: string[] | string | undefined;
|
|
7
|
+
teardown: string | undefined;
|
|
8
|
+
};
|
|
9
|
+
export declare function getProjectsFromPlaywrightConfig(platform: Platform): Promise<PlaywrightProject[]>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEpC,KAAK,iBAAiB,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,GAAG,CAAC;IACT,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC;IACzC,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B,CAAC;AAEF,wBAAsB,+BAA+B,CACnD,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAqC9B"}
|
|
@@ -0,0 +1,43 @@
|
|
|
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.getProjectsFromPlaywrightConfig = getProjectsFromPlaywrightConfig;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
// For TypeScript type safety
|
|
9
|
+
let tsxImport = null;
|
|
10
|
+
const types_1 = require("../types");
|
|
11
|
+
async function getProjectsFromPlaywrightConfig(platform) {
|
|
12
|
+
const configName = platform === types_1.Platform.WEB ? "playwright.config.ts" : "appwright.config.ts";
|
|
13
|
+
const directoryPath = ".";
|
|
14
|
+
const pwFile = path_1.default.resolve(directoryPath, configName);
|
|
15
|
+
if (typeof window !== "undefined") {
|
|
16
|
+
throw new Error("readPlaywrightConfig cannot be used in browser environments");
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
// Only initialize on server side
|
|
20
|
+
// This will only execute on the server
|
|
21
|
+
await import("tsx/cjs/api")
|
|
22
|
+
.then((module) => {
|
|
23
|
+
tsxImport = module;
|
|
24
|
+
})
|
|
25
|
+
.catch(() => {
|
|
26
|
+
console.warn("Failed to import tsx: --->");
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
if (!tsxImport) {
|
|
30
|
+
console.warn("tsx module not available");
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
const repoDir = process.cwd();
|
|
34
|
+
const [lastDir] = repoDir.split("/").reverse();
|
|
35
|
+
try {
|
|
36
|
+
const playwrightConfig = (await tsxImport.require(pwFile, `${repoDir}/${lastDir}`)).default;
|
|
37
|
+
return playwrightConfig.projects;
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
console.error("Error getting project list from playwright config", err);
|
|
41
|
+
}
|
|
42
|
+
return [];
|
|
43
|
+
}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,33 +1,5 @@
|
|
|
1
1
|
import { Node, SourceFile } from "ts-morph";
|
|
2
2
|
import { Platform, TestFramework } from "../types";
|
|
3
|
-
type PlaywrightProject = {
|
|
4
|
-
name: string;
|
|
5
|
-
use: any;
|
|
6
|
-
testMatch: string[] | string | undefined;
|
|
7
|
-
testIgnore: string[] | string | undefined;
|
|
8
|
-
teardown: string | undefined;
|
|
9
|
-
};
|
|
10
|
-
export declare function cmd(command: string[], options: {
|
|
11
|
-
env?: Record<string, string>;
|
|
12
|
-
cwd?: string;
|
|
13
|
-
captureOutput?: boolean;
|
|
14
|
-
}): Promise<number | {
|
|
15
|
-
code: number;
|
|
16
|
-
output: string;
|
|
17
|
-
}>;
|
|
18
|
-
export declare function cmdWithOutput(command: string[], options: {
|
|
19
|
-
env?: Record<string, string>;
|
|
20
|
-
cwd?: string;
|
|
21
|
-
}): Promise<{
|
|
22
|
-
code: number;
|
|
23
|
-
output: string;
|
|
24
|
-
}>;
|
|
25
|
-
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* @param {string} [directoryPath=""]
|
|
29
|
-
* @return {*} {Promise<string[]>}
|
|
30
|
-
*/
|
|
31
3
|
export declare function getAllFilePaths(directoryPath?: string, filters?: {
|
|
32
4
|
filePath?: string;
|
|
33
5
|
}): Promise<string[]>;
|
|
@@ -45,6 +17,7 @@ export declare function hasTestBlock({ filePath, scenarioName, suites, }: {
|
|
|
45
17
|
scenarioName: string;
|
|
46
18
|
suites?: string[];
|
|
47
19
|
}): Promise<boolean>;
|
|
20
|
+
export declare function getDescribeBlockName(node: Node): string | undefined;
|
|
48
21
|
export declare function findFirstSerialDescribeBlock(node: Node | undefined): Node | undefined;
|
|
49
22
|
export declare function hasTopLevelDescribeConfigureWithSerialMode(filePath: string): Promise<boolean>;
|
|
50
23
|
export declare function markTestAsOnly({ sourceFile, parentDescribeNode, testCaseNode, filePath, }: {
|
|
@@ -53,7 +26,6 @@ export declare function markTestAsOnly({ sourceFile, parentDescribeNode, testCas
|
|
|
53
26
|
testCaseNode: Node;
|
|
54
27
|
filePath: string;
|
|
55
28
|
}): Promise<void>;
|
|
56
|
-
export declare function getProjectsFromPlaywrightConfig(platform: Platform): Promise<PlaywrightProject[]>;
|
|
57
29
|
export declare const filterArrayByGlobMatchersSet: (input: string[], globMatcherSets: string[][]) => string[];
|
|
58
30
|
export declare function labelTeardownProjects(projectNames: string[], platform: Platform): Promise<{
|
|
59
31
|
isTeardown: boolean;
|
|
@@ -85,5 +57,4 @@ export declare function getTypescriptTestBlock({ scenarioName, suites, content,
|
|
|
85
57
|
testAlias: string;
|
|
86
58
|
sourceFile: SourceFile;
|
|
87
59
|
};
|
|
88
|
-
export {};
|
|
89
60
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/index.js
CHANGED
|
@@ -4,113 +4,25 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.handleTeardownSkipFlag = exports.getTestRunner = exports.downloadBuild = exports.pickNameFromPackageJson = exports.generateProjectFilters = exports.filterArrayByGlobMatchersSet = exports.getTestModuleAliasFromSourceFile = void 0;
|
|
7
|
-
exports.cmd = cmd;
|
|
8
|
-
exports.cmdWithOutput = cmdWithOutput;
|
|
9
7
|
exports.getAllFilePaths = getAllFilePaths;
|
|
10
8
|
exports.getTestCaseNode = getTestCaseNode;
|
|
11
9
|
exports.hasTestBlock = hasTestBlock;
|
|
10
|
+
exports.getDescribeBlockName = getDescribeBlockName;
|
|
12
11
|
exports.findFirstSerialDescribeBlock = findFirstSerialDescribeBlock;
|
|
13
12
|
exports.hasTopLevelDescribeConfigureWithSerialMode = hasTopLevelDescribeConfigureWithSerialMode;
|
|
14
13
|
exports.markTestAsOnly = markTestAsOnly;
|
|
15
|
-
exports.getProjectsFromPlaywrightConfig = getProjectsFromPlaywrightConfig;
|
|
16
14
|
exports.labelTeardownProjects = labelTeardownProjects;
|
|
17
15
|
exports.buildRepoName = buildRepoName;
|
|
18
16
|
exports.getTypescriptTestBlock = getTypescriptTestBlock;
|
|
19
|
-
const child_process_1 = require("child_process");
|
|
20
17
|
const fs_1 = require("fs");
|
|
21
18
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
22
19
|
const minimatch_1 = require("minimatch");
|
|
23
20
|
const path_1 = __importDefault(require("path"));
|
|
24
21
|
const ts_morph_1 = require("ts-morph");
|
|
25
|
-
// For TypeScript type safety
|
|
26
|
-
let tsxImport = null;
|
|
27
22
|
const util_1 = require("util");
|
|
23
|
+
const cmd_1 = require("../lib/cmd");
|
|
28
24
|
const types_1 = require("../types");
|
|
29
|
-
|
|
30
|
-
const handleSignal = async (signal) => {
|
|
31
|
-
console.log(`\nReceived ${signal}, gracefully shutting down...`);
|
|
32
|
-
if (proc && !proc.killed) {
|
|
33
|
-
// Forward the signal to the child process
|
|
34
|
-
proc.kill(signal);
|
|
35
|
-
// Wait for the child process to exit
|
|
36
|
-
await new Promise((resolve) => {
|
|
37
|
-
proc.once("exit", () => {
|
|
38
|
-
resolve();
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
process.exit(0);
|
|
43
|
-
};
|
|
44
|
-
const sigintHandler = async () => await handleSignal("SIGINT");
|
|
45
|
-
const sigtermHandler = async () => await handleSignal("SIGTERM");
|
|
46
|
-
const otherSignalHandlers = ["SIGABRT", "SIGHUP", "SIGQUIT"].map((signal) => {
|
|
47
|
-
const handler = () => console.log(`Received ${signal}, which is a no-op`);
|
|
48
|
-
process.once(signal, handler);
|
|
49
|
-
return { signal, handler };
|
|
50
|
-
});
|
|
51
|
-
process.once("SIGINT", sigintHandler);
|
|
52
|
-
process.once("SIGTERM", sigtermHandler);
|
|
53
|
-
// Return a cleanup function that removes all signal handlers
|
|
54
|
-
return () => {
|
|
55
|
-
process.removeListener("SIGINT", sigintHandler);
|
|
56
|
-
process.removeListener("SIGTERM", sigtermHandler);
|
|
57
|
-
otherSignalHandlers.forEach(({ signal, handler }) => {
|
|
58
|
-
process.removeListener(signal, handler);
|
|
59
|
-
});
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
async function cmd(command, options) {
|
|
63
|
-
let output = options.captureOutput ? "" : undefined;
|
|
64
|
-
let errorLogs = [];
|
|
65
|
-
return new Promise((resolveFunc, rejectFunc) => {
|
|
66
|
-
const p = (0, child_process_1.spawn)(command[0], command.slice(1), {
|
|
67
|
-
env: { ...process.env, ...options.env },
|
|
68
|
-
cwd: options.cwd,
|
|
69
|
-
// Ensure child process receives signals
|
|
70
|
-
detached: false,
|
|
71
|
-
});
|
|
72
|
-
// Setup signal handlers and get cleanup function
|
|
73
|
-
const cleanupSignalHandlers = setupProcessSignalHandlers(p);
|
|
74
|
-
p.stdout.on("data", (x) => {
|
|
75
|
-
const log = x.toString();
|
|
76
|
-
if (options.captureOutput) {
|
|
77
|
-
output += log;
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
process.stdout.write(log);
|
|
81
|
-
}
|
|
82
|
-
if (log.includes("Error")) {
|
|
83
|
-
errorLogs.push(log);
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
p.stderr.on("data", (x) => {
|
|
87
|
-
const log = x.toString();
|
|
88
|
-
process.stderr.write(log);
|
|
89
|
-
errorLogs.push(log);
|
|
90
|
-
});
|
|
91
|
-
p.on("exit", (code) => {
|
|
92
|
-
// Clean up signal handlers when the process exits
|
|
93
|
-
cleanupSignalHandlers();
|
|
94
|
-
if (code != 0) {
|
|
95
|
-
// assuming last log is the error message before exiting
|
|
96
|
-
rejectFunc(errorLogs.slice(-3).join("\n"));
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
resolveFunc(options.captureOutput ? { code: code, output: output } : code);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
async function cmdWithOutput(command, options) {
|
|
105
|
-
const result = await cmd(command, { ...options, captureOutput: true });
|
|
106
|
-
return result;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
* @param {string} [directoryPath=""]
|
|
112
|
-
* @return {*} {Promise<string[]>}
|
|
113
|
-
*/
|
|
25
|
+
const config_1 = require("./config");
|
|
114
26
|
async function getAllFilePaths(directoryPath = "tests", filters = {}) {
|
|
115
27
|
let filePaths = [];
|
|
116
28
|
try {
|
|
@@ -172,6 +84,18 @@ async function hasTestBlock({ filePath, scenarioName, suites, }) {
|
|
|
172
84
|
});
|
|
173
85
|
return !!testCaseNode;
|
|
174
86
|
}
|
|
87
|
+
function getDescribeBlockName(node) {
|
|
88
|
+
if (ts_morph_1.Node.isCallExpression(node)) {
|
|
89
|
+
const expression = node.getExpression();
|
|
90
|
+
if (expression.getText().includes("test.describe")) {
|
|
91
|
+
const [firstArg] = node.getArguments();
|
|
92
|
+
if (firstArg && ts_morph_1.Node.isStringLiteral(firstArg)) {
|
|
93
|
+
return firstArg.getLiteralValue();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
175
99
|
function findFirstSerialDescribeBlock(node) {
|
|
176
100
|
let currentNode = node;
|
|
177
101
|
// Traverse upwards until we find a 'describe' block with 'serial: true'
|
|
@@ -241,39 +165,6 @@ async function markTestAsOnly({ sourceFile, parentDescribeNode, testCaseNode, fi
|
|
|
241
165
|
}
|
|
242
166
|
await promises_1.default.writeFile(filePath, updatedTestFileContent, "utf-8");
|
|
243
167
|
}
|
|
244
|
-
async function getProjectsFromPlaywrightConfig(platform) {
|
|
245
|
-
const configName = platform === types_1.Platform.WEB ? "playwright.config.ts" : "appwright.config.ts";
|
|
246
|
-
const directoryPath = ".";
|
|
247
|
-
const pwFile = path_1.default.resolve(directoryPath, configName);
|
|
248
|
-
if (typeof window !== "undefined") {
|
|
249
|
-
throw new Error("readPlaywrightConfig cannot be used in browser environments");
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
// Only initialize on server side
|
|
253
|
-
// This will only execute on the server
|
|
254
|
-
await import("tsx/cjs/api")
|
|
255
|
-
.then((module) => {
|
|
256
|
-
tsxImport = module;
|
|
257
|
-
})
|
|
258
|
-
.catch(() => {
|
|
259
|
-
console.warn("Failed to import tsx: --->");
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
if (!tsxImport) {
|
|
263
|
-
console.warn("tsx module not available");
|
|
264
|
-
return [];
|
|
265
|
-
}
|
|
266
|
-
const repoDir = process.cwd();
|
|
267
|
-
const [lastDir] = repoDir.split("/").reverse();
|
|
268
|
-
try {
|
|
269
|
-
const playwrightConfig = (await tsxImport.require(pwFile, `${repoDir}/${lastDir}`)).default;
|
|
270
|
-
return playwrightConfig.projects;
|
|
271
|
-
}
|
|
272
|
-
catch (err) {
|
|
273
|
-
console.error("Error getting project list from playwright config", err);
|
|
274
|
-
}
|
|
275
|
-
return [];
|
|
276
|
-
}
|
|
277
168
|
const filterArrayByGlobMatchersSet = (
|
|
278
169
|
// array that needs to be filtered
|
|
279
170
|
input,
|
|
@@ -289,7 +180,7 @@ globMatcherSets) => {
|
|
|
289
180
|
};
|
|
290
181
|
exports.filterArrayByGlobMatchersSet = filterArrayByGlobMatchersSet;
|
|
291
182
|
async function labelTeardownProjects(projectNames, platform) {
|
|
292
|
-
const allProjects = await getProjectsFromPlaywrightConfig(platform);
|
|
183
|
+
const allProjects = await (0, config_1.getProjectsFromPlaywrightConfig)(platform);
|
|
293
184
|
return projectNames.map((projectName) => {
|
|
294
185
|
const setupForTeardown = allProjects.find((p) => p.teardown === projectName);
|
|
295
186
|
if (setupForTeardown) {
|
|
@@ -307,7 +198,7 @@ async function labelTeardownProjects(projectNames, platform) {
|
|
|
307
198
|
});
|
|
308
199
|
}
|
|
309
200
|
const generateProjectFilters = async ({ platform, filteringSets, }) => {
|
|
310
|
-
const allProjects = await getProjectsFromPlaywrightConfig(platform);
|
|
201
|
+
const allProjects = await (0, config_1.getProjectsFromPlaywrightConfig)(platform);
|
|
311
202
|
const allProjectNames = allProjects.map((project) => project.name);
|
|
312
203
|
const filters = filteringSets.map((matchingString) => matchingString.split(","));
|
|
313
204
|
const filteredProjects = (0, exports.filterArrayByGlobMatchersSet)(allProjectNames, filters);
|
|
@@ -334,7 +225,7 @@ const downloadBuild = async (buildUrl) => {
|
|
|
334
225
|
const buildDownloadScript = packageJSONData.scripts["download"];
|
|
335
226
|
if (buildDownloadScript && buildUrl) {
|
|
336
227
|
console.log(`Downloading build from ${buildUrl}`);
|
|
337
|
-
await
|
|
228
|
+
await (0, cmd_1.spawnCmd)(`npm`, ["run", "download", buildUrl], {
|
|
338
229
|
env: { ...Object(process.env) },
|
|
339
230
|
});
|
|
340
231
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@empiricalrun/test-run",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/",
|
|
6
6
|
"access": "public"
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@types/async-retry": "^1.4.8",
|
|
27
|
-
"@types/node": "^22.5.5"
|
|
27
|
+
"@types/node": "^22.5.5",
|
|
28
|
+
"memfs": "^4.17.1"
|
|
28
29
|
},
|
|
29
30
|
"scripts": {
|
|
30
31
|
"dev": "tsc --build --watch",
|
package/tsconfig.tsbuildinfo
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./src/dashboard.ts","./src/index.ts","./src/bin/index.ts","./src/lib/run-all-tests.ts","./src/lib/run-specific-test.ts","./src/types/index.ts","./src/utils/config-parser.ts","./src/utils/index.ts"],"version":"5.8.3"}
|
|
1
|
+
{"root":["./src/dashboard.ts","./src/index.ts","./src/bin/index.ts","./src/lib/cmd.ts","./src/lib/run-all-tests.ts","./src/lib/run-specific-test.ts","./src/lib/memfs/read-hello-world.ts","./src/types/index.ts","./src/utils/config-parser.ts","./src/utils/config.ts","./src/utils/index.ts"],"version":"5.8.3"}
|