@empiricalrun/test-run 0.4.6 → 0.5.2

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,24 @@
1
1
  # @empiricalrun/test-run
2
2
 
3
+ ## 0.5.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 7332043: feat: pass signals to test-run child process for graceful exit
8
+
9
+ ## 0.5.1
10
+
11
+ ### Patch Changes
12
+
13
+ - bce3667: fix: use name and suites to find test block
14
+ - bce3667: fix: test-run package to support suites
15
+
16
+ ## 0.5.0
17
+
18
+ ### Minor Changes
19
+
20
+ - 0c445bf: feat: add support for running specific file in test-run package
21
+
3
22
  ## 0.4.6
4
23
 
5
24
  ### Patch Changes
package/dist/bin/index.js CHANGED
@@ -11,8 +11,10 @@ const utils_1 = require("../utils");
11
11
  commander_1.program
12
12
  .option("-n, --name <test-name>", "Name of the test to run")
13
13
  .option("-d, --dir <test-dir>", "Path to the test directory")
14
+ .option("-f, --file <test-file-path>", "Path to the test file")
14
15
  .option("-p, --project <project-name...>", "Test projects to run")
15
- .option("--skip-teardown", `This options skips running teardown tests`)
16
+ .option("-s, --suites <suites>", "suites under which the test is defined")
17
+ .option("--skip-teardown", "This options skips running teardown tests")
16
18
  .option("--forbid-only", `This options forbids the use of ".only" in the test files`)
17
19
  .allowUnknownOption();
18
20
  commander_1.program.parse(process.argv);
@@ -27,13 +29,19 @@ const utils_1 = require("../utils");
27
29
  "-n",
28
30
  "--name",
29
31
  "--skip-teardown",
32
+ "-f",
33
+ "--file",
30
34
  "-d",
31
35
  "--dir",
32
36
  "-p",
33
37
  "--project",
38
+ "-s",
39
+ "--suites",
34
40
  options.skipTeardown,
35
41
  options.name,
36
42
  options.dir,
43
+ options.file,
44
+ options.suites,
37
45
  ...options.project, // an array of comma separated project names/pattern matching globs *,premium-*,super-premium-*,safari
38
46
  ];
39
47
  const pwOptions = process.argv
@@ -65,11 +73,16 @@ const utils_1 = require("../utils");
65
73
  if (options.skipTeardown) {
66
74
  await (0, utils_1.handleTeardownSkipFlag)(directory);
67
75
  }
76
+ const suites = options.suites && options.suites.trim() !== ""
77
+ ? options.suites?.split(",")
78
+ : undefined;
68
79
  const { hasTestPassed } = await (0, __1.runTest)({
69
80
  name: options.name,
81
+ filePath: options.file,
70
82
  dir: directory,
71
83
  pwOptions: pwOptions.join(" "),
72
84
  platform,
85
+ suites,
73
86
  });
74
87
  if (!hasTestPassed) {
75
88
  process.exit(1);
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ import { TestRunParameters } from "./types";
4
4
  * @export
5
5
  * @param {TestRunParameters} { name, dir, pwOptions }
6
6
  */
7
- export declare function runTest({ name, dir, pwOptions, platform, }: TestRunParameters): Promise<{
7
+ export declare function runTest({ name, filePath, dir, pwOptions, platform, suites, }: TestRunParameters): Promise<{
8
8
  hasTestPassed: boolean;
9
9
  }>;
10
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE5C;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,EAC5B,IAAI,EACJ,GAAG,EACH,SAAS,EACT,QAAQ,GACT,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAC7B,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC,CAMD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE5C;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,EAC5B,IAAI,EACJ,QAAQ,EACR,GAAG,EACH,SAAS,EACT,QAAQ,EACR,MAAM,GACP,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAC7B,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC,CAaD"}
package/dist/index.js CHANGED
@@ -8,9 +8,16 @@ const run_specific_test_1 = require("./run-specific-test");
8
8
  * @export
9
9
  * @param {TestRunParameters} { name, dir, pwOptions }
10
10
  */
11
- async function runTest({ name, dir, pwOptions, platform, }) {
11
+ async function runTest({ name, filePath, dir, pwOptions, platform, suites, }) {
12
12
  if (name) {
13
- return await (0, run_specific_test_1.runSpecificTest)({ name, dir, pwOptions, platform });
13
+ return await (0, run_specific_test_1.runSpecificTest)({
14
+ name,
15
+ dir,
16
+ filePath,
17
+ pwOptions,
18
+ platform,
19
+ suites,
20
+ });
14
21
  }
15
22
  return await (0, run_all_tests_1.runAllTests)({ pwOptions, platform });
16
23
  }
@@ -4,7 +4,7 @@ import { TestRunParameters } from "./types";
4
4
  * @export
5
5
  * @param {TestRunParameters} { name, dir, pwOptions }
6
6
  */
7
- export declare function runSpecificTest({ name, dir, pwOptions, platform, }: TestRunParameters): Promise<{
7
+ export declare function runSpecificTest({ name, filePath, dir, pwOptions, platform, suites, }: TestRunParameters): Promise<{
8
8
  hasTestPassed: boolean;
9
9
  }>;
10
10
  //# sourceMappingURL=run-specific-test.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"run-specific-test.d.ts","sourceRoot":"","sources":["../src/run-specific-test.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAY5C;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,EACpC,IAAI,EACJ,GAAG,EACH,SAAS,EACT,QAAQ,GACT,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAC7B,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC,CAwED"}
1
+ {"version":3,"file":"run-specific-test.d.ts","sourceRoot":"","sources":["../src/run-specific-test.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAY5C;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,EACpC,IAAI,EACJ,QAAQ,EACR,GAAG,EACH,SAAS,EACT,QAAQ,EACR,MAAM,GACP,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAC7B,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC,CA2ED"}
@@ -11,13 +11,19 @@ const utils_1 = require("./utils");
11
11
  * @export
12
12
  * @param {TestRunParameters} { name, dir, pwOptions }
13
13
  */
14
- async function runSpecificTest({ name, dir, pwOptions, platform, }) {
15
- const files = await (0, utils_1.getAllFilePaths)(dir);
14
+ async function runSpecificTest({ name, filePath, dir, pwOptions, platform, suites, }) {
15
+ const files = await (0, utils_1.getAllFilePaths)(dir, {
16
+ filePath,
17
+ });
16
18
  let matchingFilePath = "";
17
19
  // find the first file that contains the test block
18
20
  // TODO: add suites support
19
21
  for (const file of files) {
20
- const match = await (0, utils_1.hasTestBlock)({ filePath: file, scenarioName: name });
22
+ const match = await (0, utils_1.hasTestBlock)({
23
+ filePath: file,
24
+ scenarioName: name,
25
+ suites,
26
+ });
21
27
  if (match) {
22
28
  matchingFilePath = file;
23
29
  break;
@@ -30,6 +36,7 @@ async function runSpecificTest({ name, dir, pwOptions, platform, }) {
30
36
  const { testCaseNode, sourceFile } = await (0, utils_1.getTestCaseNode)({
31
37
  filePath: matchingFilePath,
32
38
  scenarioName: name,
39
+ suites,
33
40
  });
34
41
  const parentDescribe = (0, utils_1.findFirstSerialDescribeBlock)(testCaseNode);
35
42
  const isFileMarkedSerial = await (0, utils_1.hasTopLevelDescribeConfigureWithSerialMode)(matchingFilePath);
@@ -37,23 +44,20 @@ async function runSpecificTest({ name, dir, pwOptions, platform, }) {
37
44
  console.log("Is parent describe block marked serial:", !!parentDescribe);
38
45
  console.log("Is file marked serial:", isFileMarkedSerial);
39
46
  const currentFileContent = await fs_extra_1.default.readFile(matchingFilePath, "utf-8");
40
- const revertFileContent = () => {
41
- fs_extra_1.default.writeFileSync(matchingFilePath, currentFileContent);
42
- };
43
47
  // revert the changes made to the file to mark tests as only
44
48
  // these needs to handle by listening to process exit/kill events
45
- process.on("beforeExit", () => {
46
- revertFileContent();
47
- });
48
- process.on("exit", () => {
49
- revertFileContent();
50
- });
51
- process.on("SIGINT", () => {
52
- revertFileContent();
53
- });
54
- process.on("SIGTERM", () => {
55
- revertFileContent();
56
- });
49
+ const revertFileContent = () => {
50
+ try {
51
+ fs_extra_1.default.writeFileSync(matchingFilePath, currentFileContent);
52
+ }
53
+ catch (error) {
54
+ console.error("Error while cleaning up test file:", error);
55
+ }
56
+ };
57
+ process.on("beforeExit", revertFileContent);
58
+ process.on("exit", revertFileContent);
59
+ process.on("SIGINT", revertFileContent);
60
+ process.on("SIGTERM", revertFileContent);
57
61
  // if the file is not marked serial, we need to mark the test or describe block as only
58
62
  if (!isFileMarkedSerial && testCaseNode) {
59
63
  await (0, utils_1.markTestAsOnly)({
@@ -1,8 +1,10 @@
1
1
  export type TestRunParameters = {
2
2
  name: string;
3
+ filePath?: string;
3
4
  dir?: string;
4
5
  pwOptions?: string;
5
6
  platform: Platform;
7
+ suites?: string[];
6
8
  };
7
9
  export type Environment = {
8
10
  id: number;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,QAAQ,CAAC;CACpB,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,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,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"}
@@ -9,18 +9,22 @@ export declare function cmd(command: string[], options: {
9
9
  * @param {string} [directoryPath=""]
10
10
  * @return {*} {Promise<string[]>}
11
11
  */
12
- export declare function getAllFilePaths(directoryPath?: string): Promise<string[]>;
12
+ export declare function getAllFilePaths(directoryPath?: string, filters?: {
13
+ filePath?: string;
14
+ }): Promise<string[]>;
13
15
  export declare const getTestModuleAliasFromSourceFile: (sourceFile: SourceFile) => string;
14
- export declare function getTestCaseNode({ filePath, scenarioName, }: {
16
+ export declare function getTestCaseNode({ filePath, scenarioName, suites, }: {
15
17
  filePath: string;
16
18
  scenarioName: string;
19
+ suites?: string[];
17
20
  }): Promise<{
18
21
  testCaseNode: Node | undefined;
19
22
  sourceFile: SourceFile;
20
23
  }>;
21
- export declare function hasTestBlock({ filePath, scenarioName, }: {
24
+ export declare function hasTestBlock({ filePath, scenarioName, suites, }: {
22
25
  filePath: string;
23
26
  scenarioName: string;
27
+ suites?: string[];
24
28
  }): Promise<boolean>;
25
29
  export declare function findFirstSerialDescribeBlock(node: Node | undefined): Node | undefined;
26
30
  export declare function hasTopLevelDescribeConfigureWithSerialMode(filePath: string): Promise<boolean>;
@@ -41,4 +45,21 @@ export declare const pickNameFromPackageJson: () => Promise<string | undefined>;
41
45
  export declare const downloadBuild: (buildUrl: string) => Promise<void>;
42
46
  export declare const getTestRunner: (platform: Platform) => TestFramework;
43
47
  export declare const handleTeardownSkipFlag: (directory: string) => Promise<void>;
48
+ /**
49
+ * function to get the test block and test node for the scenario
50
+ * @export
51
+ * @param {string} scenarioName
52
+ * @param {string} content
53
+ * @return { testBlock: string; parentDescribe: string; } testBlock - the test block content, testNode - the test function node
54
+ */
55
+ export declare function getTypescriptTestBlock({ scenarioName, suites, content, }: {
56
+ scenarioName: string;
57
+ suites?: string[];
58
+ content: string;
59
+ }): {
60
+ testBlock: string | undefined;
61
+ testNode: Node | undefined;
62
+ testAlias: string;
63
+ sourceFile: SourceFile;
64
+ };
44
65
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAW,UAAU,EAAc,MAAM,UAAU,CAAC;AAGjE,OAAO,EAEL,QAAQ,EAER,aAAa,EACd,MAAM,UAAU,CAAC;AAElB,wBAAgB,GAAG,CACjB,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACxC,OAAO,CAAC,MAAM,CAAC,CA2BjB;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,aAAa,GAAE,MAAW,GACzB,OAAO,CAAC,MAAM,EAAE,CAAC,CAqBnB;AAED,eAAO,MAAM,gCAAgC,eAC/B,UAAU,KACrB,MAgBF,CAAC;AAEF,wBAAsB,eAAe,CAAC,EACpC,QAAQ,EACR,YAAY,GACb,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC;IAAE,YAAY,EAAE,IAAI,GAAG,SAAS,CAAC;IAAC,UAAU,EAAE,UAAU,CAAA;CAAE,CAAC,CAetE;AAED,wBAAsB,YAAY,CAAC,EACjC,QAAQ,EACR,YAAY,GACb,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,OAAO,CAAC,CAMnB;AAED,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,IAAI,GAAG,SAAS,GACrB,IAAI,GAAG,SAAS,CA2BlB;AAED,wBAAsB,0CAA0C,CAC9D,QAAQ,EAAE,MAAM,oBA+BjB;AAED,wBAAsB,cAAc,CAAC,EACnC,UAAU,EACV,kBAAkB,EAClB,YAAY,EACZ,QAAQ,GACT,EAAE;IACD,UAAU,EAAE,UAAU,CAAC;IACvB,kBAAkB,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IACtC,YAAY,EAAE,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB,iBAgBA;AAED,wBAAsB,+BAA+B,CAAC,QAAQ,EAAE,QAAQ,gBAevE;AAED,eAAO,MAAM,4BAA4B,UAEhC,MAAM,EAAE,mBAGE,MAAM,EAAE,EAAE,KAC1B,MAAM,EAUR,CAAC;AAEF,eAAO,MAAM,sBAAsB;cAIvB,QAAQ;mBACH,MAAM,EAAE;MACrB,QAAQ,MAAM,EAAE,CAmBnB,CAAC;AAEF,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,eAAO,MAAM,uBAAuB,QAAa,QAC/C,MAAM,GAAG,SAAS,CAMnB,CAAC;AAEF,eAAO,MAAM,aAAa,aAAoB,MAAM,KAAG,QAAQ,IAAI,CAWlE,CAAC;AAEF,eAAO,MAAM,aAAa,aAAc,QAAQ,KAAG,aAIlD,CAAC;AA4DF,eAAO,MAAM,sBAAsB,cAAqB,MAAM,kBAkB7D,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,IAAI,EAAW,UAAU,EAAc,MAAM,UAAU,CAAC;AAGjE,OAAO,EAEL,QAAQ,EAER,aAAa,EACd,MAAM,UAAU,CAAC;AAuBlB,wBAAgB,GAAG,CACjB,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACxC,OAAO,CAAC,MAAM,CAAC,CAiCjB;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,aAAa,GAAE,MAAW,EAC1B,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC,MAAM,EAAE,CAAC,CA4BnB;AAED,eAAO,MAAM,gCAAgC,eAC/B,UAAU,KACrB,MAgBF,CAAC;AAEF,wBAAsB,eAAe,CAAC,EACpC,QAAQ,EACR,YAAY,EACZ,MAAM,GACP,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,GAAG,OAAO,CAAC;IAAE,YAAY,EAAE,IAAI,GAAG,SAAS,CAAC;IAAC,UAAU,EAAE,UAAU,CAAA;CAAE,CAAC,CAQtE;AAED,wBAAsB,YAAY,CAAC,EACjC,QAAQ,EACR,YAAY,EACZ,MAAM,GACP,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,GAAG,OAAO,CAAC,OAAO,CAAC,CAOnB;AAED,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,IAAI,GAAG,SAAS,GACrB,IAAI,GAAG,SAAS,CA2BlB;AAED,wBAAsB,0CAA0C,CAC9D,QAAQ,EAAE,MAAM,oBA+BjB;AAED,wBAAsB,cAAc,CAAC,EACnC,UAAU,EACV,kBAAkB,EAClB,YAAY,EACZ,QAAQ,GACT,EAAE;IACD,UAAU,EAAE,UAAU,CAAC;IACvB,kBAAkB,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IACtC,YAAY,EAAE,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB,iBAgBA;AAED,wBAAsB,+BAA+B,CAAC,QAAQ,EAAE,QAAQ,gBAevE;AAED,eAAO,MAAM,4BAA4B,UAEhC,MAAM,EAAE,mBAGE,MAAM,EAAE,EAAE,KAC1B,MAAM,EAUR,CAAC;AAEF,eAAO,MAAM,sBAAsB;cAIvB,QAAQ;mBACH,MAAM,EAAE;MACrB,QAAQ,MAAM,EAAE,CAmBnB,CAAC;AAEF,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,eAAO,MAAM,uBAAuB,QAAa,QAC/C,MAAM,GAAG,SAAS,CAMnB,CAAC;AAEF,eAAO,MAAM,aAAa,aAAoB,MAAM,KAAG,QAAQ,IAAI,CAWlE,CAAC;AAEF,eAAO,MAAM,aAAa,aAAc,QAAQ,KAAG,aAIlD,CAAC;AA4DF,eAAO,MAAM,sBAAsB,cAAqB,MAAM,kBAkB7D,CAAC;AAuBF;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,YAAY,EACZ,MAAM,EACN,OAAO,GACR,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG;IACF,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,QAAQ,EAAE,IAAI,GAAG,SAAS,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,UAAU,CAAC;CACxB,CA8CA"}
@@ -3,20 +3,43 @@ 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.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.cmd = void 0;
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.cmd = void 0;
7
7
  const child_process_1 = require("child_process");
8
8
  const fs_extra_1 = __importDefault(require("fs-extra"));
9
+ const lodash_isequal_1 = __importDefault(require("lodash.isequal"));
9
10
  const minimatch_1 = require("minimatch");
10
11
  const path_1 = __importDefault(require("path"));
11
12
  const ts_morph_1 = require("ts-morph");
12
13
  const api_1 = require("tsx/esm/api");
13
14
  const types_1 = require("../types");
15
+ function setupProcessSignalHandlers(proc) {
16
+ const handleSignal = async (signal) => {
17
+ console.log(`\nReceived ${signal}, gracefully shutting down...`);
18
+ if (proc && !proc.killed) {
19
+ // Forward the signal to the child process
20
+ proc.kill(signal);
21
+ // Wait for the child process to exit
22
+ await new Promise((resolve) => {
23
+ proc.once("exit", () => {
24
+ resolve();
25
+ });
26
+ });
27
+ }
28
+ process.exit(0);
29
+ };
30
+ process.once("SIGINT", async () => await handleSignal("SIGINT"));
31
+ process.once("SIGTERM", async () => await handleSignal("SIGTERM"));
32
+ }
14
33
  function cmd(command, options) {
15
34
  let errorLogs = [];
16
35
  return new Promise((resolveFunc, rejectFunc) => {
17
- let p = (0, child_process_1.spawn)(command[0], command.slice(1), {
36
+ const p = (0, child_process_1.spawn)(command[0], command.slice(1), {
18
37
  env: { ...process.env, ...options.env },
38
+ // Ensure child process receives signals
39
+ detached: false,
19
40
  });
41
+ // Setup signal handlers
42
+ setupProcessSignalHandlers(p);
20
43
  p.stdout.on("data", (x) => {
21
44
  const log = x.toString();
22
45
  if (log.includes("Error")) {
@@ -47,23 +70,30 @@ exports.cmd = cmd;
47
70
  * @param {string} [directoryPath=""]
48
71
  * @return {*} {Promise<string[]>}
49
72
  */
50
- async function getAllFilePaths(directoryPath = "") {
73
+ async function getAllFilePaths(directoryPath = "", filters = {}) {
51
74
  let filePaths = [];
52
75
  try {
53
76
  const files = await fs_extra_1.default.readdir(directoryPath);
77
+ let allFilePaths = [];
54
78
  for (const file of files) {
55
79
  const filePath = path_1.default.join(directoryPath, file);
56
80
  const stat = await fs_extra_1.default.lstat(filePath);
57
81
  if (stat.isDirectory()) {
58
82
  // If it's a directory, recursively get file paths from the directory
59
- const nestedFiles = await getAllFilePaths(filePath);
60
- filePaths = filePaths.concat(nestedFiles);
83
+ const nestedFiles = await getAllFilePaths(filePath, filters);
84
+ allFilePaths = allFilePaths.concat(nestedFiles);
61
85
  }
62
86
  else {
63
87
  // If it's a file, push its path to the array
64
- filePaths.push(filePath);
88
+ allFilePaths.push(filePath);
65
89
  }
66
90
  }
91
+ filePaths = allFilePaths.filter((filePath) => {
92
+ if (filters.filePath) {
93
+ return filePath.includes(filters.filePath);
94
+ }
95
+ return true;
96
+ });
67
97
  }
68
98
  catch (err) {
69
99
  console.error("Error reading directory:", err);
@@ -85,21 +115,21 @@ const getTestModuleAliasFromSourceFile = (sourceFile) => {
85
115
  ?.getText() || "test");
86
116
  };
87
117
  exports.getTestModuleAliasFromSourceFile = getTestModuleAliasFromSourceFile;
88
- async function getTestCaseNode({ filePath, scenarioName, }) {
89
- const project = new ts_morph_1.Project();
118
+ async function getTestCaseNode({ filePath, scenarioName, suites, }) {
90
119
  const content = await fs_extra_1.default.readFile(filePath, "utf-8");
91
- const sourceFile = project.createSourceFile("test.ts", content);
92
- const testAlias = (0, exports.getTestModuleAliasFromSourceFile)(sourceFile);
93
- const testCaseNode = sourceFile.getFirstDescendant((node) => !!(node.isKind(ts_morph_1.SyntaxKind.CallExpression) &&
94
- node.getExpression().getText() === testAlias &&
95
- node.getArguments()[0]?.getText().includes(scenarioName)));
96
- return { testCaseNode, sourceFile };
120
+ const { testNode, sourceFile } = getTypescriptTestBlock({
121
+ scenarioName,
122
+ content,
123
+ suites, // since this method is called on the generated content, not the whole file
124
+ });
125
+ return { testCaseNode: testNode, sourceFile };
97
126
  }
98
127
  exports.getTestCaseNode = getTestCaseNode;
99
- async function hasTestBlock({ filePath, scenarioName, }) {
128
+ async function hasTestBlock({ filePath, scenarioName, suites, }) {
100
129
  const { testCaseNode } = await getTestCaseNode({
101
130
  filePath,
102
131
  scenarioName,
132
+ suites,
103
133
  });
104
134
  return !!testCaseNode;
105
135
  }
@@ -307,3 +337,72 @@ const handleTeardownSkipFlag = async (directory) => {
307
337
  });
308
338
  };
309
339
  exports.handleTeardownSkipFlag = handleTeardownSkipFlag;
340
+ const getParentDescribeNames = (node) => {
341
+ const names = [];
342
+ let current = node.getParent();
343
+ while (current) {
344
+ if (ts_morph_1.Node.isCallExpression(current)) {
345
+ const expr = current.getExpression();
346
+ if (expr.getText() === "test.describe") {
347
+ const describeBlockArguments = current.getArguments();
348
+ if (describeBlockArguments.length > 0) {
349
+ const describeBlockName = describeBlockArguments[0];
350
+ if (ts_morph_1.Node.isStringLiteral(describeBlockName)) {
351
+ names.push(describeBlockName.getLiteralText());
352
+ }
353
+ }
354
+ }
355
+ }
356
+ current = current.getParent();
357
+ }
358
+ return names.reverse(); // Reverse to get from outermost to innermost
359
+ };
360
+ /**
361
+ * function to get the test block and test node for the scenario
362
+ * @export
363
+ * @param {string} scenarioName
364
+ * @param {string} content
365
+ * @return { testBlock: string; parentDescribe: string; } testBlock - the test block content, testNode - the test function node
366
+ */
367
+ function getTypescriptTestBlock({ scenarioName, suites, content, }) {
368
+ const project = new ts_morph_1.Project();
369
+ const sourceFile = project.createSourceFile("test.ts", content);
370
+ const testAlias = (0, exports.getTestModuleAliasFromSourceFile)(sourceFile);
371
+ // Get all test function nodes that match the scenario name
372
+ const matchingTestFunctionNodes = sourceFile
373
+ .getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression)
374
+ .filter((node) => {
375
+ return Boolean(node.getExpression().getText() === testAlias &&
376
+ node.getArguments()[0]?.getText().includes(scenarioName));
377
+ });
378
+ if (!suites?.length) {
379
+ const firstNode = matchingTestFunctionNodes?.[0];
380
+ return {
381
+ testBlock: firstNode?.getText(),
382
+ testNode: firstNode,
383
+ testAlias,
384
+ sourceFile,
385
+ };
386
+ }
387
+ // Iterate over each test function node and check if the suites match
388
+ for (const testNode of matchingTestFunctionNodes) {
389
+ const parentDescribes = getParentDescribeNames(testNode);
390
+ if ((0, lodash_isequal_1.default)(parentDescribes, suites)) {
391
+ // Found the matching test block
392
+ return {
393
+ testBlock: testNode.getText(),
394
+ testNode,
395
+ testAlias,
396
+ sourceFile,
397
+ };
398
+ }
399
+ }
400
+ // No matching test block found
401
+ return {
402
+ testBlock: undefined,
403
+ testNode: undefined,
404
+ testAlias,
405
+ sourceFile,
406
+ };
407
+ }
408
+ exports.getTypescriptTestBlock = getTypescriptTestBlock;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empiricalrun/test-run",
3
- "version": "0.4.6",
3
+ "version": "0.5.2",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"
@@ -15,9 +15,11 @@
15
15
  },
16
16
  "author": "Empirical Team <hey@empirical.run>",
17
17
  "dependencies": {
18
+ "@types/lodash.isequal": "^4.5.8",
18
19
  "async-retry": "^1.3.3",
19
20
  "commander": "^12.1.0",
20
21
  "fs-extra": "^11.2.0",
22
+ "lodash.isequal": "^4.5.0",
21
23
  "minimatch": "^10.0.1",
22
24
  "ts-morph": "^23.0.0",
23
25
  "tsx": "^4.16.2"