testeranto 0.110.0 → 0.112.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.
@@ -37,17 +37,58 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.PM_Main = void 0;
40
- const fs_1 = __importDefault(require("fs"));
40
+ const typescript_1 = __importDefault(require("typescript"));
41
+ const fs_1 = __importStar(require("fs"));
41
42
  const path_1 = __importDefault(require("path"));
42
43
  const puppeteer_core_1 = __importDefault(require("puppeteer-core"));
43
44
  const ansi_colors_1 = __importDefault(require("ansi-colors"));
45
+ const node_crypto_1 = __importDefault(require("node:crypto"));
46
+ const eslint_1 = require("eslint");
47
+ const tsc_prog_1 = __importDefault(require("tsc-prog"));
44
48
  const utils_1 = require("../utils");
45
49
  const index_js_1 = require("./index.js");
50
+ const eslint = new eslint_1.ESLint();
51
+ const formatter = await eslint.loadFormatter("./node_modules/testeranto/dist/prebuild/esbuildConfigs/eslint-formatter-testeranto.mjs");
52
+ const changes = {};
53
+ const fileHashes = {};
46
54
  const fileStreams3 = [];
47
55
  const fPaths = [];
48
56
  const files = {};
49
57
  const recorders = {};
50
58
  const screenshots = {};
59
+ async function fileHash(filePath, algorithm = "md5") {
60
+ return new Promise((resolve, reject) => {
61
+ const hash = node_crypto_1.default.createHash(algorithm);
62
+ const fileStream = fs_1.default.createReadStream(filePath);
63
+ fileStream.on("data", (data) => {
64
+ hash.update(data);
65
+ });
66
+ fileStream.on("end", () => {
67
+ const fileHash = hash.digest("hex");
68
+ resolve(fileHash);
69
+ });
70
+ fileStream.on("error", (error) => {
71
+ reject(`Error reading file: ${error.message}`);
72
+ });
73
+ });
74
+ }
75
+ const getRunnables = (tests, payload = {
76
+ nodeEntryPoints: {},
77
+ webEntryPoints: {},
78
+ }) => {
79
+ return tests.reduce((pt, cv, cndx, cry) => {
80
+ if (cv[1] === "node") {
81
+ pt.nodeEntryPoints[cv[0]] = path_1.default.resolve(`./docs/node/${cv[0].split(".").slice(0, -1).concat("mjs").join(".")}`);
82
+ }
83
+ else if (cv[1] === "web") {
84
+ pt.webEntryPoints[cv[0]] = path_1.default.resolve(`./docs/web/${cv[0].split(".").slice(0, -1).concat("mjs").join(".")}`);
85
+ }
86
+ if (cv[3].length) {
87
+ getRunnables(cv[3], payload);
88
+ }
89
+ return pt;
90
+ }, payload);
91
+ };
51
92
  const statusMessagePretty = (failures, test) => {
52
93
  if (failures === 0) {
53
94
  console.log(ansi_colors_1.default.green(ansi_colors_1.default.inverse(`> ${test} completed successfully`)));
@@ -66,6 +107,13 @@ async function writeFileAndCreateDir(filePath, data) {
66
107
  console.error(`Error writing file: ${error}`);
67
108
  }
68
109
  }
110
+ const filesHash = async (files, algorithm = "md5") => {
111
+ return new Promise((resolve, reject) => {
112
+ resolve(files.reduce(async (mm, f) => {
113
+ return (await mm) + (await fileHash(f));
114
+ }, Promise.resolve("")));
115
+ });
116
+ };
69
117
  function isValidUrl(string) {
70
118
  try {
71
119
  new URL(string);
@@ -80,35 +128,117 @@ class PM_Main extends index_js_1.PM {
80
128
  super();
81
129
  this.shutdownMode = false;
82
130
  this.bigBoard = {};
131
+ this.stop = () => {
132
+ console.log(ansi_colors_1.default.inverse("Testeranto-Run is shutting down gracefully..."));
133
+ this.mode = "PROD";
134
+ this.nodeMetafileWatcher.close();
135
+ this.webMetafileWatcher.close();
136
+ this.checkForShutdown();
137
+ };
138
+ this.tscCheck = async ({ entrypoint, addableFiles, platform, }) => {
139
+ console.log(ansi_colors_1.default.green(ansi_colors_1.default.inverse(`tsc < ${entrypoint}`)));
140
+ this.bigBoard[entrypoint].typeErrors = "?";
141
+ const program = tsc_prog_1.default.createProgramFromConfig({
142
+ basePath: process.cwd(), // always required, used for relative paths
143
+ configFilePath: "tsconfig.json", // config to inherit from (optional)
144
+ compilerOptions: {
145
+ rootDir: "src",
146
+ outDir: (0, utils_1.tscPather)(entrypoint, platform),
147
+ // declaration: true,
148
+ // skipLibCheck: true,
149
+ noEmit: true,
150
+ },
151
+ include: addableFiles, //["src/**/*"],
152
+ // exclude: ["**/*.test.ts", "**/*.spec.ts"],
153
+ });
154
+ const tscPath = (0, utils_1.tscPather)(entrypoint, platform);
155
+ let allDiagnostics = program.getSemanticDiagnostics();
156
+ const d = [];
157
+ allDiagnostics.forEach((diagnostic) => {
158
+ if (diagnostic.file) {
159
+ let { line, character } = typescript_1.default.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
160
+ let message = typescript_1.default.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
161
+ d.push(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
162
+ }
163
+ else {
164
+ d.push(typescript_1.default.flattenDiagnosticMessageText(diagnostic.messageText, "\n"));
165
+ }
166
+ });
167
+ fs_1.default.writeFileSync(tscPath, d.join("\n"));
168
+ this.bigBoard[entrypoint].typeErrors = d.length;
169
+ if (this.shutdownMode) {
170
+ this.checkForShutdown();
171
+ }
172
+ // fs.writeFileSync(
173
+ // tscExitCodePather(entrypoint, platform),
174
+ // d.length.toString()
175
+ // );
176
+ };
177
+ this.eslintCheck = async (entrypoint, platform, addableFiles) => {
178
+ this.bigBoard[entrypoint].staticErrors = "?";
179
+ console.log(ansi_colors_1.default.green(ansi_colors_1.default.inverse(`eslint < ${entrypoint}`)));
180
+ const results = (await eslint.lintFiles(addableFiles))
181
+ .filter((r) => r.messages.length)
182
+ .filter((r) => {
183
+ return r.messages[0].ruleId !== null;
184
+ })
185
+ .map((r) => {
186
+ delete r.source;
187
+ return r;
188
+ });
189
+ fs_1.default.writeFileSync((0, utils_1.lintPather)(entrypoint, platform), await formatter.format(results));
190
+ this.bigBoard[entrypoint].staticErrors = results.length;
191
+ if (this.shutdownMode) {
192
+ this.checkForShutdown();
193
+ }
194
+ // fs.writeFileSync(
195
+ // lintExitCodePather(entrypoint, platform),
196
+ // results.length.toString()
197
+ // );
198
+ };
199
+ this.makePrompt = async (entryPoint, addableFiles, platform) => {
200
+ this.bigBoard[entryPoint].prompt = "?";
201
+ const promptPath = path_1.default.join("./docs/", platform, entryPoint.split(".").slice(0, -1).join("."), `prompt.txt`);
202
+ const testPaths = path_1.default.join("./docs/", platform, entryPoint.split(".").slice(0, -1).join("."), `tests.json`);
203
+ const featuresPath = path_1.default.join("./docs/", platform, entryPoint.split(".").slice(0, -1).join("."), `featurePrompt.txt`);
204
+ fs_1.default.writeFileSync(promptPath, `
205
+ ${addableFiles
206
+ .map((x) => {
207
+ return `/add ${x}`;
208
+ })
209
+ .join("\n")}
210
+
211
+ /read ${(0, utils_1.lintPather)(entryPoint, platform)}
212
+ /read ${(0, utils_1.tscPather)(entryPoint, platform)}
213
+ /read ${testPaths}
214
+
215
+ /load ${featuresPath}
216
+
217
+ /code Fix the failing tests described in ${testPaths}. Correct any type signature errors described in the files ${(0, utils_1.tscPather)(entryPoint, platform)}. Implement any method which throws "Function not implemented. Resolve the lint errors described in ${(0, utils_1.lintPather)(entryPoint, platform)}"
218
+ `);
219
+ this.bigBoard[entryPoint].prompt = `aider --model deepseek/deepseek-chat --load docs/${platform}/${entryPoint
220
+ .split(".")
221
+ .slice(0, -1)
222
+ .join(".")}/prompt.txt`;
223
+ if (this.shutdownMode) {
224
+ this.checkForShutdown();
225
+ }
226
+ };
83
227
  this.checkForShutdown = () => {
84
- const anyRunning = Object.values(this.bigBoard).filter((x) => x.status === "running")
85
- .length > 0;
228
+ const anyRunning = Object.values(this.bigBoard).filter((x) => x.prompt === "?").length +
229
+ Object.values(this.bigBoard).filter((x) => x.runTimeError === "?")
230
+ .length +
231
+ Object.values(this.bigBoard).filter((x) => x.staticErrors === "?")
232
+ .length +
233
+ Object.values(this.bigBoard).filter((x) => x.typeErrors === "?")
234
+ .length >
235
+ 0;
86
236
  if (anyRunning) {
237
+ console.log(ansi_colors_1.default.inverse("Shutting down. Please wait"));
87
238
  }
88
239
  else {
89
240
  this.browser.disconnect().then(() => {
90
- const final = {
91
- timestamp: Date.now(),
92
- tests: this.configs.tests.reduce((mm, t) => {
93
- const bddErrors = fs_1.default
94
- .readFileSync((0, utils_1.bddExitCodePather)(t[0], t[1]))
95
- .toString();
96
- const lintErrors = fs_1.default
97
- .readFileSync((0, utils_1.lintExitCodePather)(t[0], t[1]))
98
- .toString();
99
- const typeErrors = fs_1.default
100
- .readFileSync((0, utils_1.tscExitCodePather)(t[0], t[1]))
101
- .toString();
102
- mm[t[0]] = {
103
- bddErrors,
104
- lintErrors,
105
- typeErrors,
106
- };
107
- return mm;
108
- }, {}),
109
- };
110
- const s = JSON.stringify(final, null, 2);
111
- fs_1.default.writeFileSync("docs/summary.json", s);
241
+ fs_1.default.writeFileSync("docs/summary.json", JSON.stringify(this.bigBoard, null, 2));
112
242
  console.log(ansi_colors_1.default.inverse("Goodbye"));
113
243
  process.exit();
114
244
  });
@@ -223,15 +353,7 @@ class PM_Main extends index_js_1.PM {
223
353
  };
224
354
  this.launchWebSideCar = async (src, dest, testConfig) => {
225
355
  const d = dest + ".mjs";
226
- // console.log(green, "launchWebSideCar", src, dest, d);
227
356
  console.log(ansi_colors_1.default.green(ansi_colors_1.default.inverse(`launchWebSideCar ${src}`)));
228
- const destFolder = dest.replace(".mjs", "");
229
- // const webArgz = JSON.stringify({
230
- // name: dest,
231
- // ports: [].toString(),
232
- // fs: destFolder,
233
- // browserWSEndpoint: this.browser.wsEndpoint(),
234
- // });
235
357
  const fileStreams2 = [];
236
358
  const doneFileStream2 = [];
237
359
  return new Promise((res, rej) => {
@@ -331,7 +453,6 @@ class PM_Main extends index_js_1.PM {
331
453
  };
332
454
  this.launchNodeSideCar = async (src, dest, testConfig) => {
333
455
  const d = dest + ".mjs";
334
- // console.log(green, "launchNodeSideCar", src, dest, d);
335
456
  console.log(ansi_colors_1.default.green(ansi_colors_1.default.inverse(`launchNodeSideCar ${src}`)));
336
457
  const destFolder = dest.replace(".mjs", "");
337
458
  let argz = "";
@@ -373,10 +494,6 @@ class PM_Main extends index_js_1.PM {
373
494
  process.exit(-1);
374
495
  }
375
496
  const builtfile = dest + ".mjs";
376
- // console.log(
377
- // "node builtfile",
378
- // (await import(`${builtfile}?cacheBust=${Date.now()}`)).default
379
- // );
380
497
  this.server[builtfile] = await Promise.resolve(`${`${builtfile}?cacheBust=${Date.now()}`}`).then(s => __importStar(require(s))).then((module) => {
381
498
  return module.default.then((defaultModule) => {
382
499
  // console.log("defaultModule", defaultModule);
@@ -394,7 +511,6 @@ class PM_Main extends index_js_1.PM {
394
511
  // });
395
512
  });
396
513
  });
397
- // console.log("portsToUse", portsToUse);
398
514
  for (let i = 0; i <= portsToUse.length; i++) {
399
515
  if (portsToUse[i]) {
400
516
  this.ports[portsToUse[i]] = "true"; //port is open again
@@ -706,21 +822,25 @@ class PM_Main extends index_js_1.PM {
706
822
  })
707
823
  .join("\n"));
708
824
  });
709
- this.writeBigBoard();
825
+ // this.writeBigBoard();
710
826
  };
711
827
  this.receiveExitCode = (srcTest, failures) => {
712
828
  this.bigBoard[srcTest].runTimeError = failures;
713
829
  this.writeBigBoard();
714
830
  };
715
831
  this.writeBigBoard = () => {
716
- fs_1.default.writeFileSync("./docs/bigBoard.json", JSON.stringify(this.bigBoard, null, 2));
832
+ fs_1.default.writeFileSync("./docs/summary.json", JSON.stringify(this.bigBoard, null, 2));
717
833
  };
834
+ this.mode = configs.devMode ? "DEV" : "PROD";
718
835
  this.server = {};
719
836
  this.configs = configs;
720
837
  this.ports = {};
721
838
  this.configs.tests.forEach(([t]) => {
722
839
  this.bigBoard[t] = {
723
- status: "?",
840
+ runTimeError: "?",
841
+ typeErrors: "?",
842
+ staticErrors: "?",
843
+ prompt: "?",
724
844
  };
725
845
  });
726
846
  this.configs.ports.forEach((element) => {
@@ -789,29 +909,6 @@ class PM_Main extends index_js_1.PM {
789
909
  globalThis["end"] = (uid) => {
790
910
  fileStreams3[uid].end();
791
911
  };
792
- // async (ssOpts: ScreenshotOptions, testName: string) => {
793
- // const p = ssOpts.path as string;
794
- // const dir = path.dirname(p);
795
- // fs.mkdirSync(dir, {
796
- // recursive: true,
797
- // });
798
- // if (!files[testName]) {
799
- // files[testName] = new Set();
800
- // }
801
- // files[testName].add(ssOpts.path as string);
802
- // const sPromise = page.screenshot({
803
- // ...ssOpts,
804
- // path: p,
805
- // });
806
- // if (!screenshots[testName]) {
807
- // screenshots[testName] = [];
808
- // }
809
- // screenshots[testName].push(sPromise);
810
- // // sPromise.then(())
811
- // await sPromise;
812
- // return sPromise;
813
- // // page.evaluate(`window["screenshot done"]`);
814
- // };
815
912
  globalThis["customScreenShot"] = async (opts, pageKey, testName) => {
816
913
  const page = (await this.browser.pages()).find(
817
914
  /* @ts-ignore:next-line */
@@ -846,16 +943,6 @@ class PM_Main extends index_js_1.PM {
846
943
  recorders[opts.path] = recorder;
847
944
  return opts.path;
848
945
  };
849
- // globalThis["customclose"] = (p: string, testName: string) => {
850
- // if (!files[testName]) {
851
- // files[testName] = new Set();
852
- // }
853
- // fs.writeFileSync(
854
- // p + "/manifest.json",
855
- // JSON.stringify(Array.from(files[testName]))
856
- // );
857
- // delete files[testName];
858
- // };
859
946
  }
860
947
  customclose() {
861
948
  throw new Error("Method not implemented.");
@@ -967,18 +1054,123 @@ class PM_Main extends index_js_1.PM {
967
1054
  throw new Error("Method not implemented.");
968
1055
  }
969
1056
  ////////////////////////////////////////////////////////////////////////////////
970
- async startPuppeteer(options, destfolder) {
971
- this.browser = (await puppeteer_core_1.default.launch(options));
972
- }
973
- // goodbye = () => {
974
- // this.browser.disconnect().then(() => {
975
- // console.log("Goodbye");
976
- // process.exit();
977
- // });
978
- // };
979
- shutDown() {
980
- this.shutdownMode = true;
981
- this.checkForShutdown();
1057
+ async metafileOutputs(platform) {
1058
+ const metafile = JSON.parse(fs_1.default.readFileSync(`docs/${platform}/metafile.json`).toString()).metafile;
1059
+ if (!metafile)
1060
+ return;
1061
+ const outputs = metafile.outputs;
1062
+ Object.keys(outputs).forEach(async (k) => {
1063
+ const addableFiles = Object.keys(outputs[k].inputs).filter((i) => {
1064
+ if (!fs_1.default.existsSync(i))
1065
+ return false;
1066
+ if (i.startsWith("node_modules"))
1067
+ return false;
1068
+ return true;
1069
+ });
1070
+ const f = `${k.split(".").slice(0, -1).join(".")}/`;
1071
+ if (!fs_1.default.existsSync(f)) {
1072
+ fs_1.default.mkdirSync(f);
1073
+ }
1074
+ const entrypoint = outputs[k].entryPoint;
1075
+ if (entrypoint) {
1076
+ const changeDigest = await filesHash(addableFiles);
1077
+ if (changeDigest === changes[entrypoint]) {
1078
+ // skip
1079
+ }
1080
+ else {
1081
+ changes[entrypoint] = changeDigest;
1082
+ this.tscCheck({
1083
+ platform,
1084
+ addableFiles,
1085
+ entrypoint: "./" + entrypoint,
1086
+ });
1087
+ this.eslintCheck("./" + entrypoint, platform, addableFiles);
1088
+ this.makePrompt("./" + entrypoint, addableFiles, platform);
1089
+ }
1090
+ }
1091
+ });
1092
+ }
1093
+ async start() {
1094
+ this.browser = (await puppeteer_core_1.default.launch({
1095
+ slowMo: 1,
1096
+ // timeout: 1,
1097
+ waitForInitialPage: false,
1098
+ executablePath:
1099
+ // process.env.CHROMIUM_PATH || "/opt/homebrew/bin/chromium",
1100
+ "/opt/homebrew/bin/chromium",
1101
+ headless: true,
1102
+ dumpio: true,
1103
+ // timeout: 0,
1104
+ devtools: true,
1105
+ args: [
1106
+ "--auto-open-devtools-for-tabs",
1107
+ `--remote-debugging-port=3234`,
1108
+ // "--disable-features=IsolateOrigins,site-per-process",
1109
+ "--disable-site-isolation-trials",
1110
+ "--allow-insecure-localhost",
1111
+ "--allow-file-access-from-files",
1112
+ "--allow-running-insecure-content",
1113
+ "--disable-dev-shm-usage",
1114
+ "--disable-extensions",
1115
+ "--disable-gpu",
1116
+ "--disable-setuid-sandbox",
1117
+ "--disable-site-isolation-trials",
1118
+ "--disable-web-security",
1119
+ "--no-first-run",
1120
+ "--no-sandbox",
1121
+ "--no-startup-window",
1122
+ // "--no-zygote",
1123
+ "--reduce-security-for-testing",
1124
+ "--remote-allow-origins=*",
1125
+ "--unsafely-treat-insecure-origin-as-secure=*",
1126
+ // "--disable-features=IsolateOrigins",
1127
+ // "--remote-allow-origins=ws://localhost:3234",
1128
+ // "--single-process",
1129
+ // "--unsafely-treat-insecure-origin-as-secure",
1130
+ // "--unsafely-treat-insecure-origin-as-secure=ws://192.168.0.101:3234",
1131
+ // "--disk-cache-dir=/dev/null",
1132
+ // "--disk-cache-size=1",
1133
+ // "--start-maximized",
1134
+ ],
1135
+ }));
1136
+ const { nodeEntryPoints, webEntryPoints } = getRunnables(this.configs.tests);
1137
+ Object.entries(nodeEntryPoints).forEach(([k, outputFile]) => {
1138
+ this.launchNode(k, outputFile);
1139
+ try {
1140
+ (0, fs_1.watch)(outputFile, async (e, filename) => {
1141
+ const hash = await fileHash(outputFile);
1142
+ if (fileHashes[k] !== hash) {
1143
+ fileHashes[k] = hash;
1144
+ console.log(ansi_colors_1.default.green(ansi_colors_1.default.inverse(`< ${e} ${filename}`)));
1145
+ this.launchNode(k, outputFile);
1146
+ }
1147
+ });
1148
+ }
1149
+ catch (e) {
1150
+ console.error(e);
1151
+ }
1152
+ });
1153
+ Object.entries(webEntryPoints).forEach(([k, outputFile]) => {
1154
+ this.launchWeb(k, outputFile);
1155
+ (0, fs_1.watch)(outputFile, async (e, filename) => {
1156
+ const hash = await fileHash(outputFile);
1157
+ if (fileHashes[k] !== hash) {
1158
+ fileHashes[k] = hash;
1159
+ console.log(ansi_colors_1.default.green(ansi_colors_1.default.inverse(`< ${e} ${filename}`)));
1160
+ this.launchWeb(k, outputFile);
1161
+ }
1162
+ });
1163
+ });
1164
+ this.metafileOutputs("node");
1165
+ this.nodeMetafileWatcher = (0, fs_1.watch)("docs/node/metafile.json", async (e, filename) => {
1166
+ console.log(ansi_colors_1.default.green(ansi_colors_1.default.inverse(`< ${e} ${filename} (node)`)));
1167
+ this.metafileOutputs("node");
1168
+ });
1169
+ this.metafileOutputs("web");
1170
+ this.webMetafileWatcher = (0, fs_1.watch)("docs/web/metafile.json", async (e, filename) => {
1171
+ console.log(ansi_colors_1.default.green(ansi_colors_1.default.inverse(`< ${e} ${filename} (web)`)));
1172
+ this.metafileOutputs("web");
1173
+ });
982
1174
  }
983
1175
  }
984
1176
  exports.PM_Main = PM_Main;