@checksum-ai/runtime 1.0.8 → 1.0.9

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 (4) hide show
  1. package/cli.js +19 -0
  2. package/package.json +1 -1
  3. package/cli.ts +0 -599
  4. package/types.ts +0 -58
package/cli.js ADDED
@@ -0,0 +1,19 @@
1
+ var m=Object.create;var p=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var C=Object.getOwnPropertyNames;var w=Object.getPrototypeOf,y=Object.prototype.hasOwnProperty;var k=(c,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of C(t))!y.call(c,o)&&o!==e&&p(c,o,{get:()=>t[o],enumerable:!(s=f(t,o))||s.enumerable});return c};var P=(c,t,e)=>(e=c!=null?m(w(c)):{},k(t||!c||!c.__esModule?p(e,"default",{value:c,enumerable:!0}):e,c));var n=require("fs"),l=P(require("child_process")),r=require("path");var d="checksum";var g=class{constructor(){this.UPLOAD_AGENT_PATH=(0,r.join)(__dirname,"upload-agent.js");this.CHECKSUM_API_URL="http://localhost:3000";this.didFail=!1;this.mock=!0;this.completeIndicators={upload:!1,tests:!1,report:!1};this.guardReturn=async(t,e=1e3,s="action hang guard timed out")=>{let o="guard-timed-out",i=async()=>(await this.awaitSleep(e+1e3),o),a=await Promise.race([t,i()]);if(typeof a=="string"&&a===o)throw new Error(s);return a};this.awaitSleep=t=>new Promise(e=>setTimeout(e,t))}async execute(){var t;switch(process.argv[2]){case"init":this.install();break;case"test":if(((t=process.argv)==null?void 0:t[3])==="--help"){await this.printHelp("test");break}await this.test(process.argv.slice(3));break;case"show-report":this.showReport(process.argv.slice(3));break;default:await this.printHelp()}process.exit(0)}async execCmd(t){let e=await l.spawn(t,{shell:!0,stdio:"inherit"});return new Promise((o,i)=>{e.on("exit",a=>{a===0?o(!0):i(new Error(`Checsum failed execution with code: ${a} `))})})}async getCmdOutput(t){return new Promise(function(e,s){l.exec(t,(o,i,a)=>{if(o){s(`Error executing command: ${o.message}`);return}e(i)})})}async printHelp(t){switch(t){default:console.log(`
2
+ Checksum CLI
3
+ Usage: checksum [command] [options]
4
+
5
+ Commands:
6
+ init installs checksum files and folders
7
+ test [options] [test-filter...] runs checksum tests
8
+ help prints this help message
9
+ show-report [options] [report] show HTML report
10
+ `);break;case"test":try{let e="npx playwright test --help",s=(await this.getCmdOutput(e)).replace(/npx playwright/g,"yarn checksum").split(`
11
+ `);s.splice(5,0," --checksum-config=<config> Checksum configuration in JSON format").join(`
12
+ `),console.log(s.join(`
13
+ `))}catch(e){console.log("Error",e.message)}break}}async showReport(t){let e=`npx playwright show-report ${t.join(" ")}`;try{await this.execCmd(e)}catch(s){console.log("Error showing report",s.message)}}async test(t){t=this.getChecksumConfigFromCommand(t),this.setChecksumConfig(),await this.getSession();let e;try{let{uuid:o,uploadURL:i}=this.testSession;e=await this.guardReturn(this.startUploadAgent(o,i),1e4,"Upload agent timeout")}catch{console.log("Error starting upload agent. Test results will not be available on checksum.")}this.buildVolatileConfig();let s=`${e?`CHECKSUM_UPLOAD_AGENT_PORT=${e} `:""} PWDEBUG=console npx playwright test --config ${this.getPlaywrightConfigFile()} ${t.join(" ")}`;try{await this.execCmd(s)}catch(o){this.didFail=!0,console.log("Error during test",o.message)}finally{let o=this.getPlaywrightReportPath();(0,n.existsSync)(o)?this.uploadAgent.stdin.write(`cli:report=${o}`):console.log(`Could not find report file at ${o}`),this.completeIndicators.tests=!0,await this.handleCompleteMessage()}}getPlaywrightReportPath(){var o,i;let t=(0,r.join)(process.cwd(),"playwright-report"),e=require(this.getPlaywrightConfigFile()),{reporter:s}=e;return s instanceof Array&&s.length>1&&((o=s[1])!=null&&o.outputFolder)&&(t=(i=s[1])==null?void 0:i.outputFolder),process.env.PLAYWRIGHT_HTML_REPORT&&(t=process.env.PLAYWRIGHT_HTML_REPORT),(0,r.join)(t,"index.html")}getPlaywrightConfigFile(){return(0,r.join)(this.getRootDirPath(),"playwright.config.ts")}startUploadAgent(t,e){return new Promise((s,o)=>{console.log("Starting upload agent"),this.uploadAgent=l.spawn("node",[this.UPLOAD_AGENT_PATH,JSON.stringify({sessionId:t,checksumApiURL:this.CHECKSUM_API_URL,apiKey:this.config.apiKey}),...this.mock?["mock"]:[]]),this.uploadAgent.stdout.on("data",i=>{let a=i.toString().trim();if(!a.startsWith("upag:"))return;let[u,h]=a.substring(5).split("=");u==="port"?(console.log("Received port from upload agent",h),s(h)):this.handleUploadAgentMessage(u,h)}),this.uploadAgent.on("exit",(i,a)=>{console.log(`upload agent process exited with code ${i} and signal ${a}`)}),this.uploadAgent.on("error",i=>{console.error(`Error starting upload agent: ${i.message}`)})})}async handleUploadAgentMessage(t,e){switch(t){case"complete":this.sendUploadsComplete().then(()=>{this.completeIndicators.upload=!0});break;case"report-uploaded":let s=await this.sendTestrunEnd();this.completeIndicators.report=!0,s&&console.log(`*******************
14
+ * Checksum report URL: ${s}
15
+ *******************`);break;default:console.warn(`Unhandled upload agent message: ${t}=${e}`)}}async handleCompleteMessage(){for(;;)Object.keys(this.completeIndicators).find(t=>!this.completeIndicators[t])?await this.awaitSleep(1e3):(console.log("Tests complete"),this.shutdown(this.didFail?1:0))}shutdown(t=0){this.cleanup(),process.exit(t)}buildVolatileConfig(){if(!this.volatileChecksumConfig)return;let t=this.getVolatileConfigPath(),e=`
16
+ import { RunMode, getChecksumConfig } from "@checksum-ai/runtime";
17
+
18
+ export default getChecksumConfig(${JSON.stringify(this.config,null,2)});
19
+ `;(0,n.writeFileSync)(t,e)}cleanup(){this.deleteVolatileConfig(),this.uploadAgent.stdin.write("cli:shutdown"),this.uploadAgent.kill()}async getSession(){try{if(this.mock){this.testSession={uuid:"session-id-1234",uploadURL:"http://localhost:3000/upload"};return}let t=this.config.apiKey;(!t||t==="<API key>")&&(console.error("No API key found in checksum config"),this.shutdown(1));let e=JSON.stringify(await this.getEnvInfo()),s=await fetch(`${this.CHECKSUM_API_URL}/client-api/test-runs`,{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json",ChecksumAppCode:t},body:e});this.testSession=await s.json()}catch(t){console.error("Error getting checksum test session",t),this.shutdown(1)}}async sendTestrunEnd(){try{if(this.mock)return"https://mock.report.url";let{uuid:t}=this.testSession,e=JSON.stringify({failed:this.didFail?1:0,passed:0,healed:0,endedAt:Date.now()}),{reportURL:s}=await this.updateTestRun(`${this.CHECKSUM_API_URL}/client-api/test-runs/${t}`,"PATCH",e);return s}catch(t){return console.log("Error sending test run end",t.message),null}}async sendUploadsComplete(){if(!this.mock)try{let{uuid:t}=this.testSession;await this.updateTestRun(`${this.CHECKSUM_API_URL}/client-api/test-runs/${t}/uploads-completed`,"PATCH")}catch(t){console.log("Error sending test run uploads complete",t.message)}}async updateTestRun(t,e,s=void 0){return(await fetch(t,{method:e,headers:{Accept:"application/json","Content-Type":"application/json",ChecksumAppCode:this.config.apiKey},body:s})).json()}async getEnvInfo(){let t={commitHash:"",branch:"branch",environment:process.env.CI?"CI":"local",name:"name",startedAt:Date.now()};try{t.commitHash=(await this.getCmdOutput("git rev-parse HEAD")).toString().trim()}catch(e){console.log("Error getting git hash",e.message)}try{t.branch=(await this.getCmdOutput("git rev-parse --abbrev-ref HEAD")).toString().trim()}catch(e){console.log("Error getting branch name",e.message)}return t}getVolatileConfigPath(){return(0,r.join)(this.getRootDirPath(),"checksum.config.tmp.ts")}deleteVolatileConfig(){let t=this.getVolatileConfigPath();(0,n.existsSync)(t)&&(0,n.rmSync)(t)}setChecksumConfig(){this.config={...require((0,r.join)(this.getRootDirPath(),"checksum.config.ts")).default||{},...this.volatileChecksumConfig||{}}}getChecksumConfigFromCommand(t){this.deleteVolatileConfig();for(let e of t)if(e.startsWith("--checksum-config"))try{return this.volatileChecksumConfig=JSON.parse(e.split("=")[1]),t.filter(s=>s!==e)}catch(s){console.log("Error parsing checksum config",s.message),this.volatileChecksumConfig=void 0}return t}install(){console.log("Creating Checksum directory and necessary files to run your tests");let t=this.getRootDirPath();if((0,n.existsSync)(this.getRootDirPath())||(0,n.mkdirSync)(t),!(0,n.existsSync)(this.getChecksumRootOrigin()))throw new Error("Could not find checksum root directory, please install @checksum-ai/runtime package");["checksum.config.ts","playwright.config.ts","login.ts","README.md"].forEach(e=>{(0,n.copyFileSync)((0,r.join)(this.getChecksumRootOrigin(),e),(0,r.join)(t,e))}),(0,n.mkdirSync)((0,r.join)(t,"tests"),{recursive:!0}),["esra","har","trace","log"].forEach(e=>{(0,n.mkdirSync)((0,r.join)(t,"test-data",e),{recursive:!0})})}getRootDirPath(){return(0,r.join)(process.cwd(),d)}getChecksumRootOrigin(){return(0,r.join)(process.cwd(),"node_modules","@checksum-ai","runtime","checksum-root")}};(async()=>await new g().execute())();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checksum-ai/runtime",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "Checksum.ai test runtime",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/cli.ts DELETED
@@ -1,599 +0,0 @@
1
- import { copyFileSync, existsSync, mkdirSync, rmSync, writeFileSync } from "fs";
2
- import * as childProcess from "child_process";
3
- import { join } from "path";
4
- import {
5
- CHECKSUM_ROOT_FOLDER,
6
- ChecksumConfig,
7
- TestSuiteSession,
8
- } from "./types";
9
-
10
- class ChecksumCLI {
11
- private readonly UPLOAD_AGENT_PATH = join(__dirname, "upload-agent.js");
12
- private readonly CHECKSUM_API_URL = "http://localhost:3000";
13
-
14
- private testSession: TestSuiteSession;
15
- private volatileChecksumConfig;
16
- private uploadAgent;
17
- private config: ChecksumConfig;
18
-
19
- private didFail = false;
20
- private mock = true;
21
-
22
- private completeIndicators = {
23
- upload: false,
24
- tests: false,
25
- report: false,
26
- };
27
-
28
- constructor() {}
29
-
30
- // rename install to init
31
- // rename run to test
32
- // rename import of playwright to our alias
33
- async execute() {
34
- switch (process.argv[2]) {
35
- case "init":
36
- this.install();
37
- break;
38
- case "test":
39
- if (process.argv?.[3] === "--help") {
40
- await this.printHelp("test");
41
- break;
42
- }
43
- await this.test(process.argv.slice(3));
44
- break;
45
- case "show-report":
46
- this.showReport(process.argv.slice(3));
47
- break;
48
- default:
49
- // should we simply forward to playwright in default case?
50
- await this.printHelp();
51
- }
52
- process.exit(0);
53
- }
54
-
55
- async execCmd(cmdString) {
56
- const child = await childProcess.spawn(cmdString, {
57
- shell: true,
58
- stdio: "inherit",
59
- });
60
-
61
- const exitPromise = new Promise((resolve, reject) => {
62
- child.on("exit", (code) => {
63
- if (code === 0) {
64
- resolve(true);
65
- } else {
66
- reject(new Error(`Checsum failed execution with code: ${code} `));
67
- }
68
- });
69
- });
70
-
71
- return exitPromise;
72
- }
73
-
74
- async getCmdOutput(cmdString): Promise<string> {
75
- return new Promise<string>(function (resolve, reject) {
76
- childProcess.exec(cmdString, (error, stdout, stderr) => {
77
- if (error) {
78
- reject(`Error executing command: ${error.message}`);
79
- return;
80
- }
81
-
82
- resolve(stdout);
83
- });
84
- });
85
-
86
- // return promise;
87
- }
88
-
89
- async printHelp(command?: string) {
90
- switch (command) {
91
- default:
92
- console.log(`
93
- Checksum CLI
94
- Usage: checksum [command] [options]
95
-
96
- Commands:
97
- init installs checksum files and folders
98
- test [options] [test-filter...] runs checksum tests
99
- help prints this help message
100
- show-report [options] [report] show HTML report
101
- `);
102
- break;
103
- case "test":
104
- try {
105
- const cmd = `npx playwright test --help`;
106
- const testHelp = (await this.getCmdOutput(cmd))
107
- .replace(/npx playwright/g, "yarn checksum")
108
- .split("\n");
109
- testHelp
110
- .splice(
111
- 5,
112
- 0,
113
- " --checksum-config=<config> Checksum configuration in JSON format"
114
- )
115
- .join("\n");
116
- console.log(testHelp.join("\n"));
117
- } catch (e) {
118
- console.log("Error", e.message);
119
- }
120
-
121
- break;
122
- }
123
- }
124
-
125
- async showReport(args: string[]) {
126
- const cmd = `npx playwright show-report ${args.join(" ")}`;
127
-
128
- try {
129
- await this.execCmd(cmd);
130
- } catch (e) {
131
- console.log("Error showing report", e.message);
132
- }
133
- }
134
-
135
- async test(args: string[]) {
136
- // check for checksum config in command
137
- args = this.getChecksumConfigFromCommand(args);
138
-
139
- // load checksum config
140
- this.setChecksumConfig();
141
-
142
- // init new test session
143
- await this.getSession();
144
-
145
- // start upload agent
146
- let uploadAgentListeningPort;
147
- try {
148
- const { uuid, uploadURL } = this.testSession;
149
- // load upload agent, timout after 20 seconds
150
- uploadAgentListeningPort = await this.guardReturn(
151
- this.startUploadAgent(uuid, uploadURL),
152
- 10_000,
153
- "Upload agent timeout"
154
- );
155
- } catch (e) {
156
- console.log(
157
- "Error starting upload agent. Test results will not be available on checksum."
158
- );
159
- }
160
-
161
- // write volatile config if set
162
- this.buildVolatileConfig();
163
-
164
- // build shell command
165
- const cmd = `${
166
- uploadAgentListeningPort
167
- ? `CHECKSUM_UPLOAD_AGENT_PORT=${uploadAgentListeningPort} `
168
- : ""
169
- } PWDEBUG=console npx playwright test --config ${this.getPlaywrightConfigFile()} ${args.join(
170
- " "
171
- )}`;
172
-
173
- try {
174
- // run tests
175
- await this.execCmd(cmd);
176
- } catch (e) {
177
- this.didFail = true;
178
- console.log("Error during test", e.message);
179
- } finally {
180
- const reportFile = this.getPlaywrightReportPath();
181
- if (!existsSync(reportFile)) {
182
- console.log(`Could not find report file at ${reportFile}`);
183
- } else {
184
- this.uploadAgent.stdin.write(`cli:report=${reportFile}`);
185
- }
186
- // upload report
187
- this.completeIndicators.tests = true;
188
- await this.handleCompleteMessage();
189
- }
190
- }
191
-
192
- getPlaywrightReportPath() {
193
- // default path
194
- let reportFolder = join(process.cwd(), "playwright-report");
195
- // check for playwright report path in config file
196
- // for now, ignore cases of multiple reporters
197
- const playwrightConfig = require(this.getPlaywrightConfigFile());
198
- const { reporter } = playwrightConfig;
199
- if (
200
- reporter instanceof Array &&
201
- reporter.length > 1 &&
202
- reporter[1]?.outputFolder
203
- ) {
204
- reportFolder = reporter[1]?.outputFolder;
205
- }
206
-
207
- // check for env variable
208
- if (process.env.PLAYWRIGHT_HTML_REPORT) {
209
- reportFolder = process.env.PLAYWRIGHT_HTML_REPORT;
210
- }
211
-
212
- return join(reportFolder, "index.html");
213
- }
214
-
215
- getPlaywrightConfigFile() {
216
- return join(this.getRootDirPath(), "playwright.config.ts");
217
- }
218
-
219
- startUploadAgent(sessionId: string, uploadURL: string) {
220
- return new Promise((resolve, reject) => {
221
- console.log("Starting upload agent");
222
- this.uploadAgent = childProcess.spawn("node", [
223
- this.UPLOAD_AGENT_PATH,
224
- JSON.stringify({
225
- sessionId,
226
- checksumApiURL: this.CHECKSUM_API_URL,
227
- apiKey: this.config.apiKey,
228
- }),
229
- ...(this.mock ? ["mock"] : []),
230
- ]);
231
-
232
- // Listen for messages from the upload agent
233
- this.uploadAgent.stdout.on("data", (data) => {
234
- const message = data.toString().trim();
235
-
236
- // if not a formatted message from upload agent - ignore
237
- if (!message.startsWith("upag:")) {
238
- // console.log(`Message from upload agent: ${message}`); // remove
239
- return;
240
- }
241
-
242
- const [key, value] = message.substring(5).split("=");
243
- if (key === "port") {
244
- console.log("Received port from upload agent", value);
245
- resolve(value);
246
- } else {
247
- this.handleUploadAgentMessage(key, value);
248
- }
249
- });
250
-
251
- // Handle exit event
252
- this.uploadAgent.on("exit", (code, signal) => {
253
- console.log(
254
- `upload agent process exited with code ${code} and signal ${signal}`
255
- );
256
- });
257
-
258
- // Handle errors
259
- this.uploadAgent.on("error", (err) => {
260
- console.error(`Error starting upload agent: ${err.message}`);
261
- });
262
- });
263
- }
264
-
265
- private async handleUploadAgentMessage(key: any, value: any) {
266
- switch (key) {
267
- case "complete":
268
- // console.log("Received upload complete message from upload agent");
269
- this.sendUploadsComplete().then(() => {
270
- this.completeIndicators.upload = true;
271
- });
272
- break;
273
- case "report-uploaded":
274
- // console.log("Received report uploaded message from upload agent");
275
- const reportURL = await this.sendTestrunEnd();
276
- this.completeIndicators.report = true;
277
- if (reportURL) {
278
- console.log(
279
- `*******************\n* Checksum report URL: ${reportURL}\n*******************`
280
- );
281
- }
282
- break;
283
-
284
- default:
285
- console.warn(`Unhandled upload agent message: ${key}=${value}`);
286
- }
287
- }
288
-
289
- async handleCompleteMessage() {
290
- while (true) {
291
- if (
292
- Object.keys(this.completeIndicators).find(
293
- (key) => !this.completeIndicators[key]
294
- )
295
- ) {
296
- await this.awaitSleep(1000);
297
- } else {
298
- console.log("Tests complete");
299
- this.shutdown(this.didFail ? 1 : 0);
300
- }
301
- }
302
- }
303
-
304
- shutdown(code = 0) {
305
- this.cleanup();
306
- process.exit(code);
307
- }
308
-
309
- buildVolatileConfig() {
310
- // if no volatile config set - return
311
- if (!this.volatileChecksumConfig) {
312
- return;
313
- }
314
-
315
- const configPath = this.getVolatileConfigPath();
316
- const configString = `
317
- import { RunMode, getChecksumConfig } from "@checksum-ai/runtime";
318
-
319
- export default getChecksumConfig(${JSON.stringify(this.config, null, 2)});
320
- `;
321
-
322
- writeFileSync(configPath, configString);
323
- }
324
-
325
- cleanup() {
326
- this.deleteVolatileConfig();
327
- this.uploadAgent.stdin.write("cli:shutdown");
328
- this.uploadAgent.kill();
329
- }
330
-
331
- async getSession() {
332
- try {
333
- if (this.mock) {
334
- this.testSession = {
335
- uuid: "session-id-1234",
336
- uploadURL: "http://localhost:3000/upload",
337
- };
338
- return;
339
- }
340
-
341
- const apiKey = this.config.apiKey;
342
- if (!apiKey || apiKey === "<API key>") {
343
- console.error("No API key found in checksum config");
344
- this.shutdown(1);
345
- }
346
-
347
- const body = JSON.stringify(await this.getEnvInfo());
348
- const res = await fetch(`${this.CHECKSUM_API_URL}/client-api/test-runs`, {
349
- method: "POST",
350
- headers: {
351
- Accept: "application/json",
352
- "Content-Type": "application/json",
353
- ChecksumAppCode: apiKey,
354
- },
355
- body,
356
- });
357
-
358
- this.testSession = await res.json();
359
- } catch (e) {
360
- console.error("Error getting checksum test session", e);
361
- this.shutdown(1);
362
- }
363
- }
364
-
365
- async sendTestrunEnd() {
366
- try {
367
- if (this.mock) {
368
- return "https://mock.report.url";
369
- }
370
- const { uuid: id } = this.testSession;
371
- const body = JSON.stringify({
372
- failed: this.didFail ? 1 : 0,
373
- passed: 0,
374
- healed: 0,
375
- endedAt: Date.now(),
376
- });
377
-
378
- const { reportURL } = await this.updateTestRun(
379
- `${this.CHECKSUM_API_URL}/client-api/test-runs/${id}`,
380
- "PATCH",
381
- body
382
- );
383
- return reportURL;
384
- } catch (e) {
385
- console.log("Error sending test run end", e.message);
386
- return null;
387
- }
388
- }
389
-
390
- async sendUploadsComplete() {
391
- if (this.mock) {
392
- return;
393
- }
394
- try {
395
- const { uuid: id } = this.testSession;
396
-
397
- await this.updateTestRun(
398
- `${this.CHECKSUM_API_URL}/client-api/test-runs/${id}/uploads-completed`,
399
- "PATCH"
400
- );
401
- } catch (e) {
402
- console.log("Error sending test run uploads complete", e.message);
403
- }
404
- }
405
-
406
- /**
407
- * Sends update to the test run API
408
- *
409
- * @param url API endpoint
410
- * @param method HTTP method
411
- * @param body request body
412
- * @returns JSON response from API
413
- */
414
- async updateTestRun(
415
- url: string,
416
- method: string,
417
- body: string | undefined = undefined
418
- ) {
419
- const res = await fetch(url, {
420
- method,
421
- headers: {
422
- Accept: "application/json",
423
- "Content-Type": "application/json",
424
- ChecksumAppCode: this.config.apiKey,
425
- },
426
- body,
427
- });
428
-
429
- return res.json();
430
- }
431
-
432
- async getEnvInfo() {
433
- const info = {
434
- commitHash: "",
435
- branch: "branch",
436
- environment: process.env.CI ? "CI" : "local",
437
- name: "name",
438
- startedAt: Date.now(),
439
- };
440
-
441
- try {
442
- info.commitHash = (await this.getCmdOutput(`git rev-parse HEAD`))
443
- .toString()
444
- .trim();
445
- } catch (e) {
446
- console.log("Error getting git hash", e.message);
447
- }
448
-
449
- try {
450
- info.branch = (await this.getCmdOutput(`git rev-parse --abbrev-ref HEAD`))
451
- .toString()
452
- .trim();
453
- } catch (e) {
454
- console.log("Error getting branch name", e.message);
455
- }
456
-
457
- return info;
458
- }
459
-
460
- getVolatileConfigPath() {
461
- return join(this.getRootDirPath(), "checksum.config.tmp.ts");
462
- }
463
-
464
- deleteVolatileConfig() {
465
- const configPath = this.getVolatileConfigPath();
466
- if (existsSync(configPath)) {
467
- rmSync(configPath);
468
- }
469
- }
470
-
471
- setChecksumConfig() {
472
- this.config = {
473
- ...(require(join(this.getRootDirPath(), "checksum.config.ts")).default ||
474
- {}),
475
- ...(this.volatileChecksumConfig || {}),
476
- };
477
- }
478
-
479
- /**
480
- * Search for checksum config in the command arguments.
481
- * If found, parse and remove from args.
482
- *
483
- * @param args arguments passed to the command
484
- * @returns args without checksum config
485
- */
486
- getChecksumConfigFromCommand(args) {
487
- // delete any old config if exists
488
- this.deleteVolatileConfig();
489
-
490
- for (const arg of args) {
491
- if (arg.startsWith("--checksum-config")) {
492
- try {
493
- this.volatileChecksumConfig = JSON.parse(arg.split("=")[1]);
494
- return args.filter((a) => a !== arg);
495
- } catch (e) {
496
- console.log("Error parsing checksum config", e.message);
497
- this.volatileChecksumConfig = undefined;
498
- }
499
- }
500
- }
501
-
502
- return args;
503
- }
504
-
505
- install() {
506
- console.log(
507
- "Creating Checksum directory and necessary files to run your tests"
508
- );
509
-
510
- const checksumRoot = this.getRootDirPath();
511
-
512
- if (!existsSync(this.getRootDirPath())) {
513
- mkdirSync(checksumRoot);
514
- }
515
-
516
- if (!existsSync(this.getChecksumRootOrigin())) {
517
- throw new Error(
518
- "Could not find checksum root directory, please install @checksum-ai/runtime package"
519
- );
520
-
521
- // automatically install?
522
- }
523
-
524
- // copy sources
525
- [
526
- "checksum.config.ts",
527
- "playwright.config.ts",
528
- "login.ts",
529
- "README.md",
530
- ].forEach((file) => {
531
- copyFileSync(
532
- join(this.getChecksumRootOrigin(), file),
533
- join(checksumRoot, file)
534
- );
535
- });
536
-
537
- // create tests folder
538
- mkdirSync(join(checksumRoot, "tests"), {
539
- recursive: true,
540
- });
541
-
542
- // create test data directories
543
- ["esra", "har", "trace", "log"].forEach((folder) => {
544
- mkdirSync(join(checksumRoot, "test-data", folder), {
545
- recursive: true,
546
- });
547
- });
548
- }
549
-
550
- getRootDirPath() {
551
- return join(process.cwd(), CHECKSUM_ROOT_FOLDER);
552
- }
553
-
554
- getChecksumRootOrigin() {
555
- return join(
556
- process.cwd(),
557
- "node_modules",
558
- "@checksum-ai",
559
- "runtime",
560
- "checksum-root"
561
- );
562
- }
563
-
564
- /**
565
- * Adds a timeout limit for a promise to resolve
566
- * Will throw an error if the promise does not resolve within the timeout limit
567
- *
568
- * @param promise promise to add timeout to
569
- * @param timeout timeout in milliseconds
570
- * @param errMessage error message to throw if timeout is reached
571
- * @returns promise that resolves if the original promise resolves within the timeout limit
572
- */
573
- guardReturn = async (
574
- promise,
575
- timeout = 1_000,
576
- errMessage = "action hang guard timed out"
577
- ) => {
578
- const timeoutStringIdentifier = "guard-timed-out";
579
- const guard = async () => {
580
- await this.awaitSleep(timeout + 1_000);
581
- return timeoutStringIdentifier;
582
- };
583
-
584
- const res = await Promise.race([promise, guard()]);
585
- if (typeof res === "string" && res === timeoutStringIdentifier) {
586
- throw new Error(errMessage);
587
- }
588
- return res;
589
- };
590
-
591
- awaitSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
592
- }
593
-
594
- /**
595
- * Trigger the main function
596
- */
597
- (async () => {
598
- await new ChecksumCLI().execute();
599
- })();
package/types.ts DELETED
@@ -1,58 +0,0 @@
1
- import { Page } from "@playwright/test";
2
-
3
- /**
4
- * Checksum runtime root folder
5
- */
6
- export const CHECKSUM_ROOT_FOLDER = "checksum";
7
-
8
- /**
9
- * Checksum runtime running mode -
10
- * normal - tests run normally
11
- * heal - checksum will attempt to heal tests that failed using fallback
12
- * refactor - checksum will attempt to refactor and improve your tests
13
- */
14
- export enum RunMode {
15
- Normal = "normal",
16
- Heal = "heal",
17
- Refactor = "refactor",
18
- }
19
-
20
- export interface IChecksumPage extends Page {
21
- initWithTest: (testInfo) => Promise<void>;
22
- checksumId: (id: string) => IChecksumPage;
23
- checksumStep: (thought: string, testFunction?: () => void) => IChecksumPage;
24
- testId: (testId: string) => void;
25
- }
26
-
27
- export type RuntimeOptions = {
28
- /**
29
- * fallback to ESRA if the action selector is not found
30
- */
31
- actionsESRAfallback: boolean;
32
- actionsLLMFallback: boolean; // use LLM fallback if action selector is not found
33
- // actionsChangeSelectors: boolean; // change the selector of the action if found better match
34
- // existingAssertionsESRAfallback: boolean; // fallback to ESRA if the existing assertion selector is not found
35
- // existingAssertionsChangeSelectors: boolean; // change the selector of existing assertion if found better match
36
- newAssertionsEnabled: boolean; // add new assertions
37
- writeTraceFile: boolean; // saves trace file at the end of the test
38
- useMockData: boolean; // use mocked data
39
- printLogs: boolean; // print logs to console
40
- };
41
-
42
- export type ChecksumConfig = {
43
- runMode: RunMode;
44
- apiKey: string;
45
- baseURL: string;
46
- username?: string;
47
- password?: string;
48
- options?: RuntimeOptions;
49
-
50
- apiURL: string;
51
- uploadAgentHOST: string;
52
- uploadAgentPort: number;
53
- };
54
-
55
- export type TestSuiteSession = {
56
- uuid: string;
57
- uploadURL: string;
58
- };