testeranto 0.199.0 → 0.200.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.
Files changed (157) hide show
  1. package/bin/test_runner +0 -0
  2. package/bin/testeranto +0 -0
  3. package/bundle.js +2 -2
  4. package/cmd/test_runner/main.go +65 -0
  5. package/cmd/testeranto/main.go +37 -0
  6. package/dist/common/src/PM/main.js +126 -11
  7. package/dist/common/src/PM/pitonoRunner.js +54 -0
  8. package/dist/common/src/components/pure/TestPageView.js +180 -65
  9. package/dist/common/src/components/stateful/TestPage.js +50 -11
  10. package/dist/common/src/lib/abstractBase.test/index.js +1 -0
  11. package/dist/common/src/run.js +48 -82
  12. package/dist/common/src/{build.js → testeranto.js} +107 -55
  13. package/dist/common/src/utils/golingvuMetafile.js +116 -0
  14. package/dist/common/src/utils/logFiles.js +2 -1
  15. package/dist/common/src/utils/pitonoMetafile.js +67 -0
  16. package/dist/common/src/utils.js +40 -1
  17. package/dist/common/testeranto.config.js +23 -21
  18. package/dist/common/tsconfig.common.tsbuildinfo +1 -1
  19. package/dist/module/src/PM/main.js +126 -11
  20. package/dist/module/src/PM/pitonoRunner.js +47 -0
  21. package/dist/module/src/components/pure/TestPageView.js +180 -65
  22. package/dist/module/src/components/stateful/TestPage.js +50 -11
  23. package/dist/module/src/lib/abstractBase.test/index.js +1 -0
  24. package/dist/module/src/run.js +49 -45
  25. package/dist/module/src/{build.js → testeranto.js} +107 -55
  26. package/dist/module/src/utils/golingvuMetafile.js +109 -0
  27. package/dist/module/src/utils/logFiles.js +2 -1
  28. package/dist/module/src/utils/pitonoMetafile.js +60 -0
  29. package/dist/module/src/utils.js +40 -1
  30. package/dist/module/testeranto.config.js +23 -21
  31. package/dist/module/tsconfig.module.tsbuildinfo +1 -1
  32. package/dist/prebuild/App.js +81 -17
  33. package/dist/types/src/PM/main.d.ts +2 -0
  34. package/dist/types/src/PM/pitonoRunner.d.ts +7 -0
  35. package/dist/types/src/Types.d.ts +1 -1
  36. package/dist/types/src/run.d.ts +0 -1
  37. package/dist/types/src/utils/golingvuMetafile.d.ts +19 -0
  38. package/dist/types/src/utils/logFiles.d.ts +5 -1
  39. package/dist/types/src/utils/pitonoMetafile.d.ts +7 -0
  40. package/dist/types/src/utils.d.ts +5 -0
  41. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  42. package/docs/index.md +13 -13
  43. package/example/test_example.py +106 -0
  44. package/go.mod +3 -0
  45. package/package.json +2 -2
  46. package/pitono/__init__.py +54 -0
  47. package/pitono/base_given.py +131 -0
  48. package/pitono/base_suite.py +95 -0
  49. package/pitono/base_then.py +50 -0
  50. package/pitono/base_when.py +52 -0
  51. package/pitono/core_generator.py +110 -0
  52. package/pitono/pitono.egg-info/PKG-INFO +17 -0
  53. package/pitono/pitono.egg-info/SOURCES.txt +7 -0
  54. package/pitono/pitono.egg-info/dependency_links.txt +1 -0
  55. package/pitono/pitono.egg-info/entry_points.txt +2 -0
  56. package/pitono/pitono.egg-info/top_level.txt +1 -0
  57. package/pitono/pyproject.toml +26 -0
  58. package/pitono/setup.py +40 -0
  59. package/pitono/simple_adapter.py +24 -0
  60. package/pitono/types.py +78 -0
  61. package/sampleMetafile.json +56 -0
  62. package/src/PM/main.ts +146 -17
  63. package/src/PM/pitonoRunner.ts +49 -0
  64. package/src/Types.ts +1 -1
  65. package/src/components/pure/TestPageView.tsx +175 -8
  66. package/src/components/stateful/TestPage.tsx +57 -16
  67. package/src/core/types.go +36 -0
  68. package/src/golingvu/README.md +3 -0
  69. package/src/golingvu/base_given.go +76 -0
  70. package/src/golingvu/base_suite.go +39 -0
  71. package/src/golingvu/base_suite_test.go +197 -0
  72. package/src/golingvu/base_then.go +21 -0
  73. package/src/golingvu/base_when.go +21 -0
  74. package/src/golingvu/golingvu.go +179 -0
  75. package/src/golingvu/test_adapter.go +33 -0
  76. package/src/golingvu/types.go +86 -0
  77. package/src/lib/abstractBase.test/index.ts +1 -0
  78. package/src/pitono/README.md +3 -0
  79. package/src/run.ts +48 -48
  80. package/src/templates/frontpage.html +26 -17
  81. package/src/{build.ts → testeranto.ts} +128 -58
  82. package/src/utils/golingvuMetafile.ts +165 -0
  83. package/src/utils/logFiles.ts +2 -1
  84. package/src/utils/pitonoMetafile.ts +68 -0
  85. package/src/utils.ts +38 -1
  86. package/testeranto/App.js +81 -17
  87. package/testeranto/metafiles/golang/core.json +72 -0
  88. package/testeranto/metafiles/node/core.json +21 -459
  89. package/testeranto/metafiles/pure/core.json +18 -119
  90. package/testeranto/metafiles/web/core.json +37 -16797
  91. package/testeranto/reports/core/config.json +8 -40
  92. package/testeranto/reports/core/src/lib/BaseSuite.test/node.test/node/lint_errors.txt +6 -0
  93. package/testeranto/reports/core/src/lib/BaseSuite.test/node.test/node/prompt.txt +12 -1
  94. package/testeranto/reports/core/src/lib/BaseSuite.test/pure.test/pure/lint_errors.txt +2 -0
  95. package/testeranto/reports/core/src/lib/BaseSuite.test/pure.test/pure/prompt.txt +11 -1
  96. package/testeranto/reports/core/src/lib/BaseSuite.test/web.test/web/lint_errors.txt +2 -0
  97. package/testeranto/reports/core/src/lib/BaseSuite.test/web.test/web/prompt.txt +13 -3
  98. package/testeranto/reports/core/summary.json +9 -45
  99. package/testeranto.config.ts +25 -21
  100. package/tsc.log +46 -7
  101. package/dist/common/src/lib/mocks.test.js +0 -11
  102. package/dist/module/src/lib/mocks.test.js +0 -11
  103. package/dist/prebuild/build.mjs +0 -578
  104. package/dist/prebuild/run.mjs +0 -2290
  105. package/dist/tsconfig.tsbuildinfo +0 -1
  106. package/dist/types/src/lib/mocks.test.d.ts +0 -0
  107. package/src/lib/mocks.test.ts +0 -11
  108. package/testeranto/reports/core/src/Pure.test/pure/exit.log +0 -0
  109. package/testeranto/reports/core/src/Pure.test/pure/lint_errors.txt +0 -0
  110. package/testeranto/reports/core/src/Pure.test/pure/message.txt +0 -17
  111. package/testeranto/reports/core/src/Pure.test/pure/prompt.txt +0 -14
  112. package/testeranto/reports/core/src/Pure.test/pure/type_errors.txt +0 -66
  113. package/testeranto/reports/core/src/components/pure/FeaturesReporterView.test/index/web/debug.log +0 -0
  114. package/testeranto/reports/core/src/components/pure/FeaturesReporterView.test/index/web/error.log +0 -67
  115. package/testeranto/reports/core/src/components/pure/FeaturesReporterView.test/index/web/exit.log +0 -1
  116. package/testeranto/reports/core/src/components/pure/FeaturesReporterView.test/index/web/info.log +0 -2
  117. package/testeranto/reports/core/src/components/pure/FeaturesReporterView.test/index/web/lint_errors.txt +0 -0
  118. package/testeranto/reports/core/src/components/pure/FeaturesReporterView.test/index/web/message.txt +0 -17
  119. package/testeranto/reports/core/src/components/pure/FeaturesReporterView.test/index/web/prompt.txt +0 -16
  120. package/testeranto/reports/core/src/components/pure/FeaturesReporterView.test/index/web/tests.json +0 -68
  121. package/testeranto/reports/core/src/components/pure/FeaturesReporterView.test/index/web/type_errors.txt +0 -56
  122. package/testeranto/reports/core/src/components/pure/FeaturesReporterView.test/index/web/warn.log +0 -0
  123. package/testeranto/reports/core/src/components/pure/ProjectPageView.test/index/web/debug.log +0 -0
  124. package/testeranto/reports/core/src/components/pure/ProjectPageView.test/index/web/error.log +0 -22
  125. package/testeranto/reports/core/src/components/pure/ProjectPageView.test/index/web/exit.log +0 -1
  126. package/testeranto/reports/core/src/components/pure/ProjectPageView.test/index/web/info.log +0 -2
  127. package/testeranto/reports/core/src/components/pure/ProjectPageView.test/index/web/lint_errors.txt +0 -13
  128. package/testeranto/reports/core/src/components/pure/ProjectPageView.test/index/web/message.txt +0 -17
  129. package/testeranto/reports/core/src/components/pure/ProjectPageView.test/index/web/prompt.txt +0 -16
  130. package/testeranto/reports/core/src/components/pure/ProjectPageView.test/index/web/tests.json +0 -88
  131. package/testeranto/reports/core/src/components/pure/ProjectPageView.test/index/web/type_errors.txt +0 -45
  132. package/testeranto/reports/core/src/components/pure/ProjectPageView.test/index/web/warn.log +0 -0
  133. package/testeranto/reports/core/src/components/pure/TestPageView.test/index/web/debug.log +0 -0
  134. package/testeranto/reports/core/src/components/pure/TestPageView.test/index/web/error.log +0 -0
  135. package/testeranto/reports/core/src/components/pure/TestPageView.test/index/web/exit.log +0 -1
  136. package/testeranto/reports/core/src/components/pure/TestPageView.test/index/web/info.log +0 -2
  137. package/testeranto/reports/core/src/components/pure/TestPageView.test/index/web/lint_errors.txt +0 -47
  138. package/testeranto/reports/core/src/components/pure/TestPageView.test/index/web/message.txt +0 -17
  139. package/testeranto/reports/core/src/components/pure/TestPageView.test/index/web/prompt.txt +0 -17
  140. package/testeranto/reports/core/src/components/pure/TestPageView.test/index/web/tests.json +0 -57
  141. package/testeranto/reports/core/src/components/pure/TestPageView.test/index/web/type_errors.txt +0 -99
  142. package/testeranto/reports/core/src/components/pure/TestPageView.test/index/web/warn.log +0 -0
  143. package/testeranto/reports/core/src/lib/TipoSkripto.test/TipoSkripto/node/exit.log +0 -1
  144. package/testeranto/reports/core/src/lib/TipoSkripto.test/TipoSkripto/node/lint_errors.txt +0 -0
  145. package/testeranto/reports/core/src/lib/TipoSkripto.test/TipoSkripto/node/message.txt +0 -17
  146. package/testeranto/reports/core/src/lib/TipoSkripto.test/TipoSkripto/node/prompt.txt +0 -17
  147. package/testeranto/reports/core/src/lib/TipoSkripto.test/TipoSkripto/node/stderr.log +0 -18
  148. package/testeranto/reports/core/src/lib/TipoSkripto.test/TipoSkripto/node/stdout.log +0 -0
  149. package/testeranto/reports/core/src/lib/TipoSkripto.test/TipoSkripto/node/type_errors.txt +0 -32
  150. package/testeranto/reports/core/src/lib/pmProxy.test/index/node/exit.log +0 -1
  151. package/testeranto/reports/core/src/lib/pmProxy.test/index/node/lint_errors.txt +0 -15
  152. package/testeranto/reports/core/src/lib/pmProxy.test/index/node/message.txt +0 -17
  153. package/testeranto/reports/core/src/lib/pmProxy.test/index/node/prompt.txt +0 -17
  154. package/testeranto/reports/core/src/lib/pmProxy.test/index/node/stderr.log +0 -66
  155. package/testeranto/reports/core/src/lib/pmProxy.test/index/node/stdout.log +0 -10
  156. package/testeranto/reports/core/src/lib/pmProxy.test/index/node/type_errors.txt +0 -47
  157. /package/dist/types/src/{build.d.ts → testeranto.d.ts} +0 -0
@@ -1,2290 +0,0 @@
1
- import { createRequire } from 'module';const require = createRequire(import.meta.url);
2
-
3
- // src/run.ts
4
- import ansiC3 from "ansi-colors";
5
- import readline from "readline";
6
-
7
- // src/PM/main.ts
8
- import { spawn } from "node:child_process";
9
- import ansiColors from "ansi-colors";
10
- import net from "net";
11
- import fs4, { watch } from "fs";
12
- import path4 from "path";
13
- import puppeteer from "puppeteer-core";
14
- import ansiC2 from "ansi-colors";
15
- import crypto from "node:crypto";
16
- import { WebSocketServer } from "ws";
17
- import http from "http";
18
- import url from "url";
19
- import mime from "mime-types";
20
-
21
- // src/utils.ts
22
- import path from "path";
23
- var tscPather = (entryPoint, platform, projectName2) => {
24
- return path.join(
25
- "testeranto",
26
- "reports",
27
- projectName2,
28
- entryPoint.split(".").slice(0, -1).join("."),
29
- platform,
30
- `type_errors.txt`
31
- );
32
- };
33
- var lintPather = (entryPoint, platform, projectName2) => {
34
- return path.join(
35
- "testeranto",
36
- "reports",
37
- projectName2,
38
- entryPoint.split(".").slice(0, -1).join("."),
39
- platform,
40
- `lint_errors.txt`
41
- );
42
- };
43
- var promptPather = (entryPoint, platform, projectName2) => {
44
- return path.join(
45
- "testeranto",
46
- "reports",
47
- projectName2,
48
- entryPoint.split(".").slice(0, -1).join("."),
49
- platform,
50
- `prompt.txt`
51
- );
52
- };
53
- var getRunnables = (tests, projectName2, payload = {
54
- nodeEntryPoints: {},
55
- nodeEntryPointSidecars: {},
56
- webEntryPoints: {},
57
- webEntryPointSidecars: {},
58
- pureEntryPoints: {},
59
- pureEntryPointSidecars: {}
60
- }) => {
61
- return tests.reduce((pt, cv, cndx, cry) => {
62
- if (cv[1] === "node") {
63
- pt.nodeEntryPoints[cv[0]] = path.resolve(
64
- `./testeranto/bundles/node/${projectName2}/${cv[0].split(".").slice(0, -1).concat("mjs").join(".")}`
65
- );
66
- } else if (cv[1] === "web") {
67
- pt.webEntryPoints[cv[0]] = path.resolve(
68
- `./testeranto/bundles/web/${projectName2}/${cv[0].split(".").slice(0, -1).concat("mjs").join(".")}`
69
- );
70
- } else if (cv[1] === "pure") {
71
- pt.pureEntryPoints[cv[0]] = path.resolve(
72
- `./testeranto/bundles/pure/${projectName2}/${cv[0].split(".").slice(0, -1).concat("mjs").join(".")}`
73
- );
74
- }
75
- cv[3].filter((t) => t[1] === "node").forEach((t) => {
76
- pt.nodeEntryPointSidecars[`${t[0]}`] = path.resolve(
77
- `./testeranto/bundles/node/${projectName2}/${cv[0].split(".").slice(0, -1).concat("mjs").join(".")}`
78
- );
79
- });
80
- cv[3].filter((t) => t[1] === "web").forEach((t) => {
81
- pt.webEntryPointSidecars[`${t[0]}`] = path.resolve(
82
- `./testeranto/bundles/web/${projectName2}/${cv[0].split(".").slice(0, -1).concat("mjs").join(".")}`
83
- );
84
- });
85
- cv[3].filter((t) => t[1] === "pure").forEach((t) => {
86
- pt.pureEntryPointSidecars[`${t[0]}`] = path.resolve(
87
- `./testeranto/bundles/pure/${projectName2}/${cv[0].split(".").slice(0, -1).concat("mjs").join(".")}`
88
- );
89
- });
90
- return pt;
91
- }, payload);
92
- };
93
-
94
- // src/utils/queue.ts
95
- var Queue = class {
96
- constructor() {
97
- this.items = [];
98
- }
99
- enqueue(element) {
100
- this.items.push(element);
101
- }
102
- dequeue() {
103
- if (this.isEmpty()) {
104
- return "Queue is empty";
105
- }
106
- return this.items.shift();
107
- }
108
- peek() {
109
- if (this.isEmpty()) {
110
- return "Queue is empty";
111
- }
112
- return this.items[0];
113
- }
114
- isEmpty() {
115
- return this.items.length === 0;
116
- }
117
- size() {
118
- return this.items.length;
119
- }
120
- clear() {
121
- this.items = [];
122
- }
123
- print() {
124
- console.log(this.items.join(" -> "));
125
- }
126
- };
127
-
128
- // src/PM/PM_WithEslintAndTsc.ts
129
- import ts from "typescript";
130
- import fs3 from "fs";
131
- import ansiC from "ansi-colors";
132
- import { ESLint } from "eslint";
133
- import tsc from "tsc-prog";
134
-
135
- // src/PM/base.ts
136
- import fs from "fs";
137
- import path2 from "path";
138
- var fileStreams3 = [];
139
- var fPaths = [];
140
- var files = {};
141
- var recorders = {};
142
- var screenshots = {};
143
- var PM_Base = class {
144
- constructor(configs) {
145
- this.configs = configs;
146
- }
147
- customclose() {
148
- throw new Error("customclose not implemented.");
149
- }
150
- waitForSelector(p, s) {
151
- return new Promise((res) => {
152
- this.doInPage(p, async (page) => {
153
- const x = page.$(s);
154
- const y = await x;
155
- res(y !== null);
156
- });
157
- });
158
- }
159
- closePage(p) {
160
- return new Promise((res) => {
161
- this.doInPage(p, async (page) => {
162
- page.close();
163
- res({});
164
- });
165
- });
166
- }
167
- async newPage() {
168
- return (await this.browser.newPage()).mainFrame()._id;
169
- }
170
- goto(p, url2) {
171
- return new Promise((res) => {
172
- this.doInPage(p, async (page) => {
173
- await page?.goto(url2);
174
- res({});
175
- });
176
- });
177
- }
178
- $(selector, p) {
179
- return new Promise((res) => {
180
- this.doInPage(p, async (page) => {
181
- const x = await page.$(selector);
182
- const y = await x?.jsonValue();
183
- res(y);
184
- });
185
- });
186
- }
187
- async pages() {
188
- return (await this.browser.pages()).map((p) => {
189
- return p.mainFrame()._id;
190
- });
191
- }
192
- async screencast(ssOpts, testName, page) {
193
- const p = ssOpts.path;
194
- const dir = path2.dirname(p);
195
- fs.mkdirSync(dir, {
196
- recursive: true
197
- });
198
- if (!files[testName]) {
199
- files[testName] = /* @__PURE__ */ new Set();
200
- }
201
- files[testName].add(ssOpts.path);
202
- const sPromise = page.screenshot({
203
- ...ssOpts,
204
- path: p
205
- });
206
- if (!screenshots[testName]) {
207
- screenshots[testName] = [];
208
- }
209
- screenshots[testName].push(sPromise);
210
- await sPromise;
211
- return sPromise;
212
- }
213
- async customScreenShot(ssOpts, testName, pageUid) {
214
- const p = ssOpts.path;
215
- const dir = path2.dirname(p);
216
- fs.mkdirSync(dir, {
217
- recursive: true
218
- });
219
- if (!files[testName]) {
220
- files[testName] = /* @__PURE__ */ new Set();
221
- }
222
- files[testName].add(ssOpts.path);
223
- const page = (await this.browser.pages()).find(
224
- (p2) => p2.mainFrame()._id === pageUid
225
- );
226
- const sPromise = page.screenshot({
227
- ...ssOpts,
228
- path: p
229
- });
230
- if (!screenshots[testName]) {
231
- screenshots[testName] = [];
232
- }
233
- screenshots[testName].push(sPromise);
234
- await sPromise;
235
- return sPromise;
236
- }
237
- async end(uid) {
238
- await fileStreams3[uid].end();
239
- return true;
240
- }
241
- existsSync(destFolder) {
242
- return fs.existsSync(destFolder);
243
- }
244
- async mkdirSync(fp) {
245
- if (!fs.existsSync(fp)) {
246
- return fs.mkdirSync(fp, {
247
- recursive: true
248
- });
249
- }
250
- return false;
251
- }
252
- async writeFileSync(...x) {
253
- const filepath = x[0];
254
- const contents = x[1];
255
- const testName = x[2];
256
- return new Promise(async (res) => {
257
- fs.mkdirSync(path2.dirname(filepath), {
258
- recursive: true
259
- });
260
- if (!files[testName]) {
261
- files[testName] = /* @__PURE__ */ new Set();
262
- }
263
- files[testName].add(filepath);
264
- await fs.writeFileSync(filepath, contents);
265
- res(true);
266
- });
267
- }
268
- async createWriteStream(filepath, testName) {
269
- const folder = filepath.split("/").slice(0, -1).join("/");
270
- return new Promise((res) => {
271
- if (!fs.existsSync(folder)) {
272
- return fs.mkdirSync(folder, {
273
- recursive: true
274
- });
275
- }
276
- const f2 = fs.createWriteStream(filepath);
277
- fileStreams3.push(f2);
278
- if (!files[testName]) {
279
- files[testName] = /* @__PURE__ */ new Set();
280
- }
281
- files[testName].add(filepath);
282
- res(fileStreams3.length - 1);
283
- });
284
- }
285
- testArtiFactoryfileWriter(tLog, callback) {
286
- return (fPath, value) => {
287
- callback(
288
- new Promise((res, rej) => {
289
- tLog("testArtiFactory =>", fPath);
290
- const cleanPath = path2.resolve(fPath);
291
- fPaths.push(cleanPath.replace(process.cwd(), ``));
292
- const targetDir = cleanPath.split("/").slice(0, -1).join("/");
293
- fs.mkdir(targetDir, { recursive: true }, async (error) => {
294
- if (error) {
295
- console.error(`\u2757\uFE0FtestArtiFactory failed`, targetDir, error);
296
- }
297
- fs.writeFileSync(
298
- path2.resolve(
299
- targetDir.split("/").slice(0, -1).join("/"),
300
- "manifest"
301
- ),
302
- fPaths.join(`
303
- `),
304
- {
305
- encoding: "utf-8"
306
- }
307
- );
308
- if (Buffer.isBuffer(value)) {
309
- fs.writeFileSync(fPath, value, "binary");
310
- res();
311
- } else if (`string` === typeof value) {
312
- fs.writeFileSync(fPath, value.toString(), {
313
- encoding: "utf-8"
314
- });
315
- res();
316
- } else {
317
- const pipeStream = value;
318
- const myFile = fs.createWriteStream(fPath);
319
- pipeStream.pipe(myFile);
320
- pipeStream.on("close", () => {
321
- myFile.close();
322
- res();
323
- });
324
- }
325
- });
326
- })
327
- );
328
- };
329
- }
330
- async write(uid, contents) {
331
- return new Promise((res) => {
332
- const x = fileStreams3[uid].write(contents);
333
- res(x);
334
- });
335
- }
336
- page(p) {
337
- return p;
338
- }
339
- click(selector, page) {
340
- return page.click(selector);
341
- }
342
- async focusOn(selector, p) {
343
- this.doInPage(p, (page) => {
344
- return page.focus(selector);
345
- });
346
- }
347
- async typeInto(value, p) {
348
- this.doInPage(p, (page) => {
349
- return page.keyboard.type(value);
350
- });
351
- }
352
- // setValue(value: string, p: string) {
353
- // this.doInPage(p, (page) => {
354
- // return page.keyboard.type(value);
355
- // });
356
- // }
357
- getAttribute(selector, attribute, p) {
358
- this.doInPage(p, (page) => {
359
- return page.$eval(selector, (input) => input.getAttribute(attribute));
360
- });
361
- }
362
- async getInnerHtml(selector, p) {
363
- return new Promise((res, rej) => {
364
- this.doInPage(p, async (page) => {
365
- const e = await page.$(selector);
366
- if (!e) {
367
- rej();
368
- } else {
369
- const text = await (await e.getProperty("textContent")).jsonValue();
370
- res(text);
371
- }
372
- });
373
- });
374
- }
375
- isDisabled(selector, p) {
376
- this.doInPage(p, async (page) => {
377
- return await page.$eval(selector, (input) => {
378
- return input.disabled;
379
- });
380
- });
381
- }
382
- screencastStop(s) {
383
- return recorders[s].stop();
384
- }
385
- async doInPage(p, cb) {
386
- (await this.browser.pages()).forEach((page) => {
387
- const frame = page.mainFrame();
388
- if (frame._id === p) {
389
- return cb(page);
390
- }
391
- });
392
- }
393
- };
394
-
395
- // src/utils/makePrompt.ts
396
- import fs2 from "fs";
397
- import path3 from "path";
398
-
399
- // src/utils/logFiles.ts
400
- var LOG_FILES = {
401
- TESTS: "tests.json",
402
- TYPE_ERRORS: "type_errors.txt",
403
- LINT_ERRORS: "lint_errors.txt",
404
- EXIT: "exit.log",
405
- MESSAGE: "message.txt",
406
- PROMPT: "prompt.txt",
407
- STDOUT: "stdout.log",
408
- STDERR: "stderr.log",
409
- INFO: "info.log",
410
- ERROR: "error.log",
411
- WARN: "warn.log",
412
- DEBUG: "debug.log"
413
- };
414
- var STANDARD_LOGS = {
415
- TESTS: "tests.json",
416
- TYPE_ERRORS: "type_errors.txt",
417
- LINT_ERRORS: "lint_errors.txt",
418
- EXIT: "exit.log",
419
- MESSAGE: "message.txt",
420
- PROMPT: "prompt.txt"
421
- };
422
- var RUNTIME_SPECIFIC_LOGS = {
423
- node: {
424
- STDOUT: "stdout.log",
425
- STDERR: "stderr.log"
426
- },
427
- web: {
428
- INFO: "info.log",
429
- ERROR: "error.log",
430
- WARN: "warn.log",
431
- DEBUG: "debug.log"
432
- },
433
- pure: {}
434
- // No runtime-specific logs for pure
435
- };
436
- var ALL_LOGS = {
437
- ...STANDARD_LOGS,
438
- ...Object.values(RUNTIME_SPECIFIC_LOGS).reduce((acc, logs) => ({ ...acc, ...logs }), {})
439
- };
440
- var getRuntimeLogs = (runtime) => {
441
- return {
442
- standard: Object.values(STANDARD_LOGS),
443
- runtimeSpecific: Object.values(RUNTIME_SPECIFIC_LOGS[runtime])
444
- };
445
- };
446
- function getLogFilesForRuntime(runtime) {
447
- const { standard, runtimeSpecific } = getRuntimeLogs(runtime);
448
- return [...standard, ...runtimeSpecific];
449
- }
450
-
451
- // src/utils/makePrompt.ts
452
- var makePrompt = async (summary, name, entryPoint, addableFiles, runtime) => {
453
- summary[entryPoint].prompt = "?";
454
- const promptPath = promptPather(entryPoint, runtime, name);
455
- const testDir = path3.join(
456
- "testeranto",
457
- "reports",
458
- name,
459
- entryPoint.split(".").slice(0, -1).join("."),
460
- runtime
461
- );
462
- if (!fs2.existsSync(testDir)) {
463
- fs2.mkdirSync(testDir, { recursive: true });
464
- }
465
- const testPaths = path3.join(testDir, LOG_FILES.TESTS);
466
- const lintPath = path3.join(testDir, LOG_FILES.LINT_ERRORS);
467
- const typePath = path3.join(testDir, LOG_FILES.TYPE_ERRORS);
468
- const messagePath = path3.join(testDir, LOG_FILES.MESSAGE);
469
- try {
470
- await Promise.all([
471
- fs2.promises.writeFile(
472
- promptPath,
473
- `
474
- ${addableFiles.map((x) => {
475
- return `/add ${x}`;
476
- }).join("\n")}
477
-
478
- /read node_modules/testeranto/docs/index.md
479
- /read node_modules/testeranto/docs/style.md
480
- /read node_modules/testeranto/docs/testing.ai.txt
481
- /read node_modules/testeranto/src/CoreTypes.ts
482
-
483
- /read ${testPaths}
484
- /read ${typePath}
485
- /read ${lintPath}
486
-
487
- /read ${getLogFilesForRuntime(runtime).map((p) => `${testDir}/${p}`).join(" ")}
488
- `
489
- ),
490
- fs2.promises.writeFile(
491
- messagePath,
492
- `
493
- There are 3 types of test reports.
494
- 1) bdd (highest priority)
495
- 2) type checker
496
- 3) static analysis (lowest priority)
497
-
498
- "tests.json" is the detailed result of the bdd tests.
499
- if these files do not exist, then something has gone badly wrong and needs to be addressed.
500
-
501
- "type_errors.txt" is the result of the type checker.
502
- if this file does not exist, then type check passed without errors;
503
-
504
- "lint_errors.txt" is the result of the static analysis.
505
- if this file does not exist, then static analysis passed without errors;
506
-
507
- BDD failures are the highest priority. Focus on passing BDD tests before addressing other concerns.
508
- Do not add error throwing/catching to the tests themselves.
509
- `
510
- )
511
- ]);
512
- } catch (e) {
513
- console.error(`Failed to write prompt files at ${testDir}`);
514
- console.error(e);
515
- throw e;
516
- }
517
- summary[entryPoint].prompt = `aider --model deepseek/deepseek-chat --load testeranto/${name}/reports/${runtime}/${entryPoint.split(".").slice(0, -1).join(".")}/prompt.txt`;
518
- };
519
- var makePromptInternal = (summary, name, entryPoint, addableFiles, runTime) => {
520
- if (runTime === "node") {
521
- return makePrompt(summary, name, entryPoint, addableFiles, "node");
522
- }
523
- if (runTime === "web") {
524
- return makePrompt(summary, name, entryPoint, addableFiles, "web");
525
- }
526
- if (runTime === "pure") {
527
- return makePrompt(summary, name, entryPoint, addableFiles, "pure");
528
- }
529
- };
530
-
531
- // src/PM/PM_WithEslintAndTsc.ts
532
- var eslint = new ESLint();
533
- var formatter = await eslint.loadFormatter(
534
- "./node_modules/testeranto/dist/prebuild/esbuildConfigs/eslint-formatter-testeranto.mjs"
535
- );
536
- var PM_WithEslintAndTsc = class extends PM_Base {
537
- constructor(configs, name, mode2) {
538
- super(configs);
539
- this.summary = {};
540
- this.tscCheck = async ({
541
- entrypoint,
542
- addableFiles,
543
- platform
544
- }) => {
545
- console.log(ansiC.green(ansiC.inverse(`tsc < ${entrypoint}`)));
546
- try {
547
- this.typeCheckIsRunning(entrypoint);
548
- } catch (e) {
549
- console.error("error in tscCheck");
550
- console.error(e);
551
- console.error(entrypoint);
552
- console.error(JSON.stringify(this.summary, null, 2));
553
- process.exit(-1);
554
- }
555
- const program = tsc.createProgramFromConfig({
556
- basePath: process.cwd(),
557
- // always required, used for relative paths
558
- configFilePath: "tsconfig.json",
559
- // config to inherit from (optional)
560
- compilerOptions: {
561
- outDir: tscPather(entrypoint, platform, this.name),
562
- // declaration: true,
563
- // skipLibCheck: true,
564
- noEmit: true
565
- },
566
- include: addableFiles
567
- //["src/**/*"],
568
- // exclude: ["node_modules", "../testeranto"],
569
- // exclude: ["**/*.test.ts", "**/*.spec.ts"],
570
- });
571
- const tscPath = tscPather(entrypoint, platform, this.name);
572
- const allDiagnostics = program.getSemanticDiagnostics();
573
- const results = [];
574
- allDiagnostics.forEach((diagnostic) => {
575
- if (diagnostic.file) {
576
- const { line, character } = ts.getLineAndCharacterOfPosition(
577
- diagnostic.file,
578
- diagnostic.start
579
- );
580
- const message = ts.flattenDiagnosticMessageText(
581
- diagnostic.messageText,
582
- "\n"
583
- );
584
- results.push(
585
- `${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`
586
- );
587
- } else {
588
- results.push(
589
- ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")
590
- );
591
- }
592
- });
593
- fs3.writeFileSync(tscPath, results.join("\n"));
594
- this.typeCheckIsNowDone(entrypoint, results.length);
595
- };
596
- this.eslintCheck = async (entrypoint, platform, addableFiles) => {
597
- console.log(ansiC.green(ansiC.inverse(`eslint < ${entrypoint}`)));
598
- try {
599
- this.lintIsRunning(entrypoint);
600
- } catch (e) {
601
- console.error("error in eslintCheck");
602
- console.error(e);
603
- console.error(entrypoint);
604
- console.error(JSON.stringify(this.summary, null, 2));
605
- process.exit(-1);
606
- }
607
- const filepath = lintPather(entrypoint, platform, this.name);
608
- if (fs3.existsSync(filepath))
609
- fs3.rmSync(filepath);
610
- const results = (await eslint.lintFiles(addableFiles)).filter((r) => r.messages.length).filter((r) => {
611
- return r.messages[0].ruleId !== null;
612
- }).map((r) => {
613
- delete r.source;
614
- return r;
615
- });
616
- fs3.writeFileSync(filepath, await formatter.format(results));
617
- this.lintIsNowDone(entrypoint, results.length);
618
- };
619
- this.makePrompt = async (entryPoint, addableFiles, platform) => {
620
- await makePromptInternal(
621
- this.summary,
622
- this.name,
623
- entryPoint,
624
- addableFiles,
625
- platform
626
- );
627
- this.checkForShutdown();
628
- };
629
- this.typeCheckIsRunning = (src) => {
630
- if (!this.summary[src]) {
631
- throw `this.summary[${src}] is undefined`;
632
- }
633
- this.summary[src].typeErrors = "?";
634
- };
635
- this.typeCheckIsNowDone = (src, failures) => {
636
- if (!this.summary[src]) {
637
- throw `this.summary[${src}] is undefined`;
638
- }
639
- if (failures === 0) {
640
- console.log(ansiC.green(ansiC.inverse(`tsc > ${src}`)));
641
- } else {
642
- console.log(
643
- ansiC.red(ansiC.inverse(`tsc > ${src} failed ${failures} times`))
644
- );
645
- }
646
- this.summary[src].typeErrors = failures;
647
- this.writeBigBoard();
648
- this.checkForShutdown();
649
- };
650
- this.lintIsRunning = (src) => {
651
- if (!this.summary[src]) {
652
- throw `this.summary[${src}] is undefined`;
653
- }
654
- this.summary[src].staticErrors = "?";
655
- this.writeBigBoard();
656
- };
657
- this.lintIsNowDone = (src, failures) => {
658
- if (!this.summary[src]) {
659
- throw `this.summary[${src}] is undefined`;
660
- }
661
- if (failures === 0) {
662
- console.log(ansiC.green(ansiC.inverse(`eslint > ${src}`)));
663
- } else {
664
- console.log(
665
- ansiC.red(ansiC.inverse(`eslint > ${src} failed ${failures} times`))
666
- );
667
- }
668
- this.summary[src].staticErrors = failures;
669
- this.writeBigBoard();
670
- this.checkForShutdown();
671
- };
672
- this.bddTestIsRunning = (src) => {
673
- if (!this.summary[src]) {
674
- throw `this.summary[${src}] is undefined`;
675
- }
676
- this.summary[src].runTimeErrors = "?";
677
- this.writeBigBoard();
678
- };
679
- this.bddTestIsNowDone = (src, failures) => {
680
- if (!this.summary[src]) {
681
- throw `this.summary[${src}] is undefined`;
682
- }
683
- this.summary[src].runTimeErrors = failures;
684
- this.writeBigBoard();
685
- this.checkForShutdown();
686
- };
687
- this.writeBigBoard = () => {
688
- const summaryPath = `./testeranto/reports/${this.name}/summary.json`;
689
- const summaryData = JSON.stringify(this.summary, null, 2);
690
- fs3.writeFileSync(summaryPath, summaryData);
691
- this.broadcast({
692
- type: "summaryUpdate",
693
- data: this.summary
694
- });
695
- };
696
- this.name = name;
697
- this.mode = mode2;
698
- this.summary = {};
699
- this.configs.tests.forEach(([t, rt, tr, sidecars]) => {
700
- this.ensureSummaryEntry(t);
701
- sidecars.forEach(([sidecarName]) => {
702
- this.ensureSummaryEntry(sidecarName, true);
703
- });
704
- });
705
- }
706
- ensureSummaryEntry(src, isSidecar = false) {
707
- if (!this.summary[src]) {
708
- this.summary[src] = {
709
- typeErrors: void 0,
710
- staticErrors: void 0,
711
- runTimeErrors: void 0,
712
- prompt: void 0,
713
- failingFeatures: {}
714
- };
715
- if (isSidecar) {
716
- }
717
- }
718
- return this.summary[src];
719
- }
720
- };
721
-
722
- // src/PM/main.ts
723
- var changes = {};
724
- var fileHashes = {};
725
- var files2 = {};
726
- var screenshots2 = {};
727
- function runtimeLogs(runtime, reportDest) {
728
- const safeDest = reportDest || `testeranto/reports/default_${Date.now()}`;
729
- try {
730
- if (!fs4.existsSync(safeDest)) {
731
- fs4.mkdirSync(safeDest, { recursive: true });
732
- }
733
- if (runtime === "node") {
734
- return {
735
- stdout: fs4.createWriteStream(`${safeDest}/stdout.log`),
736
- stderr: fs4.createWriteStream(`${safeDest}/stderr.log`),
737
- exit: fs4.createWriteStream(`${safeDest}/exit.log`)
738
- };
739
- } else if (runtime === "web") {
740
- return {
741
- info: fs4.createWriteStream(`${safeDest}/info.log`),
742
- warn: fs4.createWriteStream(`${safeDest}/warn.log`),
743
- error: fs4.createWriteStream(`${safeDest}/error.log`),
744
- debug: fs4.createWriteStream(`${safeDest}/debug.log`),
745
- exit: fs4.createWriteStream(`${safeDest}/exit.log`)
746
- };
747
- } else if (runtime === "pure") {
748
- return {
749
- exit: fs4.createWriteStream(`${safeDest}/exit.log`)
750
- };
751
- } else {
752
- throw `unknown runtime: ${runtime}`;
753
- }
754
- } catch (e) {
755
- console.error(`Failed to create log streams in ${safeDest}:`, e);
756
- throw e;
757
- }
758
- }
759
- function createLogStreams(reportDest, runtime) {
760
- if (!fs4.existsSync(reportDest)) {
761
- fs4.mkdirSync(reportDest, { recursive: true });
762
- }
763
- const streams = runtimeLogs(runtime, reportDest);
764
- const safeDest = reportDest || `testeranto/reports/default_${Date.now()}`;
765
- try {
766
- if (!fs4.existsSync(safeDest)) {
767
- fs4.mkdirSync(safeDest, { recursive: true });
768
- }
769
- const streams2 = runtimeLogs(runtime, safeDest);
770
- return {
771
- ...streams2,
772
- closeAll: () => {
773
- Object.values(streams2).forEach(
774
- (stream) => !stream.closed && stream.close()
775
- );
776
- },
777
- writeExitCode: (code, error) => {
778
- if (error) {
779
- streams2.exit.write(`Error: ${error.message}
780
- `);
781
- if (error.stack) {
782
- streams2.exit.write(`Stack Trace:
783
- ${error.stack}
784
- `);
785
- }
786
- }
787
- streams2.exit.write(`${code}
788
- `);
789
- },
790
- exit: streams2.exit
791
- };
792
- } catch (e) {
793
- console.error(`Failed to create log streams in ${safeDest}:`, e);
794
- throw e;
795
- }
796
- }
797
- async function fileHash(filePath, algorithm = "md5") {
798
- return new Promise((resolve, reject) => {
799
- const hash = crypto.createHash(algorithm);
800
- const fileStream = fs4.createReadStream(filePath);
801
- fileStream.on("data", (data) => {
802
- hash.update(data);
803
- });
804
- fileStream.on("end", () => {
805
- const fileHash2 = hash.digest("hex");
806
- resolve(fileHash2);
807
- });
808
- fileStream.on("error", (error) => {
809
- reject(`Error reading file: ${error.message}`);
810
- });
811
- });
812
- }
813
- var statusMessagePretty = (failures, test, runtime) => {
814
- if (failures === 0) {
815
- console.log(ansiC2.green(ansiC2.inverse(`${runtime} > ${test}`)));
816
- } else if (failures > 0) {
817
- console.log(
818
- ansiC2.red(
819
- ansiC2.inverse(
820
- `${runtime} > ${test} failed ${failures} times (exit code: ${failures})`
821
- )
822
- )
823
- );
824
- } else {
825
- console.log(
826
- ansiC2.red(ansiC2.inverse(`${runtime} > ${test} crashed (exit code: -1)`))
827
- );
828
- }
829
- };
830
- async function writeFileAndCreateDir(filePath, data) {
831
- const dirPath = path4.dirname(filePath);
832
- try {
833
- await fs4.promises.mkdir(dirPath, { recursive: true });
834
- await fs4.writeFileSync(filePath, data);
835
- } catch (error) {
836
- console.error(`Error writing file: ${error}`);
837
- }
838
- }
839
- var filesHash = async (files3, algorithm = "md5") => {
840
- return new Promise((resolve, reject) => {
841
- resolve(
842
- files3.reduce(async (mm, f2) => {
843
- return await mm + await fileHash(f2);
844
- }, Promise.resolve(""))
845
- );
846
- });
847
- };
848
- function isValidUrl(string) {
849
- try {
850
- new URL(string);
851
- return true;
852
- } catch (err) {
853
- return false;
854
- }
855
- }
856
- async function pollForFile(path5, timeout = 2e3) {
857
- const intervalObj = setInterval(function() {
858
- const file = path5;
859
- const fileExists = fs4.existsSync(file);
860
- if (fileExists) {
861
- clearInterval(intervalObj);
862
- }
863
- }, timeout);
864
- }
865
- var PM_Main = class extends PM_WithEslintAndTsc {
866
- constructor(configs, name, mode2) {
867
- super(configs, name, mode2);
868
- this.logStreams = {};
869
- this.sidecars = {};
870
- this.clients = /* @__PURE__ */ new Set();
871
- this.runningProcesses = /* @__PURE__ */ new Map();
872
- this.allProcesses = /* @__PURE__ */ new Map();
873
- this.processLogs = /* @__PURE__ */ new Map();
874
- this.getRunnables = (tests, testName, payload = {
875
- nodeEntryPoints: {},
876
- nodeEntryPointSidecars: {},
877
- webEntryPoints: {},
878
- webEntryPointSidecars: {},
879
- pureEntryPoints: {},
880
- pureEntryPointSidecars: {}
881
- }) => {
882
- return getRunnables(tests, testName, payload);
883
- };
884
- this.launchPure = async (src, dest) => {
885
- console.log(ansiC2.green(ansiC2.inverse(`pure < ${src}`)));
886
- this.bddTestIsRunning(src);
887
- const reportDest = `testeranto/reports/${this.name}/${src.split(".").slice(0, -1).join(".")}/pure`;
888
- if (!fs4.existsSync(reportDest)) {
889
- fs4.mkdirSync(reportDest, { recursive: true });
890
- }
891
- const destFolder = dest.replace(".mjs", "");
892
- let argz2 = "";
893
- const testConfig = this.configs.tests.find((t) => {
894
- return t[0] === src;
895
- });
896
- if (!testConfig) {
897
- console.log(ansiC2.inverse("missing test config! Exiting ungracefully!"));
898
- process.exit(-1);
899
- }
900
- const testConfigResource = testConfig[2];
901
- const portsToUse = [];
902
- if (testConfigResource.ports === 0) {
903
- argz2 = JSON.stringify({
904
- scheduled: true,
905
- name: src,
906
- ports: portsToUse,
907
- fs: reportDest,
908
- browserWSEndpoint: this.browser.wsEndpoint()
909
- });
910
- } else if (testConfigResource.ports > 0) {
911
- const openPorts = Object.entries(this.ports).filter(
912
- ([portnumber, status]) => status === ""
913
- );
914
- if (openPorts.length >= testConfigResource.ports) {
915
- for (let i = 0; i < testConfigResource.ports; i++) {
916
- portsToUse.push(openPorts[i][0]);
917
- this.ports[openPorts[i][0]] = src;
918
- }
919
- argz2 = JSON.stringify({
920
- scheduled: true,
921
- name: src,
922
- ports: portsToUse,
923
- fs: destFolder,
924
- browserWSEndpoint: this.browser.wsEndpoint()
925
- });
926
- } else {
927
- this.queue.push(src);
928
- return [Math.random(), argz2];
929
- }
930
- } else {
931
- console.error("negative port makes no sense", src);
932
- process.exit(-1);
933
- }
934
- const builtfile = dest;
935
- const logs = createLogStreams(reportDest, "pure");
936
- try {
937
- await import(`${builtfile}?cacheBust=${Date.now()}`).then((module) => {
938
- const originalConsole = { ...console };
939
- return module.default.then((defaultModule) => {
940
- defaultModule.receiveTestResourceConfig(argz2).then(async (results) => {
941
- statusMessagePretty(results.fails, src, "pure");
942
- this.bddTestIsNowDone(src, results.fails);
943
- }).catch((e1) => {
944
- console.log(
945
- ansiC2.red(`launchPure - ${src} errored with: ${e1.stack}`)
946
- );
947
- this.bddTestIsNowDone(src, -1);
948
- statusMessagePretty(-1, src, "pure");
949
- });
950
- }).catch((e2) => {
951
- console.log(
952
- ansiColors.red(
953
- `pure ! ${src} failed to execute. No "tests.json" file was generated. Check the logs for more info`
954
- )
955
- );
956
- logs.exit.write(e2.stack);
957
- logs.exit.write(-1);
958
- this.bddTestIsNowDone(src, -1);
959
- statusMessagePretty(-1, src, "pure");
960
- }).finally((x) => {
961
- });
962
- });
963
- } catch (e3) {
964
- logs.writeExitCode(-1, e3);
965
- console.log(
966
- ansiC2.red(
967
- ansiC2.inverse(
968
- `${src} 1 errored with: ${e3}. Check logs for more info`
969
- )
970
- )
971
- );
972
- logs.exit.write(e3.stack);
973
- logs.exit.write("-1");
974
- this.bddTestIsNowDone(src, -1);
975
- statusMessagePretty(-1, src, "pure");
976
- }
977
- for (let i = 0; i <= portsToUse.length; i++) {
978
- if (portsToUse[i]) {
979
- this.ports[portsToUse[i]] = "";
980
- }
981
- }
982
- };
983
- this.launchNode = async (src, dest) => {
984
- console.log(ansiC2.green(ansiC2.inverse(`node < ${src}`)));
985
- this.bddTestIsRunning(src);
986
- const reportDest = `testeranto/reports/${this.name}/${src.split(".").slice(0, -1).join(".")}/node`;
987
- if (!fs4.existsSync(reportDest)) {
988
- fs4.mkdirSync(reportDest, { recursive: true });
989
- }
990
- let testResources = "";
991
- const testConfig = this.configs.tests.find((t) => {
992
- return t[0] === src;
993
- });
994
- if (!testConfig) {
995
- console.log(
996
- ansiC2.inverse(`missing test config! Exiting ungracefully for '${src}'`)
997
- );
998
- process.exit(-1);
999
- }
1000
- const testConfigResource = testConfig[2];
1001
- const portsToUse = [];
1002
- if (testConfigResource.ports === 0) {
1003
- const t = {
1004
- name: src,
1005
- // ports: portsToUse.map((v) => Number(v)),
1006
- ports: [],
1007
- fs: reportDest,
1008
- browserWSEndpoint: this.browser.wsEndpoint()
1009
- };
1010
- testResources = JSON.stringify(t);
1011
- } else if (testConfigResource.ports > 0) {
1012
- const openPorts = Object.entries(this.ports).filter(
1013
- ([portnumber, portopen]) => portopen === ""
1014
- );
1015
- if (openPorts.length >= testConfigResource.ports) {
1016
- for (let i = 0; i < testConfigResource.ports; i++) {
1017
- portsToUse.push(openPorts[i][0]);
1018
- this.ports[openPorts[i][0]] = src;
1019
- }
1020
- testResources = JSON.stringify({
1021
- scheduled: true,
1022
- name: src,
1023
- ports: portsToUse,
1024
- fs: reportDest,
1025
- browserWSEndpoint: this.browser.wsEndpoint()
1026
- });
1027
- } else {
1028
- console.log(
1029
- ansiC2.red(
1030
- `node: cannot run ${src} because there are no open ports ATM. This job will be enqueued and run again run a port is available`
1031
- )
1032
- );
1033
- this.queue.push(src);
1034
- return [Math.random(), argz];
1035
- }
1036
- } else {
1037
- console.error("negative port makes no sense", src);
1038
- process.exit(-1);
1039
- }
1040
- const builtfile = dest;
1041
- let haltReturns = false;
1042
- const ipcfile = "/tmp/tpipe_" + Math.random();
1043
- const child = spawn(
1044
- "node",
1045
- // "node",
1046
- [
1047
- // "--inspect-brk",
1048
- builtfile,
1049
- testResources,
1050
- ipcfile
1051
- ],
1052
- {
1053
- stdio: ["pipe", "pipe", "pipe", "ipc"]
1054
- }
1055
- );
1056
- let buffer = new Buffer("");
1057
- const server = net.createServer((socket) => {
1058
- const queue = new Queue();
1059
- socket.on("data", (data) => {
1060
- buffer = Buffer.concat([buffer, data]);
1061
- for (let b = 0; b < buffer.length + 1; b++) {
1062
- const c = buffer.slice(0, b);
1063
- let d;
1064
- try {
1065
- d = JSON.parse(c.toString());
1066
- queue.enqueue(d);
1067
- buffer = buffer.slice(b, buffer.length + 1);
1068
- b = 0;
1069
- } catch (e) {
1070
- }
1071
- }
1072
- while (queue.size() > 0) {
1073
- const message = queue.dequeue();
1074
- if (message) {
1075
- this.mapping().forEach(async ([command, func]) => {
1076
- if (message[0] === command) {
1077
- const x = message.slice(1, -1);
1078
- const r = await this[command](...x);
1079
- if (!haltReturns) {
1080
- child.send(
1081
- JSON.stringify({
1082
- payload: r,
1083
- key: message[message.length - 1]
1084
- })
1085
- );
1086
- }
1087
- }
1088
- });
1089
- }
1090
- }
1091
- });
1092
- });
1093
- const logs = createLogStreams(reportDest, "node");
1094
- server.listen(ipcfile, () => {
1095
- child.stdout?.on("data", (data) => {
1096
- logs.stdout?.write(data);
1097
- });
1098
- child.stderr?.on("data", (data) => {
1099
- logs.stderr?.write(data);
1100
- });
1101
- child.on("error", (err) => {
1102
- });
1103
- child.on("close", (code) => {
1104
- const exitCode = code === null ? -1 : code;
1105
- if (exitCode < 0) {
1106
- logs.writeExitCode(
1107
- exitCode,
1108
- new Error("Process crashed or was terminated")
1109
- );
1110
- } else {
1111
- logs.writeExitCode(exitCode);
1112
- }
1113
- logs.closeAll();
1114
- server.close();
1115
- if (!files2[src]) {
1116
- files2[src] = /* @__PURE__ */ new Set();
1117
- }
1118
- if (exitCode === 255) {
1119
- console.log(
1120
- ansiColors.red(
1121
- `node ! ${src} failed to execute. No "tests.json" file was generated. Check ${reportDest}/stderr.log for more info`
1122
- )
1123
- );
1124
- this.bddTestIsNowDone(src, -1);
1125
- statusMessagePretty(-1, src, "node");
1126
- return;
1127
- } else if (exitCode === 0) {
1128
- this.bddTestIsNowDone(src, 0);
1129
- statusMessagePretty(0, src, "node");
1130
- } else {
1131
- this.bddTestIsNowDone(src, exitCode);
1132
- statusMessagePretty(exitCode, src, "node");
1133
- }
1134
- haltReturns = true;
1135
- });
1136
- child.on("exit", (code) => {
1137
- haltReturns = true;
1138
- for (let i = 0; i <= portsToUse.length; i++) {
1139
- if (portsToUse[i]) {
1140
- this.ports[portsToUse[i]] = "";
1141
- }
1142
- }
1143
- });
1144
- child.on("error", (e) => {
1145
- console.log("error");
1146
- haltReturns = true;
1147
- console.log(
1148
- ansiC2.red(
1149
- ansiC2.inverse(
1150
- `${src} errored with: ${e.name}. Check error logs for more info`
1151
- )
1152
- )
1153
- );
1154
- this.bddTestIsNowDone(src, -1);
1155
- statusMessagePretty(-1, src, "node");
1156
- });
1157
- });
1158
- };
1159
- this.launchWebSideCar = async (testConfig) => {
1160
- const src = testConfig[0];
1161
- const dest = src.split(".").slice(0, -1).join(".");
1162
- const destFolder = dest.replace(".mjs", "");
1163
- console.log(ansiC2.green(ansiC2.inverse(`launchWebSideCar ${src}`)));
1164
- const logs = createLogStreams(dest, "web");
1165
- return new Promise((res, rej) => {
1166
- this.browser.newPage().then(async (page) => {
1167
- this.mapping().forEach(async ([command, func]) => {
1168
- page.exposeFunction(command, func);
1169
- });
1170
- const close = () => {
1171
- if (!files2[src]) {
1172
- files2[src] = /* @__PURE__ */ new Set();
1173
- }
1174
- delete files2[src];
1175
- Promise.all(screenshots2[src] || []).then(() => {
1176
- delete screenshots2[src];
1177
- page.close();
1178
- });
1179
- };
1180
- page.on("pageerror", (err) => {
1181
- console.debug(`Error from ${src}: [${err.name}] `);
1182
- console.debug(`Error from ${src}: [${err.name}] `);
1183
- if (err.cause) {
1184
- console.debug(`Error from ${src} cause: [${err.cause}] `);
1185
- }
1186
- if (err.stack) {
1187
- console.debug(`Error from stack ${src}: [${err.stack}] `);
1188
- }
1189
- console.debug(`Error from message ${src}: [${err.message}] `);
1190
- this.bddTestIsNowDone(src, -1);
1191
- close();
1192
- });
1193
- page.on("console", (log) => {
1194
- const msg = `${log.text()}
1195
- ${JSON.stringify(
1196
- log.location()
1197
- )}
1198
- ${JSON.stringify(log.stackTrace())}
1199
- `;
1200
- switch (log.type()) {
1201
- case "info":
1202
- logs.info?.write(msg);
1203
- break;
1204
- case "warn":
1205
- logs.warn?.write(msg);
1206
- break;
1207
- case "error":
1208
- logs.error?.write(msg);
1209
- break;
1210
- case "debug":
1211
- logs.debug?.write(msg);
1212
- break;
1213
- default:
1214
- break;
1215
- }
1216
- });
1217
- await page.goto(`file://${`${destFolder}.html`}`, {});
1218
- const webArgz = JSON.stringify({
1219
- name: dest,
1220
- ports: [].toString(),
1221
- fs: dest,
1222
- browserWSEndpoint: this.browser.wsEndpoint()
1223
- });
1224
- const d = `${dest}?cacheBust=${Date.now()}`;
1225
- const evaluation = `
1226
- import('${d}').then(async (x) => {
1227
-
1228
- try {
1229
- return await (await x.default).receiveTestResourceConfig(${webArgz})
1230
- } catch (e) {
1231
- console.log("fail", e.toString())
1232
- }
1233
- })`;
1234
- await page.evaluate(evaluation).then(async ({ fails, failed, features }) => {
1235
- statusMessagePretty(fails, src, "web");
1236
- this.bddTestIsNowDone(src, fails);
1237
- }).catch((e) => {
1238
- console.log(
1239
- ansiC2.red(
1240
- ansiC2.inverse(`launchWebSidecar - ${src} errored with: ${e}`)
1241
- )
1242
- );
1243
- }).finally(() => {
1244
- this.bddTestIsNowDone(src, -1);
1245
- close();
1246
- });
1247
- return page;
1248
- }).then(async (page) => {
1249
- await page.goto(`file://${`${dest}.html`}`, {});
1250
- res([Math.random(), page]);
1251
- });
1252
- });
1253
- };
1254
- this.launchNodeSideCar = async (sidecar) => {
1255
- const src = sidecar[0];
1256
- const dest = process.cwd() + `/testeranto/bundles/node/${this.name}/${sidecar[0]}`;
1257
- const d = dest + ".mjs";
1258
- console.log(ansiC2.green(ansiC2.inverse(`launchNodeSideCar ${sidecar[0]}`)));
1259
- const destFolder = dest.replace(".ts", "");
1260
- const reportDest = `testeranto/reports/${this.name}/${src.split(".").slice(0, -1).join(".")}/node`;
1261
- const argz2 = {
1262
- name: sidecar[0],
1263
- ports: [],
1264
- fs: destFolder,
1265
- browserWSEndpoint: this.browser.wsEndpoint()
1266
- };
1267
- const testReq = sidecar[2];
1268
- const logs = createLogStreams(dest, "node");
1269
- const portsToUse = [];
1270
- if (testReq.ports === 0) {
1271
- } else if (testReq.ports > 0) {
1272
- const openPorts = Object.entries(this.ports).filter(
1273
- ([portnumber, portopen]) => portopen === ""
1274
- );
1275
- if (openPorts.length >= testReq.ports) {
1276
- for (let i = 0; i < testReq.ports; i++) {
1277
- portsToUse.push(Number(openPorts[i][0]));
1278
- this.ports[openPorts[i][0]] = src;
1279
- }
1280
- argz2.ports = portsToUse;
1281
- const builtfile = destFolder + ".mjs";
1282
- let haltReturns = false;
1283
- let buffer = new Buffer("");
1284
- const server = net.createServer((socket) => {
1285
- socket.on("data", (data) => {
1286
- buffer = Buffer.concat([buffer, data]);
1287
- const messages = [];
1288
- for (let b = 0; b < buffer.length + 1; b++) {
1289
- const c = buffer.slice(0, b);
1290
- let d2;
1291
- try {
1292
- d2 = JSON.parse(c.toString());
1293
- messages.push(d2);
1294
- buffer = buffer.slice(b, buffer.length + 1);
1295
- b = 0;
1296
- } catch (e) {
1297
- }
1298
- }
1299
- messages.forEach(async (payload) => {
1300
- this.mapping().forEach(async ([command, func]) => {
1301
- if (payload[0] === command) {
1302
- const x = payload.slice(1, -1);
1303
- const r2 = await this[command](...x);
1304
- if (!haltReturns) {
1305
- child.send(
1306
- JSON.stringify({
1307
- payload: r2,
1308
- key: payload[payload.length - 1]
1309
- })
1310
- );
1311
- }
1312
- }
1313
- });
1314
- });
1315
- });
1316
- });
1317
- const child = spawn("node", [builtfile, JSON.stringify(argz2)], {
1318
- stdio: ["pipe", "pipe", "pipe", "ipc"]
1319
- // silent: true
1320
- });
1321
- const p = "/tmp/tpipe" + Math.random();
1322
- server.listen(p, () => {
1323
- child.on("close", (code) => {
1324
- server.close();
1325
- haltReturns = true;
1326
- });
1327
- child.on("exit", (code) => {
1328
- haltReturns = true;
1329
- for (let i = 0; i <= portsToUse.length; i++) {
1330
- if (portsToUse[i]) {
1331
- this.ports[portsToUse[i]] = "";
1332
- }
1333
- }
1334
- });
1335
- child.on("error", (e) => {
1336
- if (fs4.existsSync(p)) {
1337
- fs4.rmSync(p);
1338
- }
1339
- haltReturns = true;
1340
- console.log(
1341
- ansiC2.red(
1342
- ansiC2.inverse(
1343
- `launchNodeSideCar - ${src} errored with: ${e.name}. Check logs for more info`
1344
- )
1345
- )
1346
- );
1347
- logs.error?.write(e.toString() + "\n");
1348
- });
1349
- });
1350
- child.send({ path: p });
1351
- const r = Math.random();
1352
- this.nodeSidecars[r] = child;
1353
- return [r, argz2];
1354
- } else {
1355
- console.log(
1356
- ansiC2.red(
1357
- `cannot ${src} because there are no open ports. the job will be unqueued`
1358
- )
1359
- );
1360
- this.queue.push(sidecar[0]);
1361
- return [Math.random(), argz2];
1362
- }
1363
- } else {
1364
- console.error("negative port makes no sense", sidecar[0]);
1365
- process.exit(-1);
1366
- }
1367
- };
1368
- this.stopPureSideCar = async (uid) => {
1369
- console.log(ansiC2.green(ansiC2.inverse(`stopPureSideCar ${uid}`)));
1370
- await this.sidecars[uid].shutdown();
1371
- return;
1372
- };
1373
- this.launchPureSideCar = async (sidecar) => {
1374
- console.log(ansiC2.green(ansiC2.inverse(`launchPureSideCar ${sidecar[0]}`)));
1375
- const r = Math.random();
1376
- const dest = process.cwd() + `/testeranto/bundles/pure/${this.name}/${sidecar[0]}`;
1377
- const builtfile = dest.split(".").slice(0, -1).concat("mjs").join(".");
1378
- const destFolder = dest.replace(".mjs", "");
1379
- let argz2;
1380
- const z = sidecar[2];
1381
- const testConfigResource = sidecar[2];
1382
- const src = sidecar[0];
1383
- const portsToUse = [];
1384
- if (testConfigResource.ports === 0) {
1385
- argz2 = {
1386
- // scheduled: true,
1387
- name: src,
1388
- ports: portsToUse,
1389
- fs: destFolder,
1390
- browserWSEndpoint: this.browser.wsEndpoint()
1391
- };
1392
- } else if (testConfigResource.ports > 0) {
1393
- const openPorts = Object.entries(this.ports).filter(
1394
- ([portnumber, portopen]) => portopen === ""
1395
- );
1396
- if (openPorts.length >= testConfigResource.ports) {
1397
- for (let i = 0; i < testConfigResource.ports; i++) {
1398
- portsToUse.push(Number(openPorts[i][0]));
1399
- this.ports[openPorts[i][0]] = src;
1400
- }
1401
- argz2 = {
1402
- // scheduled: true,
1403
- name: src,
1404
- // ports: [3333],
1405
- ports: portsToUse,
1406
- fs: ".",
1407
- browserWSEndpoint: this.browser.wsEndpoint()
1408
- };
1409
- } else {
1410
- this.queue.push(src);
1411
- }
1412
- } else {
1413
- console.error("negative port makes no sense", src);
1414
- process.exit(-1);
1415
- }
1416
- await import(`${builtfile}?cacheBust=${Date.now()}`).then((module) => {
1417
- if (!this.pureSidecars)
1418
- this.pureSidecars = {};
1419
- this.pureSidecars[r] = module.default;
1420
- this.pureSidecars[r].start(argz2);
1421
- });
1422
- return [r, argz2];
1423
- };
1424
- this.launchWeb = async (src, dest) => {
1425
- console.log(ansiC2.green(ansiC2.inverse(`web < ${src}`)));
1426
- this.bddTestIsRunning(src);
1427
- const reportDest = `testeranto/reports/${this.name}/${src.split(".").slice(0, -1).join(".")}/web`;
1428
- if (!fs4.existsSync(reportDest)) {
1429
- fs4.mkdirSync(reportDest, { recursive: true });
1430
- }
1431
- const destFolder = dest.replace(".mjs", "");
1432
- const webArgz = JSON.stringify({
1433
- name: src,
1434
- ports: [].toString(),
1435
- fs: reportDest,
1436
- browserWSEndpoint: this.browser.wsEndpoint()
1437
- });
1438
- const d = `${dest}?cacheBust=${Date.now()}`;
1439
- const logs = createLogStreams(reportDest, "web");
1440
- this.browser.newPage().then((page) => {
1441
- page.on("console", (log) => {
1442
- const msg = `${log.text()}
1443
- `;
1444
- switch (log.type()) {
1445
- case "info":
1446
- logs.info?.write(msg);
1447
- break;
1448
- case "warn":
1449
- logs.warn?.write(msg);
1450
- break;
1451
- case "error":
1452
- logs.error?.write(msg);
1453
- break;
1454
- case "debug":
1455
- logs.debug?.write(msg);
1456
- break;
1457
- default:
1458
- break;
1459
- }
1460
- });
1461
- page.on("close", () => {
1462
- logs.writeExitCode(0);
1463
- logs.closeAll();
1464
- logs.closeAll();
1465
- });
1466
- this.mapping().forEach(async ([command, func]) => {
1467
- if (command === "page") {
1468
- page.exposeFunction(command, (x) => {
1469
- if (x) {
1470
- return func(x);
1471
- } else {
1472
- return func(page.mainFrame()._id);
1473
- }
1474
- });
1475
- } else {
1476
- return page.exposeFunction(command, func);
1477
- }
1478
- });
1479
- return page;
1480
- }).then(async (page) => {
1481
- const close = () => {
1482
- if (!files2[src]) {
1483
- files2[src] = /* @__PURE__ */ new Set();
1484
- }
1485
- delete files2[src];
1486
- Promise.all(screenshots2[src] || []).then(() => {
1487
- delete screenshots2[src];
1488
- page.close();
1489
- });
1490
- return;
1491
- };
1492
- page.on("pageerror", (err) => {
1493
- logs.writeExitCode(-1, err);
1494
- console.log(
1495
- ansiColors.red(
1496
- `web ! ${src} failed to execute No "tests.json" file was generated. Check ${reportDest}/error.log for more info`
1497
- )
1498
- );
1499
- this.bddTestIsNowDone(src, -1);
1500
- close();
1501
- });
1502
- await page.goto(`file://${`${destFolder}.html`}`, {});
1503
- await page.evaluate(
1504
- `
1505
- import('${d}').then(async (x) => {
1506
- try {
1507
- return await (await x.default).receiveTestResourceConfig(${webArgz})
1508
- } catch (e) {
1509
- console.log("web run failure", e.toString())
1510
- }
1511
- })
1512
- `
1513
- ).then(async ({ fails, failed, features }) => {
1514
- statusMessagePretty(fails, src, "web");
1515
- this.bddTestIsNowDone(src, fails);
1516
- }).catch((e) => {
1517
- console.log(ansiC2.red(ansiC2.inverse(e.stack)));
1518
- console.log(
1519
- ansiC2.red(
1520
- ansiC2.inverse(
1521
- `web ! ${src} failed to execute. No "tests.json" file was generated. Check logs for more info`
1522
- )
1523
- )
1524
- );
1525
- this.bddTestIsNowDone(src, -1);
1526
- }).finally(() => {
1527
- close();
1528
- });
1529
- return page;
1530
- });
1531
- };
1532
- this.receiveFeaturesV2 = (reportDest, srcTest, platform) => {
1533
- const featureDestination = path4.resolve(
1534
- process.cwd(),
1535
- "reports",
1536
- "features",
1537
- "strings",
1538
- srcTest.split(".").slice(0, -1).join(".") + ".features.txt"
1539
- );
1540
- const testReportPath = `${reportDest}/tests.json`;
1541
- if (!fs4.existsSync(testReportPath)) {
1542
- console.error(`tests.json not found at: ${testReportPath}`);
1543
- return;
1544
- }
1545
- const testReport = JSON.parse(fs4.readFileSync(testReportPath, "utf8"));
1546
- if (testReport.tests) {
1547
- testReport.tests.forEach((test) => {
1548
- test.fullPath = path4.resolve(process.cwd(), srcTest);
1549
- });
1550
- }
1551
- testReport.fullPath = path4.resolve(process.cwd(), srcTest);
1552
- fs4.writeFileSync(testReportPath, JSON.stringify(testReport, null, 2));
1553
- testReport.features.reduce(async (mm, featureStringKey) => {
1554
- const accum = await mm;
1555
- const isUrl = isValidUrl(featureStringKey);
1556
- if (isUrl) {
1557
- const u = new URL(featureStringKey);
1558
- if (u.protocol === "file:") {
1559
- const newPath = `${process.cwd()}/testeranto/features/internal/${path4.relative(
1560
- process.cwd(),
1561
- u.pathname
1562
- )}`;
1563
- accum.files.push(u.pathname);
1564
- } else if (u.protocol === "http:" || u.protocol === "https:") {
1565
- const newPath = `${process.cwd()}/testeranto/features/external/${u.hostname}${u.pathname}`;
1566
- const body = await this.configs.featureIngestor(featureStringKey);
1567
- writeFileAndCreateDir(newPath, body);
1568
- accum.files.push(newPath);
1569
- }
1570
- } else {
1571
- await fs4.promises.mkdir(path4.dirname(featureDestination), {
1572
- recursive: true
1573
- });
1574
- accum.strings.push(featureStringKey);
1575
- }
1576
- return accum;
1577
- }, Promise.resolve({ files: [], strings: [] })).then(({ files: files3, strings }) => {
1578
- fs4.writeFileSync(
1579
- `testeranto/reports/${this.name}/${srcTest.split(".").slice(0, -1).join(".")}/${platform}/featurePrompt.txt`,
1580
- files3.map((f2) => {
1581
- return `/read ${f2}`;
1582
- }).join("\n")
1583
- );
1584
- });
1585
- testReport.givens.forEach((g) => {
1586
- if (g.failed === true) {
1587
- this.summary[srcTest].failingFeatures[g.key] = g.features;
1588
- }
1589
- });
1590
- this.writeBigBoard();
1591
- };
1592
- this.checkForShutdown = () => {
1593
- this.checkQueue();
1594
- console.log(
1595
- ansiC2.inverse(
1596
- `The following jobs are awaiting resources: ${JSON.stringify(
1597
- this.queue
1598
- )}`
1599
- )
1600
- );
1601
- console.log(
1602
- ansiC2.inverse(`The status of ports: ${JSON.stringify(this.ports)}`)
1603
- );
1604
- this.writeBigBoard();
1605
- if (this.mode === "dev")
1606
- return;
1607
- let inflight = false;
1608
- Object.keys(this.summary).forEach((k) => {
1609
- if (this.summary[k].prompt === "?") {
1610
- console.log(ansiC2.blue(ansiC2.inverse(`\u{1F555} prompt ${k}`)));
1611
- inflight = true;
1612
- }
1613
- });
1614
- Object.keys(this.summary).forEach((k) => {
1615
- if (this.summary[k].runTimeErrors === "?") {
1616
- console.log(ansiC2.blue(ansiC2.inverse(`\u{1F555} runTimeError ${k}`)));
1617
- inflight = true;
1618
- }
1619
- });
1620
- Object.keys(this.summary).forEach((k) => {
1621
- if (this.summary[k].staticErrors === "?") {
1622
- console.log(ansiC2.blue(ansiC2.inverse(`\u{1F555} staticErrors ${k}`)));
1623
- inflight = true;
1624
- }
1625
- });
1626
- Object.keys(this.summary).forEach((k) => {
1627
- if (this.summary[k].typeErrors === "?") {
1628
- console.log(ansiC2.blue(ansiC2.inverse(`\u{1F555} typeErrors ${k}`)));
1629
- inflight = true;
1630
- }
1631
- });
1632
- this.writeBigBoard();
1633
- if (!inflight) {
1634
- if (this.browser) {
1635
- if (this.browser) {
1636
- this.browser.disconnect().then(() => {
1637
- console.log(
1638
- ansiC2.inverse(`${this.name} has been tested. Goodbye.`)
1639
- );
1640
- process.exit();
1641
- });
1642
- }
1643
- }
1644
- }
1645
- };
1646
- this.launchers = {};
1647
- this.ports = {};
1648
- this.queue = [];
1649
- this.nodeSidecars = {};
1650
- this.webSidecars = {};
1651
- this.pureSidecars = {};
1652
- this.configs.ports.forEach((element) => {
1653
- this.ports[element] = "";
1654
- });
1655
- this.httpServer = http.createServer(this.requestHandler.bind(this));
1656
- this.wss = new WebSocketServer({ server: this.httpServer });
1657
- this.wss.on("connection", (ws) => {
1658
- this.clients.add(ws);
1659
- console.log("Client connected");
1660
- ws.on("message", (data) => {
1661
- try {
1662
- const message = JSON.parse(data.toString());
1663
- if (message.type === "executeCommand") {
1664
- if (message.command && message.command.trim().startsWith("aider")) {
1665
- console.log(`Executing command: ${message.command}`);
1666
- const processId = Date.now().toString();
1667
- const child = spawn(message.command, {
1668
- shell: true,
1669
- cwd: process.cwd()
1670
- });
1671
- this.runningProcesses.set(processId, child);
1672
- this.allProcesses.set(processId, {
1673
- child,
1674
- status: "running",
1675
- command: message.command,
1676
- pid: child.pid,
1677
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1678
- });
1679
- this.processLogs.set(processId, []);
1680
- this.broadcast({
1681
- type: "processStarted",
1682
- processId,
1683
- command: message.command,
1684
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1685
- logs: []
1686
- });
1687
- child.stdout?.on("data", (data2) => {
1688
- const logData = data2.toString();
1689
- const logs = this.processLogs.get(processId) || [];
1690
- logs.push(logData);
1691
- this.processLogs.set(processId, logs);
1692
- this.broadcast({
1693
- type: "processStdout",
1694
- processId,
1695
- data: logData,
1696
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1697
- });
1698
- });
1699
- child.stderr?.on("data", (data2) => {
1700
- const logData = data2.toString();
1701
- const logs = this.processLogs.get(processId) || [];
1702
- logs.push(logData);
1703
- this.processLogs.set(processId, logs);
1704
- this.broadcast({
1705
- type: "processStderr",
1706
- processId,
1707
- data: logData,
1708
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1709
- });
1710
- });
1711
- child.on("error", (error) => {
1712
- console.error(`Failed to execute command: ${error}`);
1713
- this.runningProcesses.delete(processId);
1714
- const processInfo = this.allProcesses.get(processId);
1715
- if (processInfo) {
1716
- this.allProcesses.set(processId, {
1717
- ...processInfo,
1718
- status: "error",
1719
- error: error.message
1720
- });
1721
- }
1722
- this.broadcast({
1723
- type: "processError",
1724
- processId,
1725
- error: error.message,
1726
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1727
- });
1728
- });
1729
- child.on("exit", (code) => {
1730
- console.log(`Command exited with code ${code}`);
1731
- this.runningProcesses.delete(processId);
1732
- const processInfo = this.allProcesses.get(processId);
1733
- if (processInfo) {
1734
- this.allProcesses.set(processId, {
1735
- ...processInfo,
1736
- status: "exited",
1737
- exitCode: code
1738
- });
1739
- }
1740
- this.broadcast({
1741
- type: "processExited",
1742
- processId,
1743
- exitCode: code,
1744
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1745
- });
1746
- });
1747
- } else {
1748
- console.error('Invalid command: must start with "aider"');
1749
- }
1750
- } else if (message.type === "getRunningProcesses") {
1751
- const processes = Array.from(this.allProcesses.entries()).map(
1752
- ([id, procInfo]) => ({
1753
- processId: id,
1754
- command: procInfo.command,
1755
- pid: procInfo.pid,
1756
- status: procInfo.status,
1757
- exitCode: procInfo.exitCode,
1758
- error: procInfo.error,
1759
- timestamp: procInfo.timestamp,
1760
- logs: this.processLogs.get(id) || []
1761
- })
1762
- );
1763
- ws.send(
1764
- JSON.stringify({
1765
- type: "runningProcesses",
1766
- processes
1767
- })
1768
- );
1769
- } else if (message.type === "getProcess") {
1770
- const processId = message.processId;
1771
- const procInfo = this.allProcesses.get(processId);
1772
- if (procInfo) {
1773
- ws.send(
1774
- JSON.stringify({
1775
- type: "processData",
1776
- processId,
1777
- command: procInfo.command,
1778
- pid: procInfo.pid,
1779
- status: procInfo.status,
1780
- exitCode: procInfo.exitCode,
1781
- error: procInfo.error,
1782
- timestamp: procInfo.timestamp,
1783
- logs: this.processLogs.get(processId) || []
1784
- })
1785
- );
1786
- }
1787
- } else if (message.type === "stdin") {
1788
- const processId = message.processId;
1789
- const data2 = message.data;
1790
- console.log("Received stdin for process", processId, ":", data2);
1791
- const childProcess = this.runningProcesses.get(processId);
1792
- if (childProcess && childProcess.stdin) {
1793
- console.log("Writing to process stdin");
1794
- childProcess.stdin.write(data2);
1795
- } else {
1796
- console.log(
1797
- "Cannot write to stdin - process not found or no stdin:",
1798
- {
1799
- processExists: !!childProcess,
1800
- stdinExists: childProcess?.stdin ? true : false
1801
- }
1802
- );
1803
- }
1804
- } else if (message.type === "killProcess") {
1805
- const processId = message.processId;
1806
- console.log("Received killProcess for process", processId);
1807
- const childProcess = this.runningProcesses.get(processId);
1808
- if (childProcess) {
1809
- console.log("Killing process");
1810
- childProcess.kill("SIGTERM");
1811
- } else {
1812
- console.log("Cannot kill process - process not found:", {
1813
- processExists: !!childProcess
1814
- });
1815
- }
1816
- }
1817
- } catch (error) {
1818
- console.error("Error handling WebSocket message:", error);
1819
- }
1820
- });
1821
- ws.on("close", () => {
1822
- this.clients.delete(ws);
1823
- console.log("Client disconnected");
1824
- });
1825
- ws.on("error", (error) => {
1826
- console.error("WebSocket error:", error);
1827
- this.clients.delete(ws);
1828
- });
1829
- });
1830
- const httpPort = Number(process.env.HTTP_PORT) || 3e3;
1831
- this.httpServer.listen(httpPort, () => {
1832
- console.log(`HTTP server running on http://localhost:${httpPort}`);
1833
- });
1834
- }
1835
- async stopSideCar(uid) {
1836
- console.log(ansiC2.green(ansiC2.inverse(`stopSideCar ${uid}`)));
1837
- Object.entries(this.pureSidecars).forEach(async ([k, v]) => {
1838
- if (Number(k) === uid) {
1839
- await this.pureSidecars[Number(k)].stop();
1840
- delete this.pureSidecars[Number(k)];
1841
- }
1842
- });
1843
- Object.entries(this.nodeSidecars).forEach(async ([k, v]) => {
1844
- if (Number(k) === uid) {
1845
- await this.nodeSidecars[Number(k)].send("stop");
1846
- delete this.nodeSidecars[Number(k)];
1847
- }
1848
- });
1849
- Object.entries(this.webSidecars).forEach(async ([k, v]) => {
1850
- if (Number(k) === uid) {
1851
- (await this.browser.pages()).forEach(async (p) => {
1852
- if (p.mainFrame()._id === k) {
1853
- await this.webSidecars[Number(k)].close();
1854
- delete this.webSidecars[Number(k)];
1855
- }
1856
- });
1857
- }
1858
- });
1859
- return;
1860
- }
1861
- async launchSideCar(n, name) {
1862
- const c = this.configs.tests.find(([v, r2]) => {
1863
- return v === name;
1864
- });
1865
- const s = c[3][n];
1866
- const r = s[1];
1867
- if (r === "node") {
1868
- return this.launchNodeSideCar(s);
1869
- } else if (r === "web") {
1870
- return this.launchWebSideCar(s);
1871
- } else if (r === "pure") {
1872
- return this.launchPureSideCar(s);
1873
- } else {
1874
- throw `unknown runtime ${r}`;
1875
- }
1876
- }
1877
- mapping() {
1878
- return [
1879
- ["$", this.$],
1880
- ["click", this.click],
1881
- ["closePage", this.closePage],
1882
- ["createWriteStream", this.createWriteStream],
1883
- ["customclose", this.customclose],
1884
- ["customScreenShot", this.customScreenShot.bind(this)],
1885
- ["end", this.end],
1886
- ["existsSync", this.existsSync],
1887
- ["focusOn", this.focusOn],
1888
- ["getAttribute", this.getAttribute],
1889
- ["getInnerHtml", this.getInnerHtml],
1890
- // ["setValue", this.setValue],
1891
- ["goto", this.goto.bind(this)],
1892
- ["isDisabled", this.isDisabled],
1893
- ["launchSideCar", this.launchSideCar.bind(this)],
1894
- ["mkdirSync", this.mkdirSync],
1895
- ["newPage", this.newPage],
1896
- ["page", this.page],
1897
- ["pages", this.pages],
1898
- ["screencast", this.screencast],
1899
- ["screencastStop", this.screencastStop],
1900
- ["stopSideCar", this.stopSideCar.bind(this)],
1901
- ["typeInto", this.typeInto],
1902
- ["waitForSelector", this.waitForSelector],
1903
- ["write", this.write],
1904
- ["writeFileSync", this.writeFileSync]
1905
- ];
1906
- }
1907
- async start() {
1908
- this.mapping().forEach(async ([command, func]) => {
1909
- globalThis[command] = func;
1910
- });
1911
- if (!fs4.existsSync(`testeranto/reports/${this.name}`)) {
1912
- fs4.mkdirSync(`testeranto/reports/${this.name}`);
1913
- }
1914
- const executablePath = "/opt/homebrew/bin/chromium";
1915
- try {
1916
- this.browser = await puppeteer.launch({
1917
- slowMo: 1,
1918
- waitForInitialPage: false,
1919
- executablePath,
1920
- headless: true,
1921
- defaultViewport: null,
1922
- // Disable default 800x600 viewport
1923
- dumpio: false,
1924
- devtools: false,
1925
- args: [
1926
- "--allow-file-access-from-files",
1927
- "--allow-insecure-localhost",
1928
- "--allow-running-insecure-content",
1929
- "--auto-open-devtools-for-tabs",
1930
- "--disable-dev-shm-usage",
1931
- "--disable-extensions",
1932
- "--disable-features=site-per-process",
1933
- "--disable-gpu",
1934
- "--disable-setuid-sandbox",
1935
- "--disable-site-isolation-trials",
1936
- "--disable-web-security",
1937
- "--no-first-run",
1938
- "--no-sandbox",
1939
- "--no-startup-window",
1940
- "--reduce-security-for-testing",
1941
- "--remote-allow-origins=*",
1942
- "--start-maximized",
1943
- "--unsafely-treat-insecure-origin-as-secure=*",
1944
- `--remote-debugging-port=3234`
1945
- // "--disable-features=IsolateOrigins,site-per-process",
1946
- // "--disable-features=IsolateOrigins",
1947
- // "--disk-cache-dir=/dev/null",
1948
- // "--disk-cache-size=1",
1949
- // "--no-zygote",
1950
- // "--remote-allow-origins=ws://localhost:3234",
1951
- // "--single-process",
1952
- // "--start-maximized",
1953
- // "--unsafely-treat-insecure-origin-as-secure",
1954
- // "--unsafely-treat-insecure-origin-as-secure=ws://192.168.0.101:3234",
1955
- ]
1956
- });
1957
- } catch (e) {
1958
- console.error(e);
1959
- console.error(
1960
- "could not start chrome via puppeter. Check this path: ",
1961
- executablePath
1962
- );
1963
- }
1964
- const { nodeEntryPoints, webEntryPoints, pureEntryPoints } = this.getRunnables(this.configs.tests, this.name);
1965
- [
1966
- [
1967
- nodeEntryPoints,
1968
- this.launchNode,
1969
- "node",
1970
- (w) => {
1971
- this.nodeMetafileWatcher = w;
1972
- }
1973
- ],
1974
- [
1975
- webEntryPoints,
1976
- this.launchWeb,
1977
- "web",
1978
- (w) => {
1979
- this.webMetafileWatcher = w;
1980
- }
1981
- ],
1982
- [
1983
- pureEntryPoints,
1984
- this.launchPure,
1985
- "pure",
1986
- (w) => {
1987
- this.importMetafileWatcher = w;
1988
- }
1989
- ]
1990
- ].forEach(
1991
- async ([eps, launcher, runtime, watcher]) => {
1992
- const metafile = `./testeranto/metafiles/${runtime}/${this.name}.json`;
1993
- await pollForFile(metafile);
1994
- Object.entries(eps).forEach(
1995
- async ([inputFile, outputFile]) => {
1996
- this.launchers[inputFile] = () => launcher(inputFile, outputFile);
1997
- this.launchers[inputFile]();
1998
- try {
1999
- watch(outputFile, async (e, filename) => {
2000
- const hash = await fileHash(outputFile);
2001
- if (fileHashes[inputFile] !== hash) {
2002
- fileHashes[inputFile] = hash;
2003
- console.log(
2004
- ansiC2.yellow(ansiC2.inverse(`< ${e} ${filename}`))
2005
- );
2006
- this.launchers[inputFile]();
2007
- }
2008
- });
2009
- } catch (e) {
2010
- console.error(e);
2011
- }
2012
- }
2013
- );
2014
- this.metafileOutputs(runtime);
2015
- watcher(
2016
- watch(metafile, async (e, filename) => {
2017
- console.log(
2018
- ansiC2.yellow(ansiC2.inverse(`< ${e} ${filename} (${runtime})`))
2019
- );
2020
- this.metafileOutputs(runtime);
2021
- })
2022
- );
2023
- }
2024
- );
2025
- }
2026
- // async launchExternalTest(
2027
- // externalTestName: string,
2028
- // externalTest: {
2029
- // watch: string[];
2030
- // exec: string;
2031
- // }
2032
- // ) {
2033
- // // fs.mkdirSync(`testeranto/externalTests/${externalTestName}`);
2034
- // // exec(externalTest.exec, (error, stdout, stderr) => {
2035
- // // if (error) {
2036
- // // fs.writeFileSync(
2037
- // // `testeranto/externalTests/${externalTestName}/exitcode.txt`,
2038
- // // `${error.name}\n${error.message}\n${error.code}\n`
2039
- // // );
2040
- // // } else {
2041
- // // fs.writeFileSync(
2042
- // // `testeranto/externalTests/${externalTestName}/exitcode.txt`,
2043
- // // `0`
2044
- // // );
2045
- // // }
2046
- // // fs.writeFileSync(
2047
- // // `testeranto/externalTests/${externalTestName}/stdout.txt`,
2048
- // // stdout
2049
- // // );
2050
- // // fs.writeFileSync(
2051
- // // `testeranto/externalTests/${externalTestName}/stderr.txt`,
2052
- // // stderr
2053
- // // );
2054
- // // });
2055
- // }
2056
- async stop() {
2057
- console.log(ansiC2.inverse("Testeranto-Run is shutting down gracefully..."));
2058
- this.mode = "once";
2059
- this.nodeMetafileWatcher.close();
2060
- this.webMetafileWatcher.close();
2061
- this.importMetafileWatcher.close();
2062
- Object.values(this.logStreams || {}).forEach((logs) => logs.closeAll());
2063
- this.wss.close(() => {
2064
- console.log("WebSocket server closed");
2065
- });
2066
- this.clients.forEach((client) => {
2067
- client.terminate();
2068
- });
2069
- this.clients.clear();
2070
- this.httpServer.close(() => {
2071
- console.log("HTTP server closed");
2072
- });
2073
- this.checkForShutdown();
2074
- }
2075
- async metafileOutputs(platform) {
2076
- const metafile = JSON.parse(
2077
- fs4.readFileSync(`./testeranto/metafiles/${platform}/${this.name}.json`).toString()
2078
- ).metafile;
2079
- if (!metafile)
2080
- return;
2081
- const outputs = metafile.outputs;
2082
- Object.keys(outputs).forEach(async (k) => {
2083
- const pattern = `testeranto/bundles/${platform}/${this.name}/${this.configs.src}`;
2084
- if (!k.startsWith(pattern)) {
2085
- return false;
2086
- }
2087
- const addableFiles = Object.keys(outputs[k].inputs).filter((i) => {
2088
- if (!fs4.existsSync(i))
2089
- return false;
2090
- if (i.startsWith("node_modules"))
2091
- return false;
2092
- if (i.startsWith("./node_modules"))
2093
- return false;
2094
- return true;
2095
- });
2096
- const f2 = `${k.split(".").slice(0, -1).join(".")}/`;
2097
- if (!fs4.existsSync(f2)) {
2098
- fs4.mkdirSync(f2);
2099
- }
2100
- const entrypoint = outputs[k].entryPoint;
2101
- if (entrypoint) {
2102
- const changeDigest = await filesHash(addableFiles);
2103
- if (changeDigest === changes[entrypoint]) {
2104
- } else {
2105
- changes[entrypoint] = changeDigest;
2106
- this.tscCheck({
2107
- platform,
2108
- addableFiles,
2109
- entrypoint
2110
- });
2111
- this.eslintCheck(entrypoint, platform, addableFiles);
2112
- this.makePrompt(entrypoint, addableFiles, platform);
2113
- }
2114
- }
2115
- });
2116
- }
2117
- requestHandler(req, res) {
2118
- const parsedUrl = url.parse(req.url || "/");
2119
- let pathname = parsedUrl.pathname || "/";
2120
- if (pathname === "/") {
2121
- pathname = "/index.html";
2122
- }
2123
- let filePath = pathname.substring(1);
2124
- if (filePath.startsWith("reports/")) {
2125
- filePath = `testeranto/${filePath}`;
2126
- } else if (filePath.startsWith("metafiles/")) {
2127
- filePath = `testeranto/${filePath}`;
2128
- } else if (filePath === "projects.json") {
2129
- filePath = `testeranto/${filePath}`;
2130
- } else {
2131
- const possiblePaths = [
2132
- `dist/${filePath}`,
2133
- `testeranto/dist/${filePath}`,
2134
- `../dist/${filePath}`,
2135
- `./${filePath}`
2136
- ];
2137
- let foundPath = null;
2138
- for (const possiblePath of possiblePaths) {
2139
- if (fs4.existsSync(possiblePath)) {
2140
- foundPath = possiblePath;
2141
- break;
2142
- }
2143
- }
2144
- if (foundPath) {
2145
- filePath = foundPath;
2146
- } else {
2147
- const indexPath = this.findIndexHtml();
2148
- if (indexPath) {
2149
- fs4.readFile(indexPath, (err, data) => {
2150
- if (err) {
2151
- res.writeHead(404, { "Content-Type": "text/plain" });
2152
- res.end("404 Not Found");
2153
- return;
2154
- }
2155
- res.writeHead(200, { "Content-Type": "text/html" });
2156
- res.end(data);
2157
- });
2158
- return;
2159
- } else {
2160
- res.writeHead(404, { "Content-Type": "text/plain" });
2161
- res.end("404 Not Found");
2162
- return;
2163
- }
2164
- }
2165
- }
2166
- fs4.exists(filePath, (exists) => {
2167
- if (!exists) {
2168
- if (!pathname.includes(".") && pathname !== "/") {
2169
- const indexPath = this.findIndexHtml();
2170
- if (indexPath) {
2171
- fs4.readFile(indexPath, (err, data) => {
2172
- if (err) {
2173
- res.writeHead(404, { "Content-Type": "text/plain" });
2174
- res.end("404 Not Found");
2175
- return;
2176
- }
2177
- res.writeHead(200, { "Content-Type": "text/html" });
2178
- res.end(data);
2179
- });
2180
- return;
2181
- } else {
2182
- res.writeHead(200, { "Content-Type": "text/html" });
2183
- res.end(`
2184
- <html>
2185
- <body>
2186
- <h1>Testeranto is running</h1>
2187
- <p>Frontend files are not built yet. Run 'npm run build' to build the frontend.</p>
2188
- </body>
2189
- </html>
2190
- `);
2191
- return;
2192
- }
2193
- }
2194
- res.writeHead(404, { "Content-Type": "text/plain" });
2195
- res.end("404 Not Found");
2196
- return;
2197
- }
2198
- fs4.readFile(filePath, (err, data) => {
2199
- if (err) {
2200
- res.writeHead(500, { "Content-Type": "text/plain" });
2201
- res.end("500 Internal Server Error");
2202
- return;
2203
- }
2204
- const mimeType = mime.lookup(filePath) || "application/octet-stream";
2205
- res.writeHead(200, { "Content-Type": mimeType });
2206
- res.end(data);
2207
- });
2208
- });
2209
- }
2210
- findIndexHtml() {
2211
- const possiblePaths = [
2212
- "dist/index.html",
2213
- "testeranto/dist/index.html",
2214
- "../dist/index.html",
2215
- "./index.html"
2216
- ];
2217
- for (const path5 of possiblePaths) {
2218
- if (fs4.existsSync(path5)) {
2219
- return path5;
2220
- }
2221
- }
2222
- return null;
2223
- }
2224
- broadcast(message) {
2225
- const data = typeof message === "string" ? message : JSON.stringify(message);
2226
- this.clients.forEach((client) => {
2227
- if (client.readyState === 1) {
2228
- client.send(data);
2229
- }
2230
- });
2231
- }
2232
- checkQueue() {
2233
- const x = this.queue.pop();
2234
- if (!x) {
2235
- ansiC2.inverse(`The following queue is empty`);
2236
- return;
2237
- }
2238
- const test = this.configs.tests.find((t) => t[0] === x);
2239
- if (!test)
2240
- throw `test is undefined ${x}`;
2241
- this.launchers[test[0]]();
2242
- }
2243
- };
2244
-
2245
- // src/run.ts
2246
- readline.emitKeypressEvents(process.stdin);
2247
- if (process.stdin.isTTY)
2248
- process.stdin.setRawMode(true);
2249
- console.log(ansiC3.inverse("Press 'q' to initiate a graceful shutdown."));
2250
- console.log(ansiC3.inverse("Press 'x' to quit forcefully."));
2251
- process.stdin.on("keypress", (str, key) => {
2252
- if (key.name === "x") {
2253
- console.log(ansiC3.inverse("Shutting down forcefully..."));
2254
- process.exit(-1);
2255
- }
2256
- });
2257
- var projectName = process.argv[2];
2258
- var mode = process.argv[3];
2259
- if (mode !== "once" && mode !== "dev") {
2260
- console.error("the 2nd argument should be 'dev' or 'once' ");
2261
- process.exit(-1);
2262
- }
2263
- var f = process.cwd() + "/testeranto.config.ts";
2264
- console.log("config file:", f);
2265
- import(f).then(async (module) => {
2266
- const bigConfig = module.default || module;
2267
- const rawConfig = bigConfig.projects[projectName];
2268
- if (!rawConfig) {
2269
- console.error(`Project "${projectName}" does not exist in the configuration.`);
2270
- console.error("Available projects:", Object.keys(bigConfig.projects));
2271
- process.exit(-1);
2272
- }
2273
- if (!rawConfig.tests) {
2274
- console.error(projectName, "appears to have no tests: ", f);
2275
- console.error(`here is the config:`);
2276
- console.log(JSON.stringify(rawConfig));
2277
- process.exit(-1);
2278
- }
2279
- const config = {
2280
- ...rawConfig,
2281
- buildDir: process.cwd() + `/testeranto/${projectName}.json`
2282
- };
2283
- const pm = new PM_Main(config, projectName, mode);
2284
- pm.start();
2285
- process.stdin.on("keypress", (str, key) => {
2286
- if (key.name === "q") {
2287
- pm.stop();
2288
- }
2289
- });
2290
- });