testeranto 0.80.0 → 0.81.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/.aider.chat.history.md +980 -0
  2. package/.aider.input.history +87 -0
  3. package/.aider.tags.cache.v3/1c/30/af1de2ad7a137afeddb1b01e0c27.val +0 -0
  4. package/.aider.tags.cache.v3/1d/63/88318b65ce58b6bb0487e8ce2656.val +0 -0
  5. package/.aider.tags.cache.v3/2e/67/16ae65530b40038e48e00d666c63.val +0 -0
  6. package/.aider.tags.cache.v3/6f/94/80488a232866fcce7ee657da488b.val +0 -0
  7. package/.aider.tags.cache.v3/cache.db +0 -0
  8. package/.gitattributes +1 -0
  9. package/README.md +10 -6
  10. package/dist/common/src/Aider.js +116 -69
  11. package/dist/common/src/PM/main.js +49 -46
  12. package/dist/common/src/PM/node.js +1 -1
  13. package/dist/common/src/Project.js +46 -38
  14. package/dist/common/src/Puppeteer.js +44 -37
  15. package/dist/common/src/SubPackages/react-test-renderer/jsx/index.js +17 -2
  16. package/dist/common/src/esbuildConfigs/inputFilesPlugin.js +39 -12
  17. package/dist/common/src/esbuildConfigs/web.js +3 -3
  18. package/dist/common/src/lib/abstractBase.js +41 -13
  19. package/dist/common/src/lib/basebuilder.js +6 -3
  20. package/dist/common/src/lib/core.js +47 -3
  21. package/dist/common/tsconfig.common.tsbuildinfo +1 -1
  22. package/dist/module/src/Aider.js +116 -69
  23. package/dist/module/src/PM/main.js +49 -46
  24. package/dist/module/src/PM/node.js +1 -1
  25. package/dist/module/src/Project.js +46 -38
  26. package/dist/module/src/Puppeteer.js +44 -37
  27. package/dist/module/src/SubPackages/react-test-renderer/jsx/index.js +17 -2
  28. package/dist/module/src/esbuildConfigs/inputFilesPlugin.js +39 -12
  29. package/dist/module/src/esbuildConfigs/web.js +3 -3
  30. package/dist/module/src/lib/abstractBase.js +41 -13
  31. package/dist/module/src/lib/basebuilder.js +6 -3
  32. package/dist/module/src/lib/core.js +47 -3
  33. package/dist/module/tsconfig.module.tsbuildinfo +1 -1
  34. package/dist/types/src/PM/main.d.ts +7 -1
  35. package/dist/types/src/Project.d.ts +6 -1
  36. package/dist/types/src/lib/abstractBase.d.ts +1 -0
  37. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  38. package/package.json +11 -15
  39. package/secret +1 -0
  40. package/secret.env +2 -0
  41. package/src/Aider.ts +140 -97
  42. package/src/PM/main.ts +60 -56
  43. package/src/PM/node.ts +1 -1
  44. package/src/Project.ts +55 -48
  45. package/src/Puppeteer.ts +56 -49
  46. package/src/SubPackages/react-test-renderer/jsx/index.ts +25 -2
  47. package/src/esbuildConfigs/inputFilesPlugin.ts +77 -32
  48. package/src/esbuildConfigs/web.ts +3 -3
  49. package/src/lib/abstractBase.ts +48 -17
  50. package/src/lib/basebuilder.ts +8 -3
  51. package/src/lib/core.ts +55 -13
package/src/Project.ts CHANGED
@@ -9,58 +9,30 @@ import esbuildWebConfiger from "./esbuildConfigs/web.js";
9
9
  import webHtmlFrame from "./web.html.js";
10
10
  import { ITestTypes, IBaseConfig, IRunTime } from "./lib/types.js";
11
11
 
12
- var mode: "DEV" | "PROD" = process.argv[2] === "-dev" ? "DEV" : "PROD";
12
+ // var mode: "DEV" | "PROD" = process.argv[2] === "-dev" ? "DEV" : "PROD";
13
13
 
14
14
  readline.emitKeypressEvents(process.stdin);
15
15
  if (process.stdin.isTTY) process.stdin.setRawMode(true);
16
16
 
17
- process.stdin.on("keypress", (str, key) => {
18
- if (key.name === "q") {
19
- console.log("Testeranto-EsBuild is shutting down...");
20
- mode = "PROD";
21
- onDone();
22
- }
23
- });
24
-
25
- let nodeDone,
26
- webDone = false;
27
-
28
- const onNodeDone = () => {
29
- nodeDone = true;
30
- onDone();
31
- };
32
- const onWebDone = () => {
33
- webDone = true;
34
- onDone();
35
- };
36
-
37
- const onDone = () => {
38
- console.log(
39
- JSON.stringify(
40
- {
41
- nodeDone,
42
- webDone,
43
- mode,
44
- },
45
- null,
46
- 2
47
- )
48
- );
49
- if (nodeDone && webDone && mode === "PROD") {
50
- console.log("Testeranto-EsBuild is all done. Goodbye!");
51
- process.exit();
52
- } else {
53
- console.log("Testeranto-EsBuild is still working...");
54
- }
55
- };
56
-
57
17
  export class ITProject {
58
18
  config: IBaseConfig;
59
- mode: `up` | `down` = `up`;
19
+ mode: `DEV` | `PROD`;
20
+ nodeDone: boolean = false;
21
+ webDone: boolean = false;
60
22
 
61
23
  constructor(configs: IBaseConfig) {
62
24
  this.config = configs;
63
25
 
26
+ this.mode = this.config.devMode ? "DEV" : "PROD";
27
+
28
+ process.stdin.on("keypress", (str, key) => {
29
+ if (key.name === "q") {
30
+ console.log("Testeranto-EsBuild is shutting down...");
31
+ this.mode = "PROD";
32
+ this.onDone();
33
+ }
34
+ });
35
+
64
36
  fs.writeFileSync(
65
37
  `${this.config.outdir}/testeranto.json`,
66
38
  JSON.stringify(
@@ -124,13 +96,13 @@ export class ITProject {
124
96
  esbuild
125
97
  .context(esbuildNodeConfiger(this.config, nodeEntryPoints))
126
98
  .then(async (nodeContext) => {
127
- if (mode == "DEV") {
99
+ if (this.config.devMode) {
128
100
  await nodeContext.watch().then((v) => {
129
- onNodeDone();
101
+ this.onNodeDone();
130
102
  });
131
103
  } else {
132
104
  nodeContext.rebuild().then((v) => {
133
- onNodeDone();
105
+ this.onNodeDone();
134
106
  });
135
107
  }
136
108
 
@@ -139,13 +111,13 @@ export class ITProject {
139
111
  esbuild
140
112
  .context(esbuildWebConfiger(this.config, webEntryPoints))
141
113
  .then(async (webContext) => {
142
- if (mode == "DEV") {
114
+ if (this.config.devMode) {
143
115
  await webContext.watch().then((v) => {
144
- onWebDone();
116
+ this.onWebDone();
145
117
  });
146
118
  } else {
147
119
  webContext.rebuild().then((v) => {
148
- onWebDone();
120
+ this.onWebDone();
149
121
  });
150
122
  }
151
123
  return webContext;
@@ -153,6 +125,41 @@ export class ITProject {
153
125
  ]);
154
126
  }
155
127
 
128
+ onNodeDone = () => {
129
+ this.nodeDone = true;
130
+ this.onDone();
131
+ };
132
+ onWebDone = () => {
133
+ this.webDone = true;
134
+ this.onDone();
135
+ };
136
+
137
+ onDone = () => {
138
+ // console.log(this.nodeDone && this.webDone && this.mode === "PROD");
139
+ if (this.nodeDone && this.webDone && this.mode === "PROD") {
140
+ console.log("Testeranto-EsBuild is all done. Goodbye!");
141
+ process.exit();
142
+ } else {
143
+ if (this.mode === "PROD") {
144
+ console.log("waiting for tests to finish");
145
+ console.log(
146
+ JSON.stringify(
147
+ {
148
+ nodeDone: this.nodeDone,
149
+ webDone: this.webDone,
150
+ mode: this.mode,
151
+ },
152
+ null,
153
+ 2
154
+ )
155
+ );
156
+ } else {
157
+ console.log("waiting for tests to change");
158
+ }
159
+ console.log("press 'q' to quit");
160
+ }
161
+ };
162
+
156
163
  public getSecondaryEndpointsPoints(runtime?: IRunTime): string[] {
157
164
  const meta = (ts: ITestTypes[], st: Set<string>): Set<string> => {
158
165
  ts.forEach((t) => {
package/src/Puppeteer.ts CHANGED
@@ -5,27 +5,21 @@ import watch from "recursive-watch";
5
5
  import { PM_Main } from "./PM/main.js";
6
6
  import { destinationOfRuntime } from "./utils.js";
7
7
  import { timeout } from "puppeteer-core/lib/esm/puppeteer/index.js";
8
+ import { IBaseConfig, IBuiltConfig } from "./lib/types.js";
8
9
 
9
- var mode: "DEV" | "PROD" = process.argv[2] === "-dev" ? "DEV" : "PROD";
10
+ // var mode: "DEV" | "PROD" = process.argv[2] === "-dev" ? "DEV" : "PROD";
10
11
 
11
- const node2web: Record<string, string[]> = {};
12
- const web2node: Record<string, string[]> = {};
13
- const childProcesses: Record<string, "loaded" | "running" | "done"> = {};
12
+ // const node2web: Record<string, string[]> = {};
13
+ // const web2node: Record<string, string[]> = {};
14
+ // const childProcesses: Record<string, "loaded" | "running" | "done"> = {};
14
15
 
15
16
  readline.emitKeypressEvents(process.stdin);
16
17
  if (process.stdin.isTTY) process.stdin.setRawMode(true);
17
18
 
18
- // console.log("hello Puppeteer", process.env);
19
-
20
- console.log("\n Puppeteer is running. Press 'q' to quit\n");
21
- process.stdin.on("keypress", (str, key) => {
22
- if (key.name === "q") {
23
- process.exit();
24
- }
25
- });
19
+ // let shutDownMode = false;
26
20
 
27
21
  export default async (partialConfig) => {
28
- const config = {
22
+ const config: IBuiltConfig = {
29
23
  ...partialConfig,
30
24
  buildDir: process.cwd() + "/" + partialConfig.outdir,
31
25
  };
@@ -91,6 +85,14 @@ export default async (partialConfig) => {
91
85
  "."
92
86
  );
93
87
 
88
+ console.log("\n Puppeteer is running. Press 'q' to quit\n");
89
+ process.stdin.on("keypress", (str, key) => {
90
+ if (key.name === "q") {
91
+ pm.shutDown();
92
+ // process.exit();
93
+ }
94
+ });
95
+
94
96
  config.tests.forEach(([test, runtime, tr, sidecars]) => {
95
97
  if (runtime === "node") {
96
98
  pm.launchNode(test, destinationOfRuntime(test, "node", config));
@@ -101,43 +103,48 @@ export default async (partialConfig) => {
101
103
  }
102
104
  });
103
105
 
104
- console.log("ready and watching for changes...", config.buildDir);
105
-
106
- watch(config.buildDir, (eventType, changedFile) => {
107
- if (changedFile) {
108
- config.tests.forEach(([test, runtime, tr, sidecars]) => {
109
- if (eventType === "change" || eventType === "rename") {
110
- if (
111
- changedFile ===
112
- test
113
- .replace("./", "node/")
114
- .split(".")
115
- .slice(0, -1)
116
- .concat("mjs")
117
- .join(".")
118
- ) {
119
- pm.launchNode(test, destinationOfRuntime(test, "node", config));
106
+ if (config.devMode) {
107
+ console.log("ready and watching for changes...", config.buildDir);
108
+
109
+ watch(config.buildDir, (eventType, changedFile) => {
110
+ if (changedFile) {
111
+ config.tests.forEach(([test, runtime, tr, sidecars]) => {
112
+ if (eventType === "change" || eventType === "rename") {
113
+ if (
114
+ changedFile ===
115
+ test
116
+ .replace("./", "node/")
117
+ .split(".")
118
+ .slice(0, -1)
119
+ .concat("mjs")
120
+ .join(".")
121
+ ) {
122
+ pm.launchNode(test, destinationOfRuntime(test, "node", config));
123
+ }
124
+
125
+ if (
126
+ changedFile ===
127
+ test
128
+ .replace("./", "web/")
129
+ .split(".")
130
+ .slice(0, -1)
131
+ .concat("mjs")
132
+ .join(".")
133
+ ) {
134
+ pm.launchWeb(
135
+ test,
136
+ destinationOfRuntime(test, "web", config),
137
+ sidecars
138
+ );
139
+ }
120
140
  }
121
-
122
- if (
123
- changedFile ===
124
- test
125
- .replace("./", "web/")
126
- .split(".")
127
- .slice(0, -1)
128
- .concat("mjs")
129
- .join(".")
130
- ) {
131
- pm.launchWeb(
132
- test,
133
- destinationOfRuntime(test, "web", config),
134
- sidecars
135
- );
136
- }
137
- }
138
- });
139
- }
140
- });
141
+ });
142
+ }
143
+ });
144
+ } else {
145
+ pm.shutDown();
146
+ }
147
+ // pm.browser.close();
141
148
 
142
149
  // does not work on linux
143
150
  // fs.watch(
@@ -23,9 +23,17 @@ export type ITestImpl<ITestShape extends IBaseTest> = ITestImplementation<
23
23
  export type ITestSpec<ITestShape extends IBaseTest> =
24
24
  ITestSpecification<ITestShape>;
25
25
 
26
+ const Context = React.createContext({});
27
+
28
+ const AppContext = React.createContext({});
29
+ const contextValue = {
30
+ ingredients: ["flour", "sugar", "eggs"],
31
+ temperature: "200",
32
+ };
33
+
26
34
  export const testInterface = {
27
35
  butThen: async function (s: IStore, thenCB, tr): Promise<ISelection> {
28
- console.log("butThen", thenCB.toString());
36
+ // console.log("butThen", thenCB.toString());
29
37
  return thenCB(s);
30
38
  },
31
39
  beforeEach: function (
@@ -34,7 +42,22 @@ export const testInterface = {
34
42
  ): Promise<renderer.ReactTestRenderer> {
35
43
  let component;
36
44
  act(() => {
37
- component = renderer.create(React.createElement(CComponent, props, []));
45
+ // component = renderer.create(
46
+ // React.createElement(
47
+ // AppContext.Provider,
48
+ // { value: contextValue },
49
+ // React.createElement(AppContext.Consumer, null, (context) =>
50
+ // React.createElement(CComponent, Object.assign({}, context, {}))
51
+ // )
52
+ // )
53
+ // );
54
+ component = renderer.create(
55
+ React.createElement(
56
+ CComponent,
57
+ props,
58
+ React.createElement(CComponent, props, [])
59
+ )
60
+ );
38
61
  });
39
62
  return component;
40
63
  },
@@ -5,21 +5,30 @@ export default (
5
5
  platform: "web" | "node",
6
6
  entryPoints: Set<string> | string[]
7
7
  ) => {
8
- console.log("mark3", platform);
9
-
10
8
  return {
11
9
  name: "metafileWriter",
12
10
  setup(build) {
13
11
  build.onEnd((result) => {
14
12
  if (result.errors.length === 0) {
15
13
  entryPoints.forEach((entryPoint) => {
16
- console.log("mark1", entryPoint);
17
14
  const filePath = path.join(
18
15
  "./docs/",
19
16
  platform,
20
17
  entryPoint.split(".").slice(0, -1).join("."),
21
18
  `inputFiles.json`
22
19
  );
20
+ const promptPath = path.join(
21
+ "./docs/",
22
+ platform,
23
+ entryPoint.split(".").slice(0, -1).join("."),
24
+ `prompt.txt`
25
+ );
26
+ const testPaths = path.join(
27
+ "./docs/",
28
+ platform,
29
+ entryPoint.split(".").slice(0, -1).join("."),
30
+ `tests.json`
31
+ );
23
32
 
24
33
  const dirName = path.dirname(filePath);
25
34
 
@@ -27,38 +36,74 @@ export default (
27
36
  fs.mkdirSync(dirName, { recursive: true });
28
37
  }
29
38
 
30
- const jsonContent = JSON.stringify(
31
- Object.keys(
32
- Object.keys(result.metafile.outputs)
33
- .filter((s: string) => {
34
- if (!result.metafile.outputs[s].entryPoint) {
35
- return false;
36
- }
37
- return (
38
- path.resolve(result.metafile.outputs[s].entryPoint) ===
39
- path.resolve(entryPoint)
40
- );
41
- })
42
- .reduce((mm: string[], el) => {
43
- mm.push(result.metafile.outputs[el].inputs);
44
- return mm;
45
- }, [])[0]
46
- )
47
- .filter((f: string) => {
48
- const regex = /^src\/.*/g;
49
- const matches = f.match(regex);
50
- const passes = matches?.length === 1;
51
- return passes;
52
- })
53
- .filter((f: string) => {
54
- const regex = /.*\.test\..*/g;
55
- const matches = f.match(regex);
56
- const passes = matches?.length === 1;
57
- return !passes;
39
+ const j = Object.keys(
40
+ Object.keys(result.metafile.outputs)
41
+ .filter((s: string) => {
42
+ if (!result.metafile.outputs[s].entryPoint) {
43
+ return false;
44
+ }
45
+ return (
46
+ path.resolve(result.metafile.outputs[s].entryPoint) ===
47
+ path.resolve(entryPoint)
48
+ );
58
49
  })
59
- );
50
+ .reduce((mm: string[], el) => {
51
+ mm.push(result.metafile.outputs[el].inputs);
52
+ return mm;
53
+ }, [])[0]
54
+ ).filter((f: string) => {
55
+ const regex = /^src\/.*/g;
56
+ const matches = f.match(regex);
57
+ const passes = matches?.length === 1;
58
+ return passes;
59
+ });
60
+ // .filter((f: string) => {
61
+ // const regex = /.*\.test\..*/g;
62
+ // const matches = f.match(regex);
63
+ // const passes = matches?.length === 1;
64
+ // return !passes;
65
+ // })
66
+
67
+ const jsonContent = JSON.stringify(j);
60
68
 
61
69
  fs.writeFileSync(filePath, jsonContent);
70
+
71
+ fs.writeFileSync(
72
+ promptPath,
73
+ `
74
+ ${j
75
+ .map((x) => {
76
+ return `/add ${x}`;
77
+ })
78
+ .join("\n")}
79
+ /read ${testPaths}
80
+
81
+ /code fix the failing tests described in ${filePath}.
82
+ `
83
+ );
84
+
85
+ // fs.writeFileSync(
86
+ // promptPath,
87
+ // `
88
+ // from aider.coders import Coder
89
+ // from aider.models import Model
90
+ // import os
91
+
92
+ // model = Model("deepseek")
93
+
94
+ // coder = Coder.create(main_model=model)
95
+
96
+ // coder.run("/read-only", "${testPaths}")
97
+
98
+ // ${j
99
+ // .map((x) => {
100
+ // return `coder.run("/add", "${x}")`;
101
+ // })
102
+ // .join("\n")}
103
+
104
+ // coder.run("fix the failing tests described in ${filePath}.")
105
+ // `
106
+ // );
62
107
  });
63
108
  }
64
109
  });
@@ -29,10 +29,10 @@ export default (
29
29
  metafile: true,
30
30
 
31
31
  external: [
32
- "testeranto.json",
33
- "features.test.ts",
32
+ // "testeranto.json",
33
+ // "features.test.ts",
34
34
  // "url",
35
- "react",
35
+ // "react",
36
36
 
37
37
  "path",
38
38
  "fs",
@@ -58,10 +58,22 @@ export abstract class BaseSuite<
58
58
  }
59
59
 
60
60
  public toObj() {
61
+ const givens = Object.keys(this.givens).map((k) => this.givens[k].toObj());
62
+ const features = Object.keys(this.givens)
63
+ .map((k) => this.givens[k].features)
64
+ .flat()
65
+ .filter((value, index, array) => {
66
+ return array.indexOf(value) === index;
67
+ })
68
+ .reduce((mm, lm) => {
69
+ mm[lm] = lm;
70
+ return mm;
71
+ }, {});
61
72
  return {
62
73
  name: this.name,
63
- givens: Object.keys(this.givens).map((k) => this.givens[k].toObj()),
74
+ givens,
64
75
  fails: this.fails,
76
+ features,
65
77
  };
66
78
  }
67
79
 
@@ -406,7 +418,7 @@ export abstract class BaseGiven<
406
418
  this.givenCB,
407
419
  beforeEachProxy
408
420
  );
409
- console.log("mark6", this.store);
421
+ // console.log("mark6", this.store);
410
422
 
411
423
  for (const [whenNdx, whenStep] of this.whens.entries()) {
412
424
  await whenStep.test(
@@ -535,7 +547,7 @@ export abstract class BaseGiven<
535
547
  },
536
548
  });
537
549
 
538
- console.log("mark5", this.store, key);
550
+ // console.log("mark5", this.store, key);
539
551
  await this.afterEach(this.store, key, givenArtifactory, afterEachProxy);
540
552
  } catch (e) {
541
553
  console.error("afterEach failed! no error will be recorded!", e);
@@ -684,17 +696,26 @@ export abstract class BaseWhen<ITestShape extends IBaseTest> {
684
696
  },
685
697
  });
686
698
 
687
- try {
688
- return await this.andWhen(
689
- store,
690
- this.whenCB,
691
- testResourceConfiguration,
692
- andWhenProxy
693
- );
694
- } catch (e) {
699
+ return this.andWhen(
700
+ store,
701
+ this.whenCB,
702
+ testResourceConfiguration,
703
+ andWhenProxy
704
+ ).catch((e) => {
695
705
  this.error = true;
696
- throw e;
697
- }
706
+ // throw e;
707
+ });
708
+ // try {
709
+ // return await this.andWhen(
710
+ // store,
711
+ // this.whenCB,
712
+ // testResourceConfiguration,
713
+ // andWhenProxy
714
+ // );
715
+ // } catch (e) {
716
+ // this.error = true;
717
+ // throw e;
718
+ // }
698
719
  }
699
720
  }
700
721
 
@@ -818,14 +839,24 @@ export abstract class BaseThen<
818
839
  },
819
840
  });
820
841
 
821
- const x = await this.butThen(
842
+ // const x = await this.butThen(
843
+ // store,
844
+ // this.thenCB,
845
+ // testResourceConfiguration,
846
+ // butThenProxy
847
+ // );
848
+ // return x;
849
+
850
+ return this.butThen(
822
851
  store,
823
852
  this.thenCB,
824
853
  testResourceConfiguration,
825
854
  butThenProxy
826
- // pm
827
- );
828
- return x;
855
+ ).catch((e) => {
856
+ console.log("mar123");
857
+ this.error = true;
858
+ throw e;
859
+ });
829
860
  } catch (e) {
830
861
  console.log("test failed", e);
831
862
  this.error = e.message;
@@ -89,7 +89,7 @@ export abstract class BaseBuilder<
89
89
  puppetMaster: PM,
90
90
  tLog: ITLog
91
91
  ): Promise<BaseSuite<ITestShape>> => {
92
- await puppetMaster.startPuppeteer(
92
+ const puppeteerBrowser = await puppetMaster.startPuppeteer(
93
93
  {
94
94
  browserWSEndpoint:
95
95
  puppetMaster.testResourceConfiguration.browserWSEndpoint,
@@ -97,7 +97,7 @@ export abstract class BaseBuilder<
97
97
  puppetMaster.testResourceConfiguration.fs
98
98
  );
99
99
 
100
- return await suite.run(
100
+ const x = await suite.run(
101
101
  input,
102
102
  puppetMaster.testResourceConfiguration,
103
103
  (fPath: string, value: string | Buffer | PassThrough) =>
@@ -110,6 +110,10 @@ export abstract class BaseBuilder<
110
110
  tLog,
111
111
  puppetMaster
112
112
  );
113
+
114
+ // await puppetMaster.browser.disconnect();
115
+ // puppeteerBrowser.close();
116
+ return x;
113
117
  };
114
118
 
115
119
  const runner = suiteRunner(suite);
@@ -124,7 +128,7 @@ export abstract class BaseBuilder<
124
128
  runner,
125
129
 
126
130
  receiveTestResourceConfig: async function (puppetMaster: PM) {
127
- await puppetMaster.mkdirSync();
131
+ // await puppetMaster.mkdirSync();
128
132
 
129
133
  const logFilePath = "log.txt";
130
134
  const access = await puppetMaster.createWriteStream(logFilePath);
@@ -161,6 +165,7 @@ export abstract class BaseBuilder<
161
165
  JSON.stringify(this.toObj(), null, 2)
162
166
  );
163
167
  console.log(`exiting gracefully with ${numberOfFailures} failures.`);
168
+
164
169
  return {
165
170
  failed: numberOfFailures,
166
171
  artifacts: this.artifacts || [],