@empiricalrun/test-run 0.8.4 → 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 CHANGED
@@ -1,5 +1,22 @@
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
+
14
+ ## 0.8.5
15
+
16
+ ### Patch Changes
17
+
18
+ - 0634233: fix: run setup projects when test-run tool call has teardown project
19
+
3
20
  ## 0.8.4
4
21
 
5
22
  ### 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 = (await (0, config_parser_1.parseRcConfig)())?.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 testResult;
103
+ let commandToRun;
105
104
  if (hasTestsFilter) {
106
- testResult = await (0, run_specific_test_1.runSpecificTests)({
105
+ commandToRun = await (0, run_specific_test_1.runSpecificTestsCmd)({
107
106
  tests,
108
107
  projects: projectFilters,
109
- pwOptions: pwOptions.join(" "),
108
+ passthroughArgs: pwOptions.join(" "),
110
109
  platform,
111
110
  });
112
111
  }
113
112
  else {
114
- testResult = await (0, run_all_tests_1.runAllTests)({
113
+ commandToRun = (0, run_all_tests_1.runAllTestsCmd)({
115
114
  projects: projectFilters,
116
- pwOptions: pwOptions.join(" "),
115
+ passthroughArgs: pwOptions.join(" "),
117
116
  platform,
118
117
  });
119
118
  }
120
- if (!testResult.hasTestPassed) {
119
+ const { hasTestPassed } = await (0, cmd_1.runTestsForCmd)(commandToRun);
120
+ if (!hasTestPassed) {
121
121
  process.exit(1);
122
122
  }
123
123
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAK7C,eAAO,MAAM,wBAAwB,gBACtB,MAAM,mBACF,MAAM,KACtB,QAAQ;IACT,WAAW,EAAE,WAAW,CAAC;IACzB,KAAK,EAAE,KAAK,CAAC;CACd,CAuDA,CAAC"}
1
+ {"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAK7C,eAAO,MAAM,wBAAwB,GACnC,aAAa,MAAM,EACnB,iBAAiB,MAAM,KACtB,OAAO,CAAC;IACT,WAAW,EAAE,WAAW,CAAC;IACzB,KAAK,EAAE,KAAK,CAAC;CACd,CAuDA,CAAC"}
package/dist/dashboard.js CHANGED
@@ -33,7 +33,7 @@ const fetchEnvironmentAndBuild = async (projectName, environmentSlug) => {
33
33
  }, {
34
34
  retries: 3,
35
35
  minTimeout: 1000,
36
- maxTimeout: 60000,
36
+ maxTimeout: 60_000,
37
37
  factor: 3,
38
38
  });
39
39
  if (!data?.data) {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { TestCase } from "./types";
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
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAY,QAAQ,EAAE,MAAM,SAAS,CAAC;AAO7C,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;AAED,wBAAsB,YAAY,CAChC,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CA6CnC"}
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,74 +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.listAllTests = exports.runSingleTest = void 0;
6
+ exports.runSpecificTestsCmd = void 0;
7
+ exports.runSingleTest = runSingleTest;
7
8
  const promises_1 = __importDefault(require("fs/promises"));
8
9
  const path_1 = __importDefault(require("path"));
10
+ const cmd_1 = require("./lib/cmd");
9
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; } });
10
13
  const types_1 = require("./types");
11
- const utils_1 = require("./utils");
12
14
  // For test-run package, the library entrypoint, we only support web platform
13
15
  // The bin entrypoint has support for mobile also
14
16
  const supportedPlatform = types_1.Platform.WEB;
15
17
  async function runSingleTest({ testName, suites, fileName, projects, envOverrides, }) {
16
18
  const testDir = "tests";
17
19
  const filePath = path_1.default.relative(process.cwd(), fileName);
18
- const result = await (0, run_specific_test_1.runSpecificTests)({
20
+ const commandToRun = await (0, run_specific_test_1.runSpecificTestsCmd)({
19
21
  tests: [{ name: testName, dir: testDir, filePath, suites }],
20
- pwOptions: "",
21
22
  projects,
22
23
  envOverrides,
23
24
  platform: supportedPlatform,
24
25
  });
26
+ const { hasTestPassed } = await (0, cmd_1.runTestsForCmd)(commandToRun);
25
27
  const jsonFilePath = path_1.default.join(process.cwd(), "playwright-report", `summary.json`);
26
28
  const jsonFileContents = await promises_1.default.readFile(jsonFilePath, "utf8");
27
29
  const summaryJson = JSON.parse(jsonFileContents);
28
30
  return {
29
- hasTestPassed: result.hasTestPassed,
31
+ hasTestPassed,
30
32
  summaryJson,
31
33
  };
32
34
  }
33
- exports.runSingleTest = runSingleTest;
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
- }
76
- exports.listAllTests = listAllTests;
@@ -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"}
@@ -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,3 @@
1
+ export declare function readHelloWorld(path: string): string;
2
+ export declare function verifyDirectory(path: string): boolean;
3
+ //# sourceMappingURL=read-hello-world.d.ts.map
@@ -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 { TestRunParameters } from "../types";
2
- export declare function runAllTests({ projects, pwOptions, filesFilter, envOverrides, platform, }: TestRunParameters): Promise<{
3
- hasTestPassed: boolean;
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,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAG7C,wBAAsB,WAAW,CAAC,EAChC,QAAQ,EACR,SAAS,EACT,WAAW,EACX,YAAY,EACZ,QAAQ,GACT,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAC7B,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC,CAoBD"}
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,24 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.runAllTests = void 0;
3
+ exports.runAllTestsCmd = runAllTestsCmd;
4
4
  const utils_1 = require("../utils");
5
- async function runAllTests({ projects, pwOptions, filesFilter, envOverrides, platform, }) {
6
- let hasTestPassed = true;
7
- try {
8
- const testRunner = (0, utils_1.getTestRunner)(platform);
9
- let pwOptionsWithProjects = `${pwOptions} ${projects?.map((project) => `--project ${project}`).join(" ")}`;
10
- const env = Object.assign({ ...process.env, PW_TEST_HTML_REPORT_OPEN: "never" }, envOverrides);
11
- const testRunCmd = `npx ${testRunner} test ${filesFilter || ""} ${pwOptionsWithProjects}`;
12
- console.log(`Running cmd:`, testRunCmd);
13
- await (0, utils_1.cmd)(testRunCmd.split(" "), {
14
- env,
15
- });
16
- }
17
- catch (e) {
18
- hasTestPassed = false;
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
- hasTestPassed,
21
+ ...(0, cmd_1.getCommandFromString)(testRunCmd),
22
+ env,
22
23
  };
23
24
  }
24
- exports.runAllTests = runAllTests;
@@ -1,5 +1,10 @@
1
- import { TestRunParameters } from "../types";
2
- export declare function runSpecificTests({ tests, projects, pwOptions, platform, envOverrides, }: TestRunParameters): Promise<{
3
- hasTestPassed: boolean;
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":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAmB7C,wBAAsB,gBAAgB,CAAC,EACrC,KAAU,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,YAAY,GACb,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAC7B,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC,CA+ED"}
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,37 +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.runSpecificTests = void 0;
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 setupProcessListeners(cleanup) {
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
- const touchedFiles = {};
23
- // revert the changes made to the file to mark tests as only
24
- // these needs to handle by listening to process exit/kill events
25
- const revertFileContent = () => {
26
- try {
27
- Object.keys(touchedFiles).forEach((filePath) => {
28
- (0, fs_1.writeFileSync)(filePath, touchedFiles[filePath], "utf-8");
29
- });
30
- }
31
- catch (error) {
32
- console.error("Error while cleaning up test file:", error);
33
- }
34
- };
10
+ let patternsToGrep = [];
11
+ let filesToRun = [];
35
12
  for (const testCase of tests) {
36
13
  const files = await (0, utils_1.getAllFilePaths)(testCase.dir, {
37
14
  filePath: testCase.filePath,
@@ -49,42 +26,55 @@ async function runSpecificTests({ tests = [], projects, pwOptions, platform, env
49
26
  }
50
27
  }
51
28
  if (!matchingFilePath) {
52
- const message = `No test block found for the given test name: ${testCase.name} in file ${testCase.filePath} with suites: ${testCase.suites}`;
53
- revertFileContent();
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}"`;
54
34
  throw Error(message);
55
35
  }
56
- const { testCaseNode, sourceFile } = await (0, utils_1.getTestCaseNode)({
36
+ else {
37
+ filesToRun.push(matchingFilePath);
38
+ }
39
+ const { testCaseNode } = await (0, utils_1.getTestCaseNode)({
57
40
  filePath: matchingFilePath,
58
41
  scenarioName: testCase.name,
59
42
  suites: testCase.suites,
60
43
  });
61
- const parentDescribe = (0, utils_1.findFirstSerialDescribeBlock)(testCaseNode);
62
44
  const isFileMarkedSerial = await (0, utils_1.hasTopLevelDescribeConfigureWithSerialMode)(matchingFilePath);
63
- const currentFileContent = await promises_1.default.readFile(matchingFilePath, "utf-8");
64
- // if file is not marked as touched, mark it as touched
65
- if (!touchedFiles[matchingFilePath]) {
66
- touchedFiles[matchingFilePath] = currentFileContent;
67
- }
68
- // if the file is not marked serial, we need to mark the test or describe block as only
69
45
  if (!isFileMarkedSerial && testCaseNode) {
70
- await (0, utils_1.markTestAsOnly)({
71
- sourceFile,
72
- parentDescribeNode: parentDescribe,
73
- testCaseNode: testCaseNode,
74
- filePath: matchingFilePath,
75
- });
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
+ }
76
56
  }
77
57
  }
78
- const removeListeners = setupProcessListeners(revertFileContent);
79
- const result = await (0, run_all_tests_1.runAllTests)({
80
- filesFilter: Object.keys(touchedFiles).join(" "),
81
- pwOptions,
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,
82
74
  projects,
83
75
  envOverrides,
84
76
  platform,
77
+ patternsToGrep,
85
78
  });
86
- removeListeners();
87
- revertFileContent();
88
- return result;
79
+ return commandToRun;
89
80
  }
90
- exports.runSpecificTests = runSpecificTests;
@@ -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,GACd;IACE,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,GACD,SAAS,CAAC;AAEd,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC,CAAC;AAEF,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"}
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,5 +1,4 @@
1
1
  import { Config } from "../types";
2
- export declare function parseRcConfig(): Promise<Config>;
3
2
  export declare function parseToken(token: string): Config;
4
3
  export declare function buildConfigFromCliArgs(): void;
5
4
  //# sourceMappingURL=config-parser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-parser.d.ts","sourceRoot":"","sources":["../../src/utils/config-parser.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAerD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAchD;AAED,wBAAgB,sBAAsB,SAAK"}
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,28 +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.buildConfigFromCliArgs = exports.parseToken = exports.parseRcConfig = void 0;
7
- const fs_1 = require("fs");
8
- const promises_1 = __importDefault(require("fs/promises"));
9
- async function parseRcConfig() {
10
- // check if file exists
11
- if (!(0, fs_1.existsSync)("empiricalrc.json")) {
12
- return undefined;
13
- }
14
- try {
15
- const rcConfig = await promises_1.default.readFile("empiricalrc.json", "utf-8");
16
- const json = JSON.parse(rcConfig);
17
- // TODO: validate the json
18
- return json;
19
- }
20
- catch (e) {
21
- console.error("Error parsing rc file:", e);
22
- }
23
- return undefined;
24
- }
25
- exports.parseRcConfig = parseRcConfig;
3
+ exports.parseToken = parseToken;
4
+ exports.buildConfigFromCliArgs = buildConfigFromCliArgs;
26
5
  function parseToken(token) {
27
6
  if (!token) {
28
7
  return undefined;
@@ -39,6 +18,4 @@ function parseToken(token) {
39
18
  }
40
19
  return undefined;
41
20
  }
42
- exports.parseToken = parseToken;
43
21
  function buildConfigFromCliArgs() { }
44
- exports.buildConfigFromCliArgs = buildConfigFromCliArgs;
@@ -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
+ }
@@ -1,26 +1,5 @@
1
1
  import { Node, SourceFile } from "ts-morph";
2
2
  import { Platform, TestFramework } from "../types";
3
- export declare function cmd(command: string[], options: {
4
- env?: Record<string, string>;
5
- cwd?: string;
6
- captureOutput?: boolean;
7
- }): Promise<number | {
8
- code: number;
9
- output: string;
10
- }>;
11
- export declare function cmdWithOutput(command: string[], options: {
12
- env?: Record<string, string>;
13
- cwd?: string;
14
- }): Promise<{
15
- code: number;
16
- output: string;
17
- }>;
18
- /**
19
- *
20
- *
21
- * @param {string} [directoryPath=""]
22
- * @return {*} {Promise<string[]>}
23
- */
24
3
  export declare function getAllFilePaths(directoryPath?: string, filters?: {
25
4
  filePath?: string;
26
5
  }): Promise<string[]>;
@@ -38,6 +17,7 @@ export declare function hasTestBlock({ filePath, scenarioName, suites, }: {
38
17
  scenarioName: string;
39
18
  suites?: string[];
40
19
  }): Promise<boolean>;
20
+ export declare function getDescribeBlockName(node: Node): string | undefined;
41
21
  export declare function findFirstSerialDescribeBlock(node: Node | undefined): Node | undefined;
42
22
  export declare function hasTopLevelDescribeConfigureWithSerialMode(filePath: string): Promise<boolean>;
43
23
  export declare function markTestAsOnly({ sourceFile, parentDescribeNode, testCaseNode, filePath, }: {
@@ -46,8 +26,11 @@ export declare function markTestAsOnly({ sourceFile, parentDescribeNode, testCas
46
26
  testCaseNode: Node;
47
27
  filePath: string;
48
28
  }): Promise<void>;
49
- export declare function getProjectsFromPlaywrightConfig(platform: Platform): Promise<any>;
50
29
  export declare const filterArrayByGlobMatchersSet: (input: string[], globMatcherSets: string[][]) => string[];
30
+ export declare function labelTeardownProjects(projectNames: string[], platform: Platform): Promise<{
31
+ isTeardown: boolean;
32
+ correspondingSetupProject: string | undefined;
33
+ }[]>;
51
34
  export declare const generateProjectFilters: ({ platform, filteringSets, }: {
52
35
  platform: Platform;
53
36
  filteringSets: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"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"}
@@ -3,104 +3,26 @@ 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.getTypescriptTestBlock = exports.handleTeardownSkipFlag = exports.getTestRunner = exports.downloadBuild = exports.pickNameFromPackageJson = exports.buildRepoName = exports.generateProjectFilters = exports.filterArrayByGlobMatchersSet = exports.getProjectsFromPlaywrightConfig = exports.markTestAsOnly = exports.hasTopLevelDescribeConfigureWithSerialMode = exports.findFirstSerialDescribeBlock = exports.hasTestBlock = exports.getTestCaseNode = exports.getTestModuleAliasFromSourceFile = exports.getAllFilePaths = exports.cmdWithOutput = exports.cmd = void 0;
7
- const child_process_1 = require("child_process");
6
+ exports.handleTeardownSkipFlag = exports.getTestRunner = exports.downloadBuild = exports.pickNameFromPackageJson = exports.generateProjectFilters = exports.filterArrayByGlobMatchersSet = exports.getTestModuleAliasFromSourceFile = void 0;
7
+ exports.getAllFilePaths = getAllFilePaths;
8
+ exports.getTestCaseNode = getTestCaseNode;
9
+ exports.hasTestBlock = hasTestBlock;
10
+ exports.getDescribeBlockName = getDescribeBlockName;
11
+ exports.findFirstSerialDescribeBlock = findFirstSerialDescribeBlock;
12
+ exports.hasTopLevelDescribeConfigureWithSerialMode = hasTopLevelDescribeConfigureWithSerialMode;
13
+ exports.markTestAsOnly = markTestAsOnly;
14
+ exports.labelTeardownProjects = labelTeardownProjects;
15
+ exports.buildRepoName = buildRepoName;
16
+ exports.getTypescriptTestBlock = getTypescriptTestBlock;
8
17
  const fs_1 = require("fs");
9
18
  const promises_1 = __importDefault(require("fs/promises"));
10
19
  const minimatch_1 = require("minimatch");
11
20
  const path_1 = __importDefault(require("path"));
12
21
  const ts_morph_1 = require("ts-morph");
13
- // For TypeScript type safety
14
- let tsxImport = null;
15
22
  const util_1 = require("util");
23
+ const cmd_1 = require("../lib/cmd");
16
24
  const types_1 = require("../types");
17
- function setupProcessSignalHandlers(proc) {
18
- const handleSignal = async (signal) => {
19
- console.log(`\nReceived ${signal}, gracefully shutting down...`);
20
- if (proc && !proc.killed) {
21
- // Forward the signal to the child process
22
- proc.kill(signal);
23
- // Wait for the child process to exit
24
- await new Promise((resolve) => {
25
- proc.once("exit", () => {
26
- resolve();
27
- });
28
- });
29
- }
30
- process.exit(0);
31
- };
32
- const sigintHandler = async () => await handleSignal("SIGINT");
33
- const sigtermHandler = async () => await handleSignal("SIGTERM");
34
- const otherSignalHandlers = ["SIGABRT", "SIGHUP", "SIGQUIT"].map((signal) => {
35
- const handler = () => console.log(`Received ${signal}, which is a no-op`);
36
- process.once(signal, handler);
37
- return { signal, handler };
38
- });
39
- process.once("SIGINT", sigintHandler);
40
- process.once("SIGTERM", sigtermHandler);
41
- // Return a cleanup function that removes all signal handlers
42
- return () => {
43
- process.removeListener("SIGINT", sigintHandler);
44
- process.removeListener("SIGTERM", sigtermHandler);
45
- otherSignalHandlers.forEach(({ signal, handler }) => {
46
- process.removeListener(signal, handler);
47
- });
48
- };
49
- }
50
- async function cmd(command, options) {
51
- let output = options.captureOutput ? "" : undefined;
52
- let errorLogs = [];
53
- return new Promise((resolveFunc, rejectFunc) => {
54
- const p = (0, child_process_1.spawn)(command[0], command.slice(1), {
55
- env: { ...process.env, ...options.env },
56
- cwd: options.cwd,
57
- // Ensure child process receives signals
58
- detached: false,
59
- });
60
- // Setup signal handlers and get cleanup function
61
- const cleanupSignalHandlers = setupProcessSignalHandlers(p);
62
- p.stdout.on("data", (x) => {
63
- const log = x.toString();
64
- if (options.captureOutput) {
65
- output += log;
66
- }
67
- else {
68
- process.stdout.write(log);
69
- }
70
- if (log.includes("Error")) {
71
- errorLogs.push(log);
72
- }
73
- });
74
- p.stderr.on("data", (x) => {
75
- const log = x.toString();
76
- process.stderr.write(log);
77
- errorLogs.push(log);
78
- });
79
- p.on("exit", (code) => {
80
- // Clean up signal handlers when the process exits
81
- cleanupSignalHandlers();
82
- if (code != 0) {
83
- // assuming last log is the error message before exiting
84
- rejectFunc(errorLogs.slice(-3).join("\n"));
85
- }
86
- else {
87
- resolveFunc(options.captureOutput ? { code: code, output: output } : code);
88
- }
89
- });
90
- });
91
- }
92
- exports.cmd = cmd;
93
- async function cmdWithOutput(command, options) {
94
- const result = await cmd(command, { ...options, captureOutput: true });
95
- return result;
96
- }
97
- exports.cmdWithOutput = cmdWithOutput;
98
- /**
99
- *
100
- *
101
- * @param {string} [directoryPath=""]
102
- * @return {*} {Promise<string[]>}
103
- */
25
+ const config_1 = require("./config");
104
26
  async function getAllFilePaths(directoryPath = "tests", filters = {}) {
105
27
  let filePaths = [];
106
28
  try {
@@ -131,7 +53,6 @@ async function getAllFilePaths(directoryPath = "tests", filters = {}) {
131
53
  }
132
54
  return filePaths;
133
55
  }
134
- exports.getAllFilePaths = getAllFilePaths;
135
56
  const getTestModuleAliasFromSourceFile = (sourceFile) => {
136
57
  return (sourceFile
137
58
  .getImportDeclarations()
@@ -155,7 +76,6 @@ async function getTestCaseNode({ filePath, scenarioName, suites, }) {
155
76
  });
156
77
  return { testCaseNode: testNode, sourceFile };
157
78
  }
158
- exports.getTestCaseNode = getTestCaseNode;
159
79
  async function hasTestBlock({ filePath, scenarioName, suites, }) {
160
80
  const { testCaseNode } = await getTestCaseNode({
161
81
  filePath,
@@ -164,7 +84,18 @@ async function hasTestBlock({ filePath, scenarioName, suites, }) {
164
84
  });
165
85
  return !!testCaseNode;
166
86
  }
167
- exports.hasTestBlock = hasTestBlock;
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
+ }
168
99
  function findFirstSerialDescribeBlock(node) {
169
100
  let currentNode = node;
170
101
  // Traverse upwards until we find a 'describe' block with 'serial: true'
@@ -189,7 +120,6 @@ function findFirstSerialDescribeBlock(node) {
189
120
  }
190
121
  return undefined; // Return undefined if no 'describe' with serial: true is found
191
122
  }
192
- exports.findFirstSerialDescribeBlock = findFirstSerialDescribeBlock;
193
123
  async function hasTopLevelDescribeConfigureWithSerialMode(filePath) {
194
124
  const project = new ts_morph_1.Project();
195
125
  const content = await promises_1.default.readFile(filePath, "utf-8");
@@ -217,7 +147,6 @@ async function hasTopLevelDescribeConfigureWithSerialMode(filePath) {
217
147
  }
218
148
  return false;
219
149
  }
220
- exports.hasTopLevelDescribeConfigureWithSerialMode = hasTopLevelDescribeConfigureWithSerialMode;
221
150
  async function markTestAsOnly({ sourceFile, parentDescribeNode, testCaseNode, filePath, }) {
222
151
  let updatedTestFileContent = sourceFile.getFullText();
223
152
  if (!parentDescribeNode) {
@@ -236,42 +165,6 @@ async function markTestAsOnly({ sourceFile, parentDescribeNode, testCaseNode, fi
236
165
  }
237
166
  await promises_1.default.writeFile(filePath, updatedTestFileContent, "utf-8");
238
167
  }
239
- exports.markTestAsOnly = markTestAsOnly;
240
- async function getProjectsFromPlaywrightConfig(platform) {
241
- const configName = platform === types_1.Platform.WEB ? "playwright.config.ts" : "appwright.config.ts";
242
- const directoryPath = ".";
243
- const pwFile = path_1.default.resolve(directoryPath, configName);
244
- if (typeof window !== "undefined") {
245
- throw new Error("readPlaywrightConfig cannot be used in browser environments");
246
- }
247
- else {
248
- // Only initialize on server side
249
- // This will only execute on the server
250
- await import("tsx/cjs/api")
251
- .then((module) => {
252
- tsxImport = module;
253
- })
254
- .catch(() => {
255
- console.warn("Failed to import tsx: --->");
256
- });
257
- }
258
- if (!tsxImport) {
259
- console.warn("tsx module not available");
260
- return [];
261
- }
262
- const repoDir = process.cwd();
263
- const [lastDir] = repoDir.split("/").reverse();
264
- try {
265
- const playwrightConfig = (await tsxImport.require(pwFile, `${repoDir}/${lastDir}`)).default;
266
- const projectConfig = playwrightConfig.projects;
267
- return projectConfig.map((projectConfig) => projectConfig.name);
268
- }
269
- catch (err) {
270
- console.error("Error getting project list from playwright config", err);
271
- }
272
- return [];
273
- }
274
- exports.getProjectsFromPlaywrightConfig = getProjectsFromPlaywrightConfig;
275
168
  const filterArrayByGlobMatchersSet = (
276
169
  // array that needs to be filtered
277
170
  input,
@@ -286,10 +179,29 @@ globMatcherSets) => {
286
179
  return filteredList;
287
180
  };
288
181
  exports.filterArrayByGlobMatchersSet = filterArrayByGlobMatchersSet;
182
+ async function labelTeardownProjects(projectNames, platform) {
183
+ const allProjects = await (0, config_1.getProjectsFromPlaywrightConfig)(platform);
184
+ return projectNames.map((projectName) => {
185
+ const setupForTeardown = allProjects.find((p) => p.teardown === projectName);
186
+ if (setupForTeardown) {
187
+ return {
188
+ isTeardown: true,
189
+ correspondingSetupProject: setupForTeardown.name,
190
+ };
191
+ }
192
+ else {
193
+ return {
194
+ isTeardown: false,
195
+ correspondingSetupProject: undefined,
196
+ };
197
+ }
198
+ });
199
+ }
289
200
  const generateProjectFilters = async ({ platform, filteringSets, }) => {
290
- const projectsDefinedInPwConfig = await getProjectsFromPlaywrightConfig(platform);
201
+ const allProjects = await (0, config_1.getProjectsFromPlaywrightConfig)(platform);
202
+ const allProjectNames = allProjects.map((project) => project.name);
291
203
  const filters = filteringSets.map((matchingString) => matchingString.split(","));
292
- const filteredProjects = (0, exports.filterArrayByGlobMatchersSet)(projectsDefinedInPwConfig, filters);
204
+ const filteredProjects = (0, exports.filterArrayByGlobMatchersSet)(allProjectNames, filters);
293
205
  if (filteredProjects.length === 0) {
294
206
  throw new Error("No projects found in playwright config that matches the filtering criteria");
295
207
  }
@@ -299,7 +211,6 @@ exports.generateProjectFilters = generateProjectFilters;
299
211
  function buildRepoName(projectName) {
300
212
  return `${projectName}-tests`;
301
213
  }
302
- exports.buildRepoName = buildRepoName;
303
214
  const pickNameFromPackageJson = async () => {
304
215
  const packageJSONPath = "package.json";
305
216
  const packageJsonStr = await promises_1.default.readFile(packageJSONPath, "utf-8");
@@ -314,7 +225,7 @@ const downloadBuild = async (buildUrl) => {
314
225
  const buildDownloadScript = packageJSONData.scripts["download"];
315
226
  if (buildDownloadScript && buildUrl) {
316
227
  console.log(`Downloading build from ${buildUrl}`);
317
- await cmd(`npm run download ${buildUrl}`.split(" "), {
228
+ await (0, cmd_1.spawnCmd)(`npm`, ["run", "download", buildUrl], {
318
229
  env: { ...Object(process.env) },
319
230
  });
320
231
  }
@@ -459,4 +370,3 @@ function getTypescriptTestBlock({ scenarioName, suites, content, }) {
459
370
  sourceFile,
460
371
  };
461
372
  }
462
- exports.getTypescriptTestBlock = getTypescriptTestBlock;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empiricalrun/test-run",
3
- "version": "0.8.4",
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",
@@ -0,0 +1 @@
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"}