@deeeed/metamask-harness 0.42.0 → 0.44.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/CHANGELOG.md +42 -0
- package/README.md +5 -0
- package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +363 -55
- package/adapters/mobile/bridge-runtime/console-forwarder.cjs +23 -10
- package/adapters/mobile/bridge-runtime/lib/bridge-errors.cjs +2 -0
- package/adapters/mobile/bridge-runtime/lib/cdp-broker.cjs +296 -25
- package/adapters/mobile/bridge-runtime/lib/config.cjs +14 -4
- package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +4 -2
- package/adapters/mobile/reload-app.mjs +99 -1
- package/adapters/mobile/start-console-forwarder.sh +5 -1
- package/dist/adapters/extension/browser-cdp.js +174 -0
- package/dist/adapters/extension/network-observer.js +19 -110
- package/dist/adapters/extension/performance-observer.js +75 -0
- package/dist/adapters/mobile/frame-metrics.js +45 -0
- package/dist/adapters/mobile/performance-observer.js +43 -0
- package/dist/adapters/mobile/prepare.js +12 -0
- package/dist/adapters/performance/cdp-trace.js +342 -0
- package/dist/adapters/performance/js-task-metrics.js +35 -0
- package/dist/adapters.js +39 -5
- package/dist/artifact-files.js +92 -0
- package/dist/async.js +19 -0
- package/dist/commands/call.js +63 -4
- package/dist/commands/run-engine.js +265 -139
- package/dist/commands/run-report.js +68 -0
- package/dist/commands/run.js +67 -3
- package/dist/execution-provenance.js +342 -0
- package/dist/network-observation.js +59 -47
- package/dist/performance-observation.js +465 -0
- package/dist/run-diagnostics.js +36 -11
- package/dist/runner.js +44 -13
- package/docs/PERFORMANCE-CAPTURE.md +33 -0
- package/docs/RECIPES.md +7 -0
- package/library/actions/mobile/perps/performance-capture.mjs +570 -189
- package/library/actions/mobile/perps/perps.mjs +122 -0
- package/library/actions/mobile/platform/bridge.mjs +43 -8
- package/library/actions/mobile/platform/native-session.mjs +1 -1
- package/library/actions/mobile/wallet/lock.mjs +1 -4
- package/library/actions/mobile/wallet/select_account.mjs +129 -17
- package/library/manifests/extension.action-manifest.json +85 -0
- package/library/manifests/mobile.action-manifest.json +125 -3
- package/library/recipes/mobile/perps/performance.recipe.json +73 -47
- package/package.json +1 -1
- package/scripts/site-contrast.mjs +43 -27
|
@@ -1219,6 +1219,103 @@
|
|
|
1219
1219
|
},
|
|
1220
1220
|
"execution_capabilities": []
|
|
1221
1221
|
},
|
|
1222
|
+
"app.performance_capture": {
|
|
1223
|
+
"description": "Capture bounded CDP native UI frame metrics and separately labelled JavaScript work across Recipe Protocol v1 node boundaries.",
|
|
1224
|
+
"examples": [
|
|
1225
|
+
{
|
|
1226
|
+
"action": "app.performance_capture",
|
|
1227
|
+
"phase": "start",
|
|
1228
|
+
"id": "homepage-scroll",
|
|
1229
|
+
"max_duration_ms": 300000,
|
|
1230
|
+
"intent": "Start the bounded UI smoothness window",
|
|
1231
|
+
"next": "exercise-flow"
|
|
1232
|
+
},
|
|
1233
|
+
{
|
|
1234
|
+
"action": "app.performance_capture",
|
|
1235
|
+
"phase": "end",
|
|
1236
|
+
"id": "homepage-scroll",
|
|
1237
|
+
"artifact_path": "performance/homepage-scroll.json",
|
|
1238
|
+
"html_path": "performance/homepage-scroll.html",
|
|
1239
|
+
"intent": "End and index the UI smoothness window",
|
|
1240
|
+
"next": "assert-performance"
|
|
1241
|
+
}
|
|
1242
|
+
],
|
|
1243
|
+
"schema": {
|
|
1244
|
+
"type": "object",
|
|
1245
|
+
"properties": {
|
|
1246
|
+
"phase": {
|
|
1247
|
+
"type": "string",
|
|
1248
|
+
"enum": ["start", "end"]
|
|
1249
|
+
},
|
|
1250
|
+
"id": {
|
|
1251
|
+
"type": "string"
|
|
1252
|
+
},
|
|
1253
|
+
"max_duration_ms": {
|
|
1254
|
+
"type": "integer",
|
|
1255
|
+
"minimum": 1,
|
|
1256
|
+
"description": "Maximum accepted at runtime: 3600000 ms."
|
|
1257
|
+
},
|
|
1258
|
+
"artifact_path": {
|
|
1259
|
+
"type": "string"
|
|
1260
|
+
},
|
|
1261
|
+
"html_path": {
|
|
1262
|
+
"type": "string"
|
|
1263
|
+
}
|
|
1264
|
+
},
|
|
1265
|
+
"required": ["phase", "id"],
|
|
1266
|
+
"additionalProperties": false
|
|
1267
|
+
},
|
|
1268
|
+
"execution_capabilities": ["host-read-export"]
|
|
1269
|
+
},
|
|
1270
|
+
"app.performance_assert": {
|
|
1271
|
+
"description": "Assert a previously indexed app.performance_capture summary without hiding unavailable or partial source coverage.",
|
|
1272
|
+
"examples": [
|
|
1273
|
+
{
|
|
1274
|
+
"action": "app.performance_assert",
|
|
1275
|
+
"id": "homepage-scroll",
|
|
1276
|
+
"artifact_path": "performance/homepage-scroll.json",
|
|
1277
|
+
"required_status": ["complete", "partial"],
|
|
1278
|
+
"minimum_frame_count": 1,
|
|
1279
|
+
"require_native_ui_when_supported": true,
|
|
1280
|
+
"required_node_ids": ["scroll-homepage"],
|
|
1281
|
+
"intent": "Assert the indexed UI smoothness summary",
|
|
1282
|
+
"next": "done"
|
|
1283
|
+
}
|
|
1284
|
+
],
|
|
1285
|
+
"schema": {
|
|
1286
|
+
"type": "object",
|
|
1287
|
+
"properties": {
|
|
1288
|
+
"id": {
|
|
1289
|
+
"type": "string"
|
|
1290
|
+
},
|
|
1291
|
+
"artifact_path": {
|
|
1292
|
+
"type": "string"
|
|
1293
|
+
},
|
|
1294
|
+
"required_status": {
|
|
1295
|
+
"type": "array",
|
|
1296
|
+
"items": {
|
|
1297
|
+
"type": "string",
|
|
1298
|
+
"enum": ["complete", "partial", "unavailable"]
|
|
1299
|
+
}
|
|
1300
|
+
},
|
|
1301
|
+
"minimum_frame_count": {
|
|
1302
|
+
"type": "integer",
|
|
1303
|
+
"minimum": 0,
|
|
1304
|
+
"description": "Minimum native UI frame count. JavaScript task count cannot satisfy this assertion."
|
|
1305
|
+
},
|
|
1306
|
+
"require_native_ui_when_supported": {
|
|
1307
|
+
"type": "boolean"
|
|
1308
|
+
},
|
|
1309
|
+
"required_node_ids": {
|
|
1310
|
+
"type": "array",
|
|
1311
|
+
"items": { "type": "string" }
|
|
1312
|
+
}
|
|
1313
|
+
},
|
|
1314
|
+
"required": ["id"],
|
|
1315
|
+
"additionalProperties": false
|
|
1316
|
+
},
|
|
1317
|
+
"execution_capabilities": ["host-read-export"]
|
|
1318
|
+
},
|
|
1222
1319
|
"app.status": {
|
|
1223
1320
|
"description": "Report the adapter's static status — platform, project root, resolved checkout shape, and headless compatibility mode (no live route or account).",
|
|
1224
1321
|
"examples": [
|
|
@@ -1521,7 +1618,7 @@
|
|
|
1521
1618
|
"execution_capabilities": ["app-mutation"]
|
|
1522
1619
|
},
|
|
1523
1620
|
"metamask.wallet.lock": {
|
|
1524
|
-
"description": "Lock the Mobile wallet through the visible menu and confirm the login screen.",
|
|
1621
|
+
"description": "Lock the Android Mobile wallet through the visible menu and confirm the login screen on development or opaque clients.",
|
|
1525
1622
|
"schema": {
|
|
1526
1623
|
"type": "object",
|
|
1527
1624
|
"properties": {
|
|
@@ -3018,6 +3115,9 @@
|
|
|
3018
3115
|
"wait_for_lifecycle": {
|
|
3019
3116
|
"type": "string"
|
|
3020
3117
|
},
|
|
3118
|
+
"wait_for_content_variant": {
|
|
3119
|
+
"type": "string"
|
|
3120
|
+
},
|
|
3021
3121
|
"wait_timeout_ms": {
|
|
3022
3122
|
"type": "number",
|
|
3023
3123
|
"minimum": 1
|
|
@@ -3047,7 +3147,7 @@
|
|
|
3047
3147
|
},
|
|
3048
3148
|
"required_live_streams": {
|
|
3049
3149
|
"type": "array",
|
|
3050
|
-
"description": "Require values_ready records
|
|
3150
|
+
"description": "Require app-emitted [PerpsLoadProof] values_ready records from fresh_socket with one complete session, lifecycle, account, context, and connection-generation identity.",
|
|
3051
3151
|
"items": {
|
|
3052
3152
|
"type": "string",
|
|
3053
3153
|
"enum": ["prices", "positions", "orders", "account"]
|
|
@@ -3116,7 +3216,7 @@
|
|
|
3116
3216
|
{
|
|
3117
3217
|
"action": "metamask.perps.capture_performance",
|
|
3118
3218
|
"phase": "end",
|
|
3119
|
-
"require_records":
|
|
3219
|
+
"require_records": true,
|
|
3120
3220
|
"required_live_streams": ["positions", "orders", "account"],
|
|
3121
3221
|
"intent": "Preserve production Perps loading and live-stream evidence emitted during this run",
|
|
3122
3222
|
"next": "done"
|
|
@@ -3193,6 +3293,28 @@
|
|
|
3193
3293
|
],
|
|
3194
3294
|
"execution_capabilities": ["app-mutation"]
|
|
3195
3295
|
},
|
|
3296
|
+
"metamask.perps.ensure_mode": {
|
|
3297
|
+
"description": "Ensure the viewport-visible Perps market-detail root is Lite or Pro. The action presses the active mode control at most once and fails closed if that press opens another chooser or does not converge directly.",
|
|
3298
|
+
"schema": {
|
|
3299
|
+
"type": "object",
|
|
3300
|
+
"properties": {
|
|
3301
|
+
"mode": {
|
|
3302
|
+
"type": "string",
|
|
3303
|
+
"enum": ["lite", "pro"]
|
|
3304
|
+
},
|
|
3305
|
+
"timeout_ms": { "type": "integer", "minimum": 1000, "default": 30000 }
|
|
3306
|
+
},
|
|
3307
|
+
"required": ["mode"],
|
|
3308
|
+
"additionalProperties": false
|
|
3309
|
+
},
|
|
3310
|
+
"examples": [{
|
|
3311
|
+
"action": "metamask.perps.ensure_mode",
|
|
3312
|
+
"mode": "pro",
|
|
3313
|
+
"intent": "Reach Pro through the visible market-detail mode control",
|
|
3314
|
+
"next": "done"
|
|
3315
|
+
}],
|
|
3316
|
+
"execution_capabilities": ["app-mutation"]
|
|
3317
|
+
},
|
|
3196
3318
|
"metamask.perps.start_state": {
|
|
3197
3319
|
"description": "mobile Default to testnet for Perps mutations; mainnet is read-only unless explicitly requested. Recommended configurable Perps start state that first restores the fixture-backed unlocked wallet, then composes reusable domain operations before the proof window.",
|
|
3198
3320
|
"schema": {
|
|
@@ -51,7 +51,6 @@
|
|
|
51
51
|
"type": "string",
|
|
52
52
|
"enum": [
|
|
53
53
|
"navigate_return",
|
|
54
|
-
"cold_disk_cache",
|
|
55
54
|
"cold_no_cache",
|
|
56
55
|
"background_short",
|
|
57
56
|
"background_reconnect",
|
|
@@ -168,25 +167,25 @@
|
|
|
168
167
|
"value": "{{params.content_variant}}",
|
|
169
168
|
"equals": "trending",
|
|
170
169
|
"cases": {
|
|
171
|
-
"match": "
|
|
170
|
+
"match": "assert-no-positions"
|
|
172
171
|
},
|
|
173
172
|
"default": "assert-position",
|
|
174
173
|
"intent": "Prepare either the existing position or an empty account before timing"
|
|
175
174
|
},
|
|
176
|
-
"
|
|
177
|
-
"action": "metamask.perps.
|
|
175
|
+
"assert-no-positions": {
|
|
176
|
+
"action": "metamask.perps.assert_positions",
|
|
178
177
|
"state": "none",
|
|
179
178
|
"mode": "all",
|
|
180
179
|
"timeout_ms": 120000,
|
|
181
|
-
"intent": "
|
|
182
|
-
"next": "
|
|
180
|
+
"intent": "Require an already-empty position account without changing financial state",
|
|
181
|
+
"next": "assert-no-orders"
|
|
183
182
|
},
|
|
184
|
-
"
|
|
185
|
-
"action": "metamask.perps.
|
|
183
|
+
"assert-no-orders": {
|
|
184
|
+
"action": "metamask.perps.assert_orders",
|
|
186
185
|
"state": "none",
|
|
187
186
|
"mode": "all",
|
|
188
187
|
"timeout_ms": 120000,
|
|
189
|
-
"intent": "
|
|
188
|
+
"intent": "Require an already-empty order account without changing financial state",
|
|
190
189
|
"next": "select-account-switch-preparation"
|
|
191
190
|
},
|
|
192
191
|
"assert-position": {
|
|
@@ -366,7 +365,7 @@
|
|
|
366
365
|
"cases": {
|
|
367
366
|
"match": "clear-perps-caches"
|
|
368
367
|
},
|
|
369
|
-
"default": "select-
|
|
368
|
+
"default": "select-background-short",
|
|
370
369
|
"intent": "Choose the cold no-cache transition only for its named lifecycle"
|
|
371
370
|
},
|
|
372
371
|
"clear-perps-caches": {
|
|
@@ -374,16 +373,6 @@
|
|
|
374
373
|
"intent": "Remove only persisted Perps market and user snapshots while preserving the wallet",
|
|
375
374
|
"next": "restart-app"
|
|
376
375
|
},
|
|
377
|
-
"select-cold-disk-cache": {
|
|
378
|
-
"action": "switch",
|
|
379
|
-
"value": "{{params.lifecycle}}",
|
|
380
|
-
"equals": "cold_disk_cache",
|
|
381
|
-
"cases": {
|
|
382
|
-
"match": "restart-app"
|
|
383
|
-
},
|
|
384
|
-
"default": "select-background-short",
|
|
385
|
-
"intent": "Choose the returning cold-process transition only for its named lifecycle"
|
|
386
|
-
},
|
|
387
376
|
"restart-app": {
|
|
388
377
|
"action": "app.lifecycle",
|
|
389
378
|
"command": "restart",
|
|
@@ -414,6 +403,7 @@
|
|
|
414
403
|
"background-short": {
|
|
415
404
|
"action": "app.lifecycle",
|
|
416
405
|
"command": "background",
|
|
406
|
+
"require_process_continuity": true,
|
|
417
407
|
"settle_ms": 3000,
|
|
418
408
|
"intent": "Keep the process inactive for less than the Perps connection grace period",
|
|
419
409
|
"next": "foreground-app"
|
|
@@ -431,6 +421,7 @@
|
|
|
431
421
|
"background-reconnect": {
|
|
432
422
|
"action": "app.lifecycle",
|
|
433
423
|
"command": "background",
|
|
424
|
+
"require_process_continuity": true,
|
|
434
425
|
"settle_ms": 25000,
|
|
435
426
|
"intent": "Keep the process inactive beyond the Perps connection grace period",
|
|
436
427
|
"next": "foreground-app"
|
|
@@ -438,6 +429,7 @@
|
|
|
438
429
|
"foreground-app": {
|
|
439
430
|
"action": "app.lifecycle",
|
|
440
431
|
"command": "foreground",
|
|
432
|
+
"require_process_continuity": true,
|
|
441
433
|
"settle_ms": 0,
|
|
442
434
|
"runtime_ready_timeout_ms": 90000,
|
|
443
435
|
"timeout_ms": 120000,
|
|
@@ -568,18 +560,8 @@
|
|
|
568
560
|
"cases": {
|
|
569
561
|
"match": "finish-cold-no-cache-performance-capture"
|
|
570
562
|
},
|
|
571
|
-
"default": "select-cold-disk-cache-finish",
|
|
572
|
-
"intent": "Accept fresh data preloaded after process start but before the Homepage viewport demand only for cold no-cache"
|
|
573
|
-
},
|
|
574
|
-
"select-cold-disk-cache-finish": {
|
|
575
|
-
"action": "switch",
|
|
576
|
-
"value": "{{params.lifecycle}}",
|
|
577
|
-
"equals": "cold_disk_cache",
|
|
578
|
-
"cases": {
|
|
579
|
-
"match": "finish-cold-disk-cache-performance-capture"
|
|
580
|
-
},
|
|
581
563
|
"default": "select-background-short-finish",
|
|
582
|
-
"intent": "
|
|
564
|
+
"intent": "Accept fresh data preloaded after process start but before the Homepage viewport demand only for cold no-cache"
|
|
583
565
|
},
|
|
584
566
|
"select-background-short-finish": {
|
|
585
567
|
"action": "switch",
|
|
@@ -614,43 +596,69 @@
|
|
|
614
596
|
"finish-resident-performance-capture": {
|
|
615
597
|
"action": "metamask.perps.capture_performance",
|
|
616
598
|
"phase": "end",
|
|
617
|
-
"require_records":
|
|
599
|
+
"require_records": true,
|
|
600
|
+
"required_stages": [
|
|
601
|
+
"surface_demand",
|
|
602
|
+
"surface_initial_ui_recorded",
|
|
603
|
+
"surface_resolved_recorded"
|
|
604
|
+
],
|
|
605
|
+
"wait_for_stage": "surface_resolved_recorded",
|
|
606
|
+
"wait_for_lifecycle": "{{params.lifecycle}}",
|
|
607
|
+
"wait_for_content_variant": "{{params.content_variant}}",
|
|
618
608
|
"artifact_prefix": "logs/perps-{{params.content_variant}}-{{params.lifecycle}}-{{params.sample_id}}",
|
|
609
|
+
"wait_timeout_ms": 90000,
|
|
619
610
|
"intent": "Preserve the warm resident capture after native visible-content proof",
|
|
620
611
|
"next": "capture-position"
|
|
621
612
|
},
|
|
622
613
|
"finish-cold-no-cache-performance-capture": {
|
|
623
614
|
"action": "metamask.perps.capture_performance",
|
|
624
615
|
"phase": "end",
|
|
625
|
-
"require_records":
|
|
616
|
+
"require_records": true,
|
|
617
|
+
"required_stages": [
|
|
618
|
+
"surface_demand",
|
|
619
|
+
"surface_initial_ui_recorded",
|
|
620
|
+
"surface_resolved_recorded",
|
|
621
|
+
"surface_live_recorded"
|
|
622
|
+
],
|
|
623
|
+
"wait_for_stage": "surface_live_recorded",
|
|
624
|
+
"wait_for_lifecycle": "{{params.lifecycle}}",
|
|
625
|
+
"wait_for_content_variant": "{{params.content_variant}}",
|
|
626
626
|
"required_live_streams": ["positions", "orders", "account"],
|
|
627
627
|
"artifact_prefix": "logs/perps-{{params.content_variant}}-{{params.lifecycle}}-{{params.sample_id}}",
|
|
628
628
|
"wait_timeout_ms": 90000,
|
|
629
629
|
"intent": "Require complete live account streams after native cold visible-content proof; live prices remain an independent optional milestone",
|
|
630
630
|
"next": "capture-position"
|
|
631
631
|
},
|
|
632
|
-
"finish-cold-disk-cache-performance-capture": {
|
|
633
|
-
"action": "metamask.perps.capture_performance",
|
|
634
|
-
"phase": "end",
|
|
635
|
-
"require_records": false,
|
|
636
|
-
"required_live_streams": ["positions", "orders", "account"],
|
|
637
|
-
"artifact_prefix": "logs/perps-{{params.content_variant}}-{{params.lifecycle}}-{{params.sample_id}}",
|
|
638
|
-
"wait_timeout_ms": 90000,
|
|
639
|
-
"intent": "Require complete live account takeover after native cached-content proof",
|
|
640
|
-
"next": "capture-position"
|
|
641
|
-
},
|
|
642
632
|
"finish-background-short-performance-capture": {
|
|
643
633
|
"action": "metamask.perps.capture_performance",
|
|
644
634
|
"phase": "end",
|
|
645
|
-
"require_records":
|
|
635
|
+
"require_records": true,
|
|
636
|
+
"required_stages": [
|
|
637
|
+
"surface_demand",
|
|
638
|
+
"surface_initial_ui_recorded",
|
|
639
|
+
"surface_resolved_recorded"
|
|
640
|
+
],
|
|
641
|
+
"wait_for_stage": "surface_resolved_recorded",
|
|
642
|
+
"wait_for_lifecycle": "{{params.lifecycle}}",
|
|
643
|
+
"wait_for_content_variant": "{{params.content_variant}}",
|
|
646
644
|
"artifact_prefix": "logs/perps-{{params.content_variant}}-{{params.lifecycle}}-{{params.sample_id}}",
|
|
645
|
+
"wait_timeout_ms": 90000,
|
|
647
646
|
"intent": "Preserve the short-resume capture after native resident-content proof; a new socket frame is not required while continuity is retained",
|
|
648
647
|
"next": "capture-position"
|
|
649
648
|
},
|
|
650
649
|
"finish-account-switch-performance-capture": {
|
|
651
650
|
"action": "metamask.perps.capture_performance",
|
|
652
651
|
"phase": "end",
|
|
653
|
-
"require_records":
|
|
652
|
+
"require_records": true,
|
|
653
|
+
"required_stages": [
|
|
654
|
+
"surface_demand",
|
|
655
|
+
"surface_initial_ui_recorded",
|
|
656
|
+
"surface_resolved_recorded",
|
|
657
|
+
"surface_live_recorded"
|
|
658
|
+
],
|
|
659
|
+
"wait_for_stage": "surface_live_recorded",
|
|
660
|
+
"wait_for_lifecycle": "{{params.lifecycle}}",
|
|
661
|
+
"wait_for_content_variant": "{{params.content_variant}}",
|
|
654
662
|
"required_live_streams": ["positions", "orders", "account"],
|
|
655
663
|
"artifact_prefix": "logs/perps-{{params.content_variant}}-{{params.lifecycle}}-{{params.sample_id}}",
|
|
656
664
|
"wait_timeout_ms": 90000,
|
|
@@ -660,7 +668,16 @@
|
|
|
660
668
|
"finish-account-switch-empty-performance-capture": {
|
|
661
669
|
"action": "metamask.perps.capture_performance",
|
|
662
670
|
"phase": "end",
|
|
663
|
-
"require_records":
|
|
671
|
+
"require_records": true,
|
|
672
|
+
"required_stages": [
|
|
673
|
+
"surface_demand",
|
|
674
|
+
"surface_initial_ui_recorded",
|
|
675
|
+
"surface_resolved_recorded",
|
|
676
|
+
"surface_live_recorded"
|
|
677
|
+
],
|
|
678
|
+
"wait_for_stage": "surface_live_recorded",
|
|
679
|
+
"wait_for_lifecycle": "{{params.lifecycle}}",
|
|
680
|
+
"wait_for_content_variant": "{{params.content_variant}}",
|
|
664
681
|
"required_live_streams": ["positions", "orders", "account"],
|
|
665
682
|
"artifact_prefix": "logs/perps-{{params.content_variant}}-{{params.lifecycle}}-{{params.sample_id}}",
|
|
666
683
|
"wait_timeout_ms": 90000,
|
|
@@ -670,7 +687,16 @@
|
|
|
670
687
|
"finish-performance-capture": {
|
|
671
688
|
"action": "metamask.perps.capture_performance",
|
|
672
689
|
"phase": "end",
|
|
673
|
-
"require_records":
|
|
690
|
+
"require_records": true,
|
|
691
|
+
"required_stages": [
|
|
692
|
+
"surface_demand",
|
|
693
|
+
"surface_initial_ui_recorded",
|
|
694
|
+
"surface_resolved_recorded",
|
|
695
|
+
"surface_live_recorded"
|
|
696
|
+
],
|
|
697
|
+
"wait_for_stage": "surface_live_recorded",
|
|
698
|
+
"wait_for_lifecycle": "{{params.lifecycle}}",
|
|
699
|
+
"wait_for_content_variant": "{{params.content_variant}}",
|
|
674
700
|
"required_live_streams": ["positions", "orders", "account"],
|
|
675
701
|
"artifact_prefix": "logs/perps-{{params.content_variant}}-{{params.lifecycle}}-{{params.sample_id}}",
|
|
676
702
|
"wait_timeout_ms": 90000,
|
package/package.json
CHANGED
|
@@ -272,6 +272,18 @@ function findChrome() {
|
|
|
272
272
|
}
|
|
273
273
|
|
|
274
274
|
async function startChrome() {
|
|
275
|
+
let lastError;
|
|
276
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
277
|
+
try {
|
|
278
|
+
return await startChromeOnce();
|
|
279
|
+
} catch (error) {
|
|
280
|
+
lastError = error;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
throw lastError;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async function startChromeOnce() {
|
|
275
287
|
const binary = findChrome();
|
|
276
288
|
const userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'site-contrast-'));
|
|
277
289
|
const child = spawn(
|
|
@@ -279,6 +291,7 @@ async function startChrome() {
|
|
|
279
291
|
[
|
|
280
292
|
'--headless=new',
|
|
281
293
|
'--disable-gpu',
|
|
294
|
+
'--disable-dev-shm-usage',
|
|
282
295
|
'--no-first-run',
|
|
283
296
|
'--no-default-browser-check',
|
|
284
297
|
'--remote-debugging-port=0',
|
|
@@ -290,35 +303,38 @@ async function startChrome() {
|
|
|
290
303
|
|
|
291
304
|
// Chrome writes the chosen port to DevToolsActivePort once it is listening.
|
|
292
305
|
const portFile = path.join(userDataDir, 'DevToolsActivePort');
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
close: async () => {
|
|
304
|
-
child.kill();
|
|
305
|
-
// Chrome holds the profile dir open until it exits; removing it early
|
|
306
|
-
// fails with ENOTEMPTY. Wait for exit, and never fail the gate on cleanup.
|
|
307
|
-
await new Promise((done) => {
|
|
308
|
-
if (child.exitCode !== null || child.signalCode !== null) return done();
|
|
309
|
-
const timer = setTimeout(done, 5000);
|
|
310
|
-
child.once('exit', () => {
|
|
311
|
-
clearTimeout(timer);
|
|
312
|
-
done();
|
|
313
|
-
});
|
|
306
|
+
const close = async () => {
|
|
307
|
+
child.kill();
|
|
308
|
+
// Chrome holds the profile dir open until it exits; removing it early
|
|
309
|
+
// fails with ENOTEMPTY. Wait for exit, and never fail the gate on cleanup.
|
|
310
|
+
await new Promise((done) => {
|
|
311
|
+
if (child.exitCode !== null || child.signalCode !== null) return done();
|
|
312
|
+
const timer = setTimeout(done, 5000);
|
|
313
|
+
child.once('exit', () => {
|
|
314
|
+
clearTimeout(timer);
|
|
315
|
+
done();
|
|
314
316
|
});
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
}
|
|
317
|
+
});
|
|
318
|
+
try {
|
|
319
|
+
fs.rmSync(userDataDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 200 });
|
|
320
|
+
} catch {
|
|
321
|
+
/* a leftover temp profile is not worth failing a contrast run over */
|
|
322
|
+
}
|
|
321
323
|
};
|
|
324
|
+
|
|
325
|
+
try {
|
|
326
|
+
const port = await waitFor(() => {
|
|
327
|
+
if (!fs.existsSync(portFile)) return null;
|
|
328
|
+
const line = fs.readFileSync(portFile, 'utf8').split('\n')[0].trim();
|
|
329
|
+
return line ? Number(line) : null;
|
|
330
|
+
}, 15000, 'Chrome did not report a debugging port');
|
|
331
|
+
|
|
332
|
+
const version = await getJson(`http://127.0.0.1:${port}/json/version`);
|
|
333
|
+
return { endpoint: version.webSocketDebuggerUrl, port, close };
|
|
334
|
+
} catch (error) {
|
|
335
|
+
await close();
|
|
336
|
+
throw error;
|
|
337
|
+
}
|
|
322
338
|
}
|
|
323
339
|
|
|
324
340
|
async function auditPage(browserEndpoint, url) {
|