@checksum-ai/runtime 4.18.1 → 5.0.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.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Checksum.ai test runtime",
4
4
  "main": "index.js",
5
5
  "dependencies": {
6
- "@playwright/test": "1.62.1",
6
+ "@playwright/test": "1.63.0",
7
7
  "dotenv": "17.4.2",
8
8
  "jsdom": "30.0.1",
9
9
  "playwright-extra": "4.3.6",
@@ -45,5 +45,5 @@
45
45
  "url": "https://github.com/checksum-ai/checksum-ai-monorepo/issues"
46
46
  },
47
47
  "homepage": "https://github.com/checksum-ai/checksum-ai-monorepo/tree/main/packages/runtime#readme",
48
- "version": "4.18.1"
48
+ "version": "5.0.0"
49
49
  }
package/scripts/patch.js CHANGED
@@ -6,14 +6,19 @@ function getPlaywrightPatchesDirectory() {
6
6
  return join(__dirname, "playwright_patches");
7
7
  }
8
8
 
9
- const availablePatchVersions = fs
10
- .readdirSync(getPlaywrightPatchesDirectory())
11
- .map((patchFile) => path.basename(patchFile, ".js"))
12
- .sort();
9
+ function availablePatchVersions() {
10
+ return fs
11
+ .readdirSync(getPlaywrightPatchesDirectory())
12
+ .map((patchFile) => path.basename(patchFile, ".js"))
13
+ .sort();
14
+ }
13
15
 
14
- function findSuitablePatchVersion(version) {
15
- let suitablePatchVersion = availablePatchVersions[0];
16
- for (const patchVersion of availablePatchVersions) {
16
+ function findSuitablePatchVersion(
17
+ version,
18
+ available = availablePatchVersions()
19
+ ) {
20
+ let suitablePatchVersion = available[0];
21
+ for (const patchVersion of available) {
17
22
  if (version >= patchVersion) {
18
23
  suitablePatchVersion = patchVersion;
19
24
  } else {
@@ -23,26 +28,38 @@ function findSuitablePatchVersion(version) {
23
28
  return suitablePatchVersion;
24
29
  }
25
30
 
26
- const projectRootFromEnv = process.env.PROJECT_ROOT;
27
- const projectPaths = projectRootFromEnv
28
- ? [projectRootFromEnv]
29
- : [".", "../", "../.."].map((project) => join(process.cwd(), project));
31
+ function patchAll() {
32
+ const projectRootFromEnv = process.env.PROJECT_ROOT;
33
+ const projectPaths = projectRootFromEnv
34
+ ? [projectRootFromEnv]
35
+ : [".", "../", "../.."].map((project) => join(process.cwd(), project));
30
36
 
31
- for (const projectPath of projectPaths) {
32
- try {
33
- if (fs.existsSync(join(projectPath, "node_modules", "playwright"))) {
34
- const absolutePathToPackageJSON = resolve(
35
- join(projectPath, "node_modules", "playwright", "package.json")
36
- );
37
- const packageJSON = require(absolutePathToPackageJSON);
38
- const playwrightVersion = packageJSON.version;
39
- const patchVersion = findSuitablePatchVersion(playwrightVersion);
40
- const patchPath = resolve(
41
- join(getPlaywrightPatchesDirectory(), `${patchVersion}.js`)
42
- );
43
- require(patchPath)(projectPath);
37
+ for (const projectPath of projectPaths) {
38
+ try {
39
+ if (fs.existsSync(join(projectPath, "node_modules", "playwright"))) {
40
+ const absolutePathToPackageJSON = resolve(
41
+ join(projectPath, "node_modules", "playwright", "package.json")
42
+ );
43
+ const packageJSON = require(absolutePathToPackageJSON);
44
+ const playwrightVersion = packageJSON.version;
45
+ const patchVersion = findSuitablePatchVersion(playwrightVersion);
46
+ const patchPath = resolve(
47
+ join(getPlaywrightPatchesDirectory(), `${patchVersion}.js`)
48
+ );
49
+ require(patchPath)(projectPath);
50
+ }
51
+ } catch (e) {
52
+ // Patching is best-effort: a failure here must not block the install.
44
53
  }
45
- } catch (e) {
46
- // Patching is best-effort: a failure here must not block the install.
47
54
  }
48
55
  }
56
+
57
+ // Guarded like playwright_pins.js: the runtime always spawns this as
58
+ // `node patch.js`, so requiring it must not patch anything as a side effect —
59
+ // that is what lets playwright_patches.spec.ts test the real selection logic
60
+ // instead of keeping a copy of it in sync by hand.
61
+ if (require.main === module) {
62
+ patchAll();
63
+ }
64
+
65
+ module.exports = { availablePatchVersions, findSuitablePatchVersion };
@@ -525,6 +525,39 @@ function htmlReporter(projectRoot) {
525
525
  replaceContent(file, originalContent, newContent);
526
526
  }
527
527
 
528
+ // -------- [Shutdown safety] -------- //
529
+
530
+ // Playwright leaves two teardown fixtures at `timeout: 0`, and TimeoutManager
531
+ // reads that as `deadline = kMaxDeadline` — unbounded, not "use the default".
532
+ // Every other phase is capped at the project timeout, so these are the only two
533
+ // ways a worker can stop reporting for good: an unreaped browser leaves
534
+ // `browser.close()` awaiting a `close` event that never arrives, which is how
535
+ // run cc44a300 went silent for 10h42m until the k8s job deadline killed the pod.
536
+ // Playwright's own 5-minute worker watchdog cannot catch it — heartbeats run on
537
+ // an interval independent of any pending await, so a wedged worker keeps
538
+ // heartbeating and the deadline slides forever.
539
+ //
540
+ // `> 0 ? : ` rather than `||`: a zero, negative or unparseable override must
541
+ // fall back to the default, never restore the unbounded wait.
542
+ function boundUnboundedFixtures(projectRoot) {
543
+ const file = join(projectRoot, "node_modules/playwright/lib/index.js");
544
+ if (!doesFileExist(file)) {
545
+ return;
546
+ }
547
+
548
+ let originalContent, newContent;
549
+
550
+ // ArtifactsRecorder.didFinishTest(): screenshot, trace stop, aria snapshot.
551
+ originalContent = `}, { auto: "all-hooks-included", title: "trace recording", box: true, timeout: 0 }],`;
552
+ newContent = `}, { auto: "all-hooks-included", title: "trace recording", box: true, timeout: Number(process.env["CHECKSUM_PW_ARTIFACTS_TIMEOUT_MS"]) > 0 ? Number(process.env["CHECKSUM_PW_ARTIFACTS_TIMEOUT_MS"]) : 120000 }],`;
553
+ replaceContent(file, originalContent, newContent);
554
+
555
+ // The `browser` fixture: launch on setup, `browser.close()` on teardown.
556
+ originalContent = `}, { scope: "worker", timeout: 0 }],`;
557
+ newContent = `}, { scope: "worker", timeout: Number(process.env["CHECKSUM_PW_BROWSER_FIXTURE_TIMEOUT_MS"]) > 0 ? Number(process.env["CHECKSUM_PW_BROWSER_FIXTURE_TIMEOUT_MS"]) : 180000 }],`;
558
+ replaceContent(file, originalContent, newContent);
559
+ }
560
+
528
561
  // -------- [Run] -------- //
529
562
 
530
563
  const isRuntime = true || process.env.RUNTIME === "true";
@@ -549,6 +582,7 @@ function run(projectPath) {
549
582
  reportTraceFile(projectPath);
550
583
  }
551
584
  htmlReporter(projectPath);
585
+ boundUnboundedFixtures(projectPath);
552
586
  }
553
587
  } else {
554
588
  console.warn("Project path not found", projectPath);