@checksum-ai/runtime 1.1.10 → 1.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/checksum-root/playwright.config.ts +7 -1
- package/checksumlib.js +104 -3
- package/cli.js +31 -58
- package/index.d.ts +19 -0
- package/index.js +130 -248
- package/package.json +5 -5
- package/repl.js +1 -1
- package/scripts/patch.js +36 -325
- package/scripts/playwright_patches/1.41.2.js +310 -0
- package/scripts/playwright_patches/1.46.0.js +305 -0
- package/test-run-monitor.js +12 -13
- package/.DS_Store +0 -0
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@checksum-ai/runtime",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.13",
|
|
4
4
|
"description": "Checksum.ai test runtime",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@playwright/test": "1.
|
|
8
|
-
"ts-node": "^10.9.1",
|
|
7
|
+
"@playwright/test": "1.46.0",
|
|
9
8
|
"jsdom": "^22.1.0",
|
|
10
|
-
"
|
|
11
|
-
"request": "^2.88.2"
|
|
9
|
+
"playwright-extra": "^4.3.6",
|
|
10
|
+
"request": "^2.88.2",
|
|
11
|
+
"ts-node": "^10.9.1"
|
|
12
12
|
},
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
package/repl.js
CHANGED
package/scripts/patch.js
CHANGED
|
@@ -1,347 +1,58 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
|
-
const
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const { join, resolve } = require("path");
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// -------- [Modifiers] -------- //
|
|
8
|
-
|
|
9
|
-
// Amends the file with the given entry point text and append text
|
|
10
|
-
// When "on" is true, the append text is added to the entry point,
|
|
11
|
-
// otherwise the append text is completely removed from the file
|
|
12
|
-
function amend(filePath, entryPointText, appendText) {
|
|
13
|
-
const data = fs.readFileSync(filePath, "utf8");
|
|
14
|
-
if (!data.includes(entryPointText)) {
|
|
15
|
-
throw new Error("Entry point not found!", entryPointText);
|
|
16
|
-
}
|
|
17
|
-
// Ignore if the append text is already present
|
|
18
|
-
if (on && data.includes(appendText)) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
// Add or clear according to on state
|
|
22
|
-
const result = on
|
|
23
|
-
? data.replace(entryPointText, entryPointText + appendText)
|
|
24
|
-
: data.replace(appendText, "");
|
|
25
|
-
|
|
26
|
-
// Write
|
|
27
|
-
fs.writeFileSync(filePath, result, "utf8");
|
|
5
|
+
function getPlaywrightPatchesDirectory() {
|
|
6
|
+
return join(__dirname, "playwright_patches");
|
|
28
7
|
}
|
|
29
8
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// Read the file content
|
|
35
|
-
const fileContent = fs.readFileSync(filePath, "utf8");
|
|
36
|
-
|
|
37
|
-
// add a marker for newContent that can be later recognized for "off" state
|
|
38
|
-
newContent = `/* checksumai */ ${newContent}`;
|
|
39
|
-
|
|
40
|
-
if (on && fileContent.includes(newContent)) {
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Join the lines back into a single string
|
|
45
|
-
const updatedContent = on
|
|
46
|
-
? fileContent.replace(originalContent, newContent)
|
|
47
|
-
: fileContent.replace(newContent, originalContent);
|
|
48
|
-
|
|
49
|
-
// Write the modified content back to the file
|
|
50
|
-
fs.writeFileSync(filePath, updatedContent, "utf8");
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function doesFileExist(filePath) {
|
|
54
|
-
if (!fs.existsSync(filePath)) {
|
|
55
|
-
console.warn("File not found", filePath);
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// -------- [Modifications] -------- //
|
|
62
|
-
|
|
63
|
-
// Remove conditions for injecting Playwright scripts
|
|
64
|
-
function alwaysInjectScripts(projectRoot) {
|
|
65
|
-
const file = join(
|
|
66
|
-
projectRoot,
|
|
67
|
-
"node_modules/playwright-core/lib/server/browserContext.js"
|
|
68
|
-
);
|
|
69
|
-
if (!doesFileExist(file)) {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
const originalContent =
|
|
73
|
-
"if ((0, _utils.debugMode)() === 'console') await this.extendInjectedScript(consoleApiSource.source);";
|
|
9
|
+
const availablePatchVersions = fs
|
|
10
|
+
.readdirSync(getPlaywrightPatchesDirectory())
|
|
11
|
+
.map((patchFile) => path.basename(patchFile, ".js"))
|
|
12
|
+
.sort();
|
|
74
13
|
|
|
75
|
-
|
|
76
|
-
"await this.extendInjectedScript(consoleApiSource.source);";
|
|
77
|
-
|
|
78
|
-
replaceContent(file, originalContent, newContent);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Add implementation for generateSelectorAndLocator and inject to Playwright console API
|
|
82
|
-
function addGenerateSelectorAndLocator(projectRoot) {
|
|
83
|
-
const file = join(
|
|
84
|
-
projectRoot,
|
|
85
|
-
"node_modules/playwright-core/lib/generated/consoleApiSource.js"
|
|
86
|
-
);
|
|
87
|
-
if (!doesFileExist(file)) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
const entryPointText1 = "this._generateLocator(element, language),\\n ";
|
|
91
|
-
const appendText1 =
|
|
92
|
-
"generateSelectorAndLocator: (element, language) => this._generateSelectorAndLocator(element, language),\\n asLocator,\\n ";
|
|
93
|
-
amend(file, entryPointText1, appendText1);
|
|
94
|
-
|
|
95
|
-
const entryPointText2 =
|
|
96
|
-
'return asLocator(language || \\"javascript\\", selector);\\n }\\n ';
|
|
97
|
-
const appendText2 =
|
|
98
|
-
'_generateSelectorAndLocator(element, language) {\\n if (!(element instanceof Element))\\n throw new Error(`Usage: playwright.locator(element).`);\\n const selector = this._injectedScript.generateSelector(element);\\n return {selector, locator: asLocator(language || \\"javascript\\", selector)};\\n }\\n ';
|
|
99
|
-
amend(file, entryPointText2, appendText2);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// -------- [Runtime modifications] -------- //
|
|
103
|
-
|
|
104
|
-
function expect(projectRoot) {
|
|
105
|
-
const file = join(
|
|
106
|
-
projectRoot,
|
|
107
|
-
"node_modules/playwright/lib/matchers/expect.js"
|
|
108
|
-
);
|
|
109
|
-
if (!doesFileExist(file)) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
let originalContent, newContent;
|
|
113
|
-
|
|
114
|
-
originalContent = `return (...args) => {
|
|
115
|
-
const testInfo = (0, _globals.currentTestInfo)();`;
|
|
116
|
-
newContent = `return (...args) => {
|
|
117
|
-
let noSoft = false;
|
|
118
|
-
if (args[args.length-1]==='no-soft'){
|
|
119
|
-
noSoft = true;
|
|
120
|
-
args.pop();
|
|
121
|
-
}
|
|
122
|
-
const testInfo = (0, _globals.currentTestInfo)();`;
|
|
123
|
-
replaceContent(file, originalContent, newContent);
|
|
124
|
-
|
|
125
|
-
originalContent = `const rawStack = (0, _utils.captureRawStack)();`;
|
|
126
|
-
newContent = `const rawStack = (0, _utils.captureRawStack)().filter(s=>!s.includes('@checksum-ai/runtime'));`;
|
|
127
|
-
replaceContent(file, originalContent, newContent);
|
|
128
|
-
|
|
129
|
-
originalContent = `step.complete({
|
|
130
|
-
error
|
|
131
|
-
})`;
|
|
132
|
-
newContent = `step.complete({
|
|
133
|
-
error,
|
|
134
|
-
noSoft
|
|
135
|
-
})`;
|
|
136
|
-
replaceContent(file, originalContent, newContent);
|
|
137
|
-
|
|
138
|
-
originalContent = `if (!this._info.isSoft) throw error;`;
|
|
139
|
-
newContent = `if (!this._info.isSoft || noSoft) throw error;`;
|
|
140
|
-
replaceContent(file, originalContent, newContent);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function testInfo(projectRoot) {
|
|
144
|
-
const file = join(
|
|
145
|
-
projectRoot,
|
|
146
|
-
"node_modules/playwright/lib/worker/testInfo.js"
|
|
147
|
-
);
|
|
148
|
-
if (!doesFileExist(file)) {
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
let originalContent, newContent;
|
|
152
|
-
let entryPointText, appendText;
|
|
14
|
+
// console.log("patchVersions", availablePatchVersions);
|
|
153
15
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
entryPointText = `data.location = data.location || filteredStack[0];`;
|
|
163
|
-
appendText = `\nif (this._checksumInternal) {
|
|
164
|
-
data.location = undefined;
|
|
165
|
-
this._checksumInternal = false;
|
|
16
|
+
function findSuitablePatchVersion(version) {
|
|
17
|
+
let suitablePatchVersion = availablePatchVersions[0];
|
|
18
|
+
for (const patchVersion of availablePatchVersions) {
|
|
19
|
+
if (version >= patchVersion) {
|
|
20
|
+
suitablePatchVersion = patchVersion;
|
|
21
|
+
} else {
|
|
22
|
+
break;
|
|
166
23
|
}
|
|
167
|
-
if (this._checksumNoLocation){
|
|
168
|
-
data.location = undefined;
|
|
169
|
-
}`;
|
|
170
|
-
amend(file, entryPointText, appendText);
|
|
171
|
-
|
|
172
|
-
originalContent = `if (!step.error) {`;
|
|
173
|
-
newContent = `if (!step.error && !step.preventInfectParentStepsWithError) {`;
|
|
174
|
-
replaceContent(file, originalContent, newContent);
|
|
175
|
-
|
|
176
|
-
originalContent = `_failWithError(error, isHardError) {`;
|
|
177
|
-
newContent = `addError(error, message) {
|
|
178
|
-
const serialized = (0, _util.serializeError)(error);
|
|
179
|
-
serialized.message = [message, serialized.message].join('\\n\\n');
|
|
180
|
-
serialized.stack = [message, serialized.stack].join('\\n\\n');
|
|
181
|
-
const step = error[stepSymbol];
|
|
182
|
-
if (step && step.boxedStack) serialized.stack = \`\${error.name}: \${error.message}\\n\${(0, _utils.stringifyStackFrames)(step.boxedStack).join('\\n')}\`;
|
|
183
|
-
this.errors.push(serialized);
|
|
184
|
-
}
|
|
185
|
-
_failWithError(error, isHardError) {`;
|
|
186
|
-
replaceContent(file, originalContent, newContent);
|
|
187
|
-
|
|
188
|
-
originalContent = `if (step.isSoft && result.error) this._failWithError`;
|
|
189
|
-
newContent = `if (!result.noSoft && step.isSoft && result.error) this._failWithError`;
|
|
190
|
-
replaceContent(file, originalContent, newContent);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function testType(projectRoot) {
|
|
194
|
-
const file = join(
|
|
195
|
-
projectRoot,
|
|
196
|
-
"node_modules/playwright/lib/common/testType.js"
|
|
197
|
-
);
|
|
198
|
-
if (!doesFileExist(file)) {
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
let originalContent, newContent;
|
|
203
|
-
|
|
204
|
-
originalContent = `}, async () => {
|
|
205
|
-
// Make sure that internal "step" is not leaked to the user callback.
|
|
206
|
-
return await body();
|
|
207
|
-
});`;
|
|
208
|
-
newContent = `}, async (step) => {
|
|
209
|
-
if (options.obtainStep){
|
|
210
|
-
options.obtainStep(step);
|
|
211
|
-
}
|
|
212
|
-
// Make sure that internal "step" is not leaked to the user callback.
|
|
213
|
-
return await body();
|
|
214
|
-
});`;
|
|
215
|
-
replaceContent(file, originalContent, newContent);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function channelOwner(projectRoot) {
|
|
219
|
-
const file = join(
|
|
220
|
-
projectRoot,
|
|
221
|
-
"node_modules/playwright-core/lib/client/channelOwner.js"
|
|
222
|
-
);
|
|
223
|
-
if (!doesFileExist(file)) {
|
|
224
|
-
return;
|
|
225
24
|
}
|
|
226
|
-
|
|
227
|
-
let entryPointText, appendText;
|
|
228
|
-
|
|
229
|
-
entryPointText = `async _wrapApiCall(func, isInternal = false) {`;
|
|
230
|
-
appendText = `\nif (this._checksumInternal){
|
|
231
|
-
isInternal = true;
|
|
232
|
-
}`;
|
|
233
|
-
amend(file, entryPointText, appendText);
|
|
234
|
-
|
|
235
|
-
originalContent = `const stack = (0, _stackTrace.captureRawStack)();`;
|
|
236
|
-
newContent = `const stack = (0, _stackTrace.captureRawStack)().filter(s=>!s.includes('@checksum-ai/runtime'));`;
|
|
237
|
-
replaceContent(file, originalContent, newContent);
|
|
238
|
-
|
|
239
|
-
entryPointText = `let apiName = stackTrace.apiName;`;
|
|
240
|
-
appendText = `\nif (!isInternal && this._checksumTitle){
|
|
241
|
-
apiName = this._checksumTitle;
|
|
242
|
-
this._checksumTitle = undefined;
|
|
243
|
-
}`;
|
|
244
|
-
amend(file, entryPointText, appendText);
|
|
25
|
+
return suitablePatchVersion;
|
|
245
26
|
}
|
|
246
27
|
|
|
247
|
-
// -------- [Run] -------- //
|
|
248
|
-
|
|
249
|
-
const isRuntime = true || process.env.RUNTIME === "true";
|
|
250
28
|
const projectRootFromEnv = process.env.PROJECT_ROOT;
|
|
251
|
-
|
|
252
29
|
const projectPaths = projectRootFromEnv
|
|
253
30
|
? [projectRootFromEnv]
|
|
254
|
-
: [
|
|
255
|
-
".",
|
|
256
|
-
"../checksum-ai-libs/lib",
|
|
257
|
-
"../backend",
|
|
258
|
-
"../libs/lib",
|
|
259
|
-
"../frontend",
|
|
260
|
-
"../runtime",
|
|
261
|
-
].map((project) => join(process.cwd(), project));
|
|
31
|
+
: [".", "../", "../.."].map((project) => join(process.cwd(), project));
|
|
262
32
|
|
|
263
33
|
for (const projectPath of projectPaths) {
|
|
264
34
|
try {
|
|
265
|
-
if (fs.existsSync(projectPath)) {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
35
|
+
if (fs.existsSync(join(projectPath, "node_modules", "playwright"))) {
|
|
36
|
+
const absolutePathToPackageJSON = resolve(
|
|
37
|
+
join(projectPath, "node_modules", "playwright", "package.json")
|
|
38
|
+
);
|
|
39
|
+
const packageJSON = require(absolutePathToPackageJSON);
|
|
40
|
+
const playwrightVersion = packageJSON.version;
|
|
41
|
+
const patchVersion = findSuitablePatchVersion(playwrightVersion);
|
|
42
|
+
// console.log(
|
|
43
|
+
// "Found installed version",
|
|
44
|
+
// playwrightVersion,
|
|
45
|
+
// "and will use patch for version",
|
|
46
|
+
// patchVersion
|
|
47
|
+
// );
|
|
48
|
+
const patchPath = resolve(
|
|
49
|
+
join(getPlaywrightPatchesDirectory(), `${patchVersion}.js`)
|
|
50
|
+
);
|
|
51
|
+
require(patchPath)(projectPath);
|
|
274
52
|
} else {
|
|
275
53
|
// console.warn("Project path not found", projectPath);
|
|
276
54
|
}
|
|
277
55
|
} catch (e) {
|
|
278
|
-
// ignore
|
|
56
|
+
// ignore for now although we might want to notify users that patching wasn't successful
|
|
279
57
|
}
|
|
280
58
|
}
|
|
281
|
-
|
|
282
|
-
// function alwaysInjectScriptsOld() {
|
|
283
|
-
// const file = "node_modules/playwright-core/lib/server/browserContext.js";
|
|
284
|
-
// const lineNumber = 108;
|
|
285
|
-
// const originalLine =
|
|
286
|
-
// " if ((0, _utils.debugMode)() === 'console') await this.extendInjectedScript(consoleApiSource.source);";
|
|
287
|
-
// const newLine =
|
|
288
|
-
// " await this.extendInjectedScript(consoleApiSource.source);";
|
|
289
|
-
// replaceLine(file, lineNumber, originalLine, newLine);
|
|
290
|
-
// }
|
|
291
|
-
|
|
292
|
-
// // Replaces the content of the specified line in the file.
|
|
293
|
-
// // When "on" is true, the new line is written to the file,
|
|
294
|
-
// // otherwise the original line is restored.
|
|
295
|
-
// function replaceLine(filePath, lineNumber, originalLine, newLine) {
|
|
296
|
-
// // Read the file content
|
|
297
|
-
// const fileContent = fs.readFileSync(filePath, "utf8");
|
|
298
|
-
|
|
299
|
-
// // Split the content into an array of lines
|
|
300
|
-
// const lines = fileContent.split(/\r?\n/);
|
|
301
|
-
|
|
302
|
-
// // Check if the line number is within the range of the file's line count
|
|
303
|
-
// if (lineNumber < 1 || lineNumber > lines.length) {
|
|
304
|
-
// throw new Error("Line number out of range");
|
|
305
|
-
// }
|
|
306
|
-
|
|
307
|
-
// // Replace the content of the specified line
|
|
308
|
-
// lines[lineNumber - 1] = on ? newLine : originalLine;
|
|
309
|
-
|
|
310
|
-
// // Join the lines back into a single string
|
|
311
|
-
// const updatedContent = lines.join("\n");
|
|
312
|
-
|
|
313
|
-
// // Write the modified content back to the file
|
|
314
|
-
// fs.writeFileSync(filePath, updatedContent, "utf8");
|
|
315
|
-
// }
|
|
316
|
-
|
|
317
|
-
// // Replaces the content of the specified line in the file.
|
|
318
|
-
// // When "on" is true, the new line is written to the file,
|
|
319
|
-
// // otherwise the original line is restored.
|
|
320
|
-
// function replaceContent(filePath, originalContent, newContent) {
|
|
321
|
-
// // Read the file content
|
|
322
|
-
// const fileContent = fs.readFileSync(filePath, "utf8");
|
|
323
|
-
|
|
324
|
-
// // add a marker for newContent that can be later recognized for "off" state
|
|
325
|
-
// newContent = `/* checksumai */ ${newContent}`;
|
|
326
|
-
|
|
327
|
-
// // Split the content into an array of lines
|
|
328
|
-
// const lines = fileContent.split(/\r?\n/);
|
|
329
|
-
|
|
330
|
-
// lines.forEach((line, index) => {
|
|
331
|
-
// if (on) {
|
|
332
|
-
// if (line.includes(originalContent)) {
|
|
333
|
-
// lines[index] = line.replace(originalContent, newContent);
|
|
334
|
-
// }
|
|
335
|
-
// } else {
|
|
336
|
-
// if (line.includes(newContent)) {
|
|
337
|
-
// lines[index] = line.replace(newContent, originalContent);
|
|
338
|
-
// }
|
|
339
|
-
// }
|
|
340
|
-
// });
|
|
341
|
-
|
|
342
|
-
// // Join the lines back into a single string
|
|
343
|
-
// const updatedContent = lines.join("\n");
|
|
344
|
-
|
|
345
|
-
// // Write the modified content back to the file
|
|
346
|
-
// fs.writeFileSync(filePath, updatedContent, "utf8");
|
|
347
|
-
// }
|