@checksum-ai/runtime 1.4.14 → 1.4.16-alpha
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/.env +1 -1
- package/package.json +1 -1
- package/scripts/playwright_patches/1.56.1.js +353 -0
package/.env
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
CHECKSUM_RUNTIME_BUILD_TIME=2025-12-
|
|
1
|
+
CHECKSUM_RUNTIME_BUILD_TIME=2025-12-02T12:55:26.876Z
|
package/package.json
CHANGED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const { join } = require("path");
|
|
3
|
+
|
|
4
|
+
// Args
|
|
5
|
+
const on = process.argv[2] !== "off";
|
|
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");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Replaces content.
|
|
31
|
+
// When "on" is true, the new content is written to the file replacing the original content,
|
|
32
|
+
// otherwise the original content is restored.
|
|
33
|
+
function replaceContent(filePath, originalContent, newContent) {
|
|
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
|
+
|
|
73
|
+
const originalContent = 'if ((0, import_debug.debugMode)() === "console")';
|
|
74
|
+
|
|
75
|
+
const newContent = "";
|
|
76
|
+
|
|
77
|
+
replaceContent(file, originalContent, newContent);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Add implementation for generateSelectorAndLocator and inject to Playwright console API
|
|
81
|
+
function addGenerateSelectorAndLocator(projectRoot) {
|
|
82
|
+
const file = join(
|
|
83
|
+
projectRoot,
|
|
84
|
+
"node_modules/playwright-core/lib/generated/injectedScriptSource.js"
|
|
85
|
+
);
|
|
86
|
+
if (!doesFileExist(file)) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const entryPointText1 = "this._generateLocator(element, language),\\n ";
|
|
90
|
+
const appendText1 =
|
|
91
|
+
"generateSelectorAndLocator: (element, language) => this._generateSelectorAndLocator(element, language),\\n asLocator,\\n ";
|
|
92
|
+
amend(file, entryPointText1, appendText1);
|
|
93
|
+
|
|
94
|
+
const entryPointText2 = `return asLocator(language || "javascript", selector);\\n }\\n `;
|
|
95
|
+
const appendText2 =
|
|
96
|
+
'_generateSelectorAndLocator(element, language) {\\n if (!(element instanceof Element))\\n throw new Error(`Usage: playwright.locator(element).`);\\n const selector = this._injectedScript.generateSelectorSimple(element);\\n return {selector, locator: asLocator(language || \\"javascript\\", selector)};\\n }\\n ';
|
|
97
|
+
amend(file, entryPointText2, appendText2);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// -------- [Runtime modifications] -------- //
|
|
101
|
+
|
|
102
|
+
function expect(projectRoot) {
|
|
103
|
+
const file = join(
|
|
104
|
+
projectRoot,
|
|
105
|
+
"node_modules/playwright/lib/matchers/expect.js"
|
|
106
|
+
);
|
|
107
|
+
if (!doesFileExist(file)) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
let originalContent, newContent;
|
|
111
|
+
|
|
112
|
+
// originalContent = `return (...args) => {
|
|
113
|
+
// const testInfo = (0, _globals.currentTestInfo)();`;
|
|
114
|
+
// newContent = `return (...args) => {
|
|
115
|
+
// let noSoft = false;
|
|
116
|
+
// if (args.find(arg=>arg==='no-soft')){
|
|
117
|
+
// noSoft = true;
|
|
118
|
+
// args.pop();
|
|
119
|
+
// }
|
|
120
|
+
// const testInfo = (0, _globals.currentTestInfo)();`;
|
|
121
|
+
// replaceContent(file, originalContent, newContent);
|
|
122
|
+
|
|
123
|
+
// originalContent = `step.complete({
|
|
124
|
+
// error
|
|
125
|
+
// })`;
|
|
126
|
+
// newContent = `step.complete({
|
|
127
|
+
// error,
|
|
128
|
+
// noSoft
|
|
129
|
+
// })`;
|
|
130
|
+
// replaceContent(file, originalContent, newContent);
|
|
131
|
+
|
|
132
|
+
// originalContent = `if (this._info.isSoft) testInfo._failWithError(error);else throw error;`;
|
|
133
|
+
// newContent = `if (this._info.isSoft && !noSoft) testInfo._failWithError(error);else throw error;`;
|
|
134
|
+
// replaceContent(file, originalContent, newContent);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function testInfo(projectRoot) {
|
|
138
|
+
const file = join(
|
|
139
|
+
projectRoot,
|
|
140
|
+
"node_modules/playwright/lib/worker/testInfo.js"
|
|
141
|
+
);
|
|
142
|
+
if (!doesFileExist(file)) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
let originalContent, newContent;
|
|
146
|
+
let entryPointText, appendText;
|
|
147
|
+
|
|
148
|
+
// originalContent = `const filteredStack = (0, _util.filteredStackTrace)((0, _utils.captureRawStack)());`;
|
|
149
|
+
// newContent = `const filteredStack = (0, _util.filteredStackTrace)((0, _utils.captureRawStack)().filter(s=>!s.includes('@checksum-ai/runtime')));`;
|
|
150
|
+
// replaceContent(file, originalContent, newContent);
|
|
151
|
+
|
|
152
|
+
entryPointText = `location = location || filteredStack[0];`;
|
|
153
|
+
appendText = `\nif (this._checksumInternal) {
|
|
154
|
+
location = undefined;
|
|
155
|
+
this._checksumInternal = false;
|
|
156
|
+
}
|
|
157
|
+
if (this._checksumNoLocation){
|
|
158
|
+
location = undefined;
|
|
159
|
+
}`;
|
|
160
|
+
amend(file, entryPointText, appendText);
|
|
161
|
+
|
|
162
|
+
originalContent = `if (childStep.error && childStep.infectParentStepsWithError) {`;
|
|
163
|
+
newContent = `if (childStep.error && childStep.infectParentStepsWithError && !step.preventInfectParentStepsWithError) {`;
|
|
164
|
+
replaceContent(file, originalContent, newContent);
|
|
165
|
+
|
|
166
|
+
originalContent = `_failWithError(error) {`;
|
|
167
|
+
newContent = `addError(error, message) {
|
|
168
|
+
const serialized = (0, import_util2.testInfoError)(error);
|
|
169
|
+
serialized.message = [message, serialized.message].join('\\n\\n');
|
|
170
|
+
serialized.stack = [message, serialized.stack].join('\\n\\n');
|
|
171
|
+
const step = error[stepSymbol];
|
|
172
|
+
if (step && step.boxedStack) serialized.stack = \`\${error.name}: \${error.message}\\n\${(0, import_utils.stringifyStackFrames)(step.boxedStack).join('\\n')}\`;
|
|
173
|
+
this.errors.push(serialized);
|
|
174
|
+
}
|
|
175
|
+
_failWithError(error) {`;
|
|
176
|
+
replaceContent(file, originalContent, newContent);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function testType(projectRoot) {
|
|
180
|
+
const file = join(
|
|
181
|
+
projectRoot,
|
|
182
|
+
"node_modules/playwright/lib/common/testType.js"
|
|
183
|
+
);
|
|
184
|
+
if (!doesFileExist(file)) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
entryPointText = `return await (0, import_utils.currentZone)().with("stepZone", step).run(async () => {`;
|
|
189
|
+
// entryPointText = `return await _utils.zones.run('stepZone', step, async () => {`;
|
|
190
|
+
appendText = `\nif (options.obtainStep){
|
|
191
|
+
options.obtainStep(step);
|
|
192
|
+
}`;
|
|
193
|
+
amend(file, entryPointText, appendText);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function indexContent(projectRoot) {
|
|
197
|
+
const file = join(projectRoot, "node_modules/playwright/lib/index.js");
|
|
198
|
+
if (!doesFileExist(file)) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
let originalContent, newContent;
|
|
202
|
+
originalContent = `const browser = await playwright[browserName].launch();`;
|
|
203
|
+
newContent = `
|
|
204
|
+
let browser = playwright[browserName];
|
|
205
|
+
try {
|
|
206
|
+
const { playwrightExtra } = testInfo?.project?.use || {};
|
|
207
|
+
if (playwrightExtra && playwrightExtra?.length) {
|
|
208
|
+
const pw = require("playwright-extra")
|
|
209
|
+
const PupeteerExtraPlugin = require("puppeteer-extra-plugin").PuppeteerExtraPlugin
|
|
210
|
+
const chromium = pw.chromium;
|
|
211
|
+
|
|
212
|
+
playwrightExtra.forEach((plugin, i) => {
|
|
213
|
+
try {
|
|
214
|
+
if(!(plugin instanceof PupeteerExtraPlugin)){
|
|
215
|
+
console.warn(\`Plugin at index \${i} is not an instance of PupeteerExtraPlugin\`);
|
|
216
|
+
}
|
|
217
|
+
chromium.use(plugin);
|
|
218
|
+
} catch (e) {
|
|
219
|
+
console.warn(e);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
browser = chromium;
|
|
223
|
+
}
|
|
224
|
+
} catch (e) {
|
|
225
|
+
console.warn(
|
|
226
|
+
"CHECKSUM: Failed to load Playwright Extra, using Playwright instead.",
|
|
227
|
+
e
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
browser = await browser.launch();
|
|
231
|
+
`;
|
|
232
|
+
replaceContent(file, originalContent, newContent);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function channelOwner(projectRoot) {
|
|
236
|
+
const file = join(
|
|
237
|
+
projectRoot,
|
|
238
|
+
"node_modules/playwright-core/lib/client/channelOwner.js"
|
|
239
|
+
);
|
|
240
|
+
if (!doesFileExist(file)) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
let originalContent, newContent;
|
|
244
|
+
let entryPointText, appendText;
|
|
245
|
+
|
|
246
|
+
entryPointText = `async _wrapApiCall(func, options) {`;
|
|
247
|
+
|
|
248
|
+
appendText = `\nif (this._checksumInternal){
|
|
249
|
+
options = options || {};
|
|
250
|
+
options.internal = true;
|
|
251
|
+
}`;
|
|
252
|
+
amend(file, entryPointText, appendText);
|
|
253
|
+
|
|
254
|
+
entryPointText = `const apiZone = { title: options?.title, apiName: stackTrace.apiName, frames: stackTrace.frames, internal: options?.internal ?? false, reported: false, userData: void 0, stepId: void 0 };`;
|
|
255
|
+
|
|
256
|
+
appendText = `\nif (!apiZone.internal && this._checksumTitle){
|
|
257
|
+
apiZone.apiName = this._checksumTitle;
|
|
258
|
+
this._checksumTitle = undefined;
|
|
259
|
+
}
|
|
260
|
+
if (!apiZone.apiName){
|
|
261
|
+
options = options || {};
|
|
262
|
+
options.internal = true;
|
|
263
|
+
apiZone.internal = true;
|
|
264
|
+
apiZone.reported = true;
|
|
265
|
+
}
|
|
266
|
+
if (apiZone.apiName && apiZone.apiName.startsWith('proxy')) {
|
|
267
|
+
apiZone.apiName = apiZone.apiName.replace('proxy', 'page');
|
|
268
|
+
}`;
|
|
269
|
+
amend(file, entryPointText, appendText);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function stackTrace(projectRoot) {
|
|
273
|
+
const file = join(
|
|
274
|
+
projectRoot,
|
|
275
|
+
"node_modules/playwright-core/lib/utils/isomorphic/stackTrace.js"
|
|
276
|
+
);
|
|
277
|
+
if (!doesFileExist(file)) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
let originalContent, newContent;
|
|
282
|
+
|
|
283
|
+
// Create a regex for getting a file for each line in the stacktrace that overrides the original regex
|
|
284
|
+
// This regex is only used for getting files, the original regex is sitll used for the rest of the content
|
|
285
|
+
originalContent = `let file = match[7];`;
|
|
286
|
+
const fileRe = /(\/.*?\.[a-zA-Z0-9]+)(?=:\d+:\d+)/;
|
|
287
|
+
newContent = `
|
|
288
|
+
const fileRe = new RegExp(${JSON.stringify(fileRe.source)}, "${
|
|
289
|
+
fileRe.flags
|
|
290
|
+
}");
|
|
291
|
+
const m = fileRe.exec(match[0] ?? "");
|
|
292
|
+
let file = m ? m[1] : undefined;
|
|
293
|
+
`;
|
|
294
|
+
replaceContent(file, originalContent, newContent);
|
|
295
|
+
|
|
296
|
+
// Filter out checksum-ai/runtime from stack traces
|
|
297
|
+
originalContent = `return stack.split("\\n");`;
|
|
298
|
+
newContent = `return stack.split("\\n").filter(s=>!s.includes('@checksum-ai/runtime'));`;
|
|
299
|
+
replaceContent(file, originalContent, newContent);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function reportTraceFile(projectRoot) {
|
|
303
|
+
const file = join(
|
|
304
|
+
projectRoot,
|
|
305
|
+
"node_modules/playwright/lib/reporters/html.js"
|
|
306
|
+
);
|
|
307
|
+
if (!doesFileExist(file)) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
let originalContent, newContent;
|
|
312
|
+
|
|
313
|
+
originalContent = `const buffer = import_fs.default.readFileSync(a.path);`;
|
|
314
|
+
newContent = `let buffer = import_fs.default.readFileSync(a.path);
|
|
315
|
+
if (a.name === "trace") {
|
|
316
|
+
let retries = 2;
|
|
317
|
+
while (!buffer.slice(0,100).toString().startsWith("checksum-playwright-trace") && retries > 0) {
|
|
318
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 5000);
|
|
319
|
+
buffer = import_fs.default.readFileSync(a.path)
|
|
320
|
+
retries--;
|
|
321
|
+
}
|
|
322
|
+
}`;
|
|
323
|
+
replaceContent(file, originalContent, newContent);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// -------- [Run] -------- //
|
|
327
|
+
|
|
328
|
+
const isRuntime = true || process.env.RUNTIME === "true";
|
|
329
|
+
|
|
330
|
+
function run(projectPath) {
|
|
331
|
+
try {
|
|
332
|
+
if (fs.existsSync(projectPath)) {
|
|
333
|
+
alwaysInjectScripts(projectPath);
|
|
334
|
+
addGenerateSelectorAndLocator(projectPath);
|
|
335
|
+
if (isRuntime) {
|
|
336
|
+
expect(projectPath);
|
|
337
|
+
testInfo(projectPath);
|
|
338
|
+
testType(projectPath);
|
|
339
|
+
channelOwner(projectPath);
|
|
340
|
+
stackTrace(projectPath);
|
|
341
|
+
indexContent(projectPath);
|
|
342
|
+
reportTraceFile(projectPath);
|
|
343
|
+
}
|
|
344
|
+
} else {
|
|
345
|
+
console.warn("Project path not found", projectPath);
|
|
346
|
+
}
|
|
347
|
+
} catch (e) {
|
|
348
|
+
// ignore
|
|
349
|
+
console.error(e);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
module.exports = run;
|