@ai-ecoverse/slicc-browsers 1.7.11 → 1.7.12
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/README.md +2 -0
- package/guard-runner.mjs +34 -7
- package/guard-vectors.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -287,6 +287,8 @@ every alternative of an `any`, or a required (`all`, `not` or plain) check, need
|
|
|
287
287
|
selects one variant or an array; omission selects all. `parallel` defaults to 1.
|
|
288
288
|
With the `browserContexts` capability each leg runs in its own browser context. A `Target.createBrowserContext` that fails with a timeout is retried once, and the row's `notes` records it; any other failure fails the row.
|
|
289
289
|
|
|
290
|
+
`settleScale` (default 1) multiplies every bound the runner waits on: settles (beacon, served, title and load), `paused`, page and child loads, the wait for a background step's `as` result (`<as> did not complete within <ms>ms`), and the wait for an observation. Adopters on slow hosts can set 3 or 4. Every wait polls and returns as soon as its event arrives, so a larger bound only costs time when something is really missing, and each timeout error names the bound it used. An observation read from the fixture (`observer: "fixture"`, such as a beacon or a served request) that the leg expects to be `true` is polled for up to 5000 ms (scaled) instead of read once. One that the leg expects to be `false` is still read once.
|
|
291
|
+
|
|
290
292
|
`onStart(row)` receives each row's `id`, `target`, `variant`, `transport` and `via` as the row starts. `run()` returns a promise that also has `inFlight()`, and its result has `inFlight` too. `inFlight()` lists the rows that are running now, each as `{ id, target, variant, leg, phase, step, clients }`, where `step` is the index of the current step in its `phase` (`setup`, `trigger` or `child`), and `clients` lists each client's pending CDP calls as `{ method, session, ageMs }` (`client`, plus `unguarded` when it is a separate client). With `rowTimeoutMs`, a row still running after that many milliseconds fails on its own. Its `errors` hold `row timed out after <ms>ms: <the in-flight view>`, `inFlight` holds the view, and the clients that `clientFactory` made for it are closed. The run then goes on to the next row. It is off by default, and a client passed as `client` is never closed.
|
|
291
293
|
|
|
292
294
|
`log(row)` receives completed rows. Besides `control`, `guarded`, `live`, `why` and `errors`, a row may carry `ran` (steps that ran on one leg only) and `notes` (settles skipped because their registration was refused). Both are informational: they explain a row's verdict, and adopters (bios, slicc-cli, slicc-cdp) must not count them as failures, or as passes. The verdict is `control`, `guarded` and `live`. A variant supplies the wait before an
|
package/guard-runner.mjs
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
|
|
14
14
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
15
15
|
const namedResultMs = 10000;
|
|
16
|
+
const observeMs = 5000;
|
|
16
17
|
const q = JSON.stringify;
|
|
17
18
|
const at = (value, path = '') =>
|
|
18
19
|
path
|
|
@@ -115,6 +116,7 @@ class Execution {
|
|
|
115
116
|
this.expectWhere = [...(vector.setup ?? []), ...(vector.trigger ?? [])].some(
|
|
116
117
|
(step) => step.children && step.where
|
|
117
118
|
);
|
|
119
|
+
this.scale = options.settleScale ?? 1;
|
|
118
120
|
this.childRuns = new Map();
|
|
119
121
|
this.named = new Map();
|
|
120
122
|
this.processed = new Set();
|
|
@@ -167,8 +169,12 @@ class Execution {
|
|
|
167
169
|
return out;
|
|
168
170
|
}
|
|
169
171
|
|
|
172
|
+
bound(ms) {
|
|
173
|
+
return Math.round(ms * this.scale);
|
|
174
|
+
}
|
|
175
|
+
|
|
170
176
|
async poll(predicate, ms = 1500) {
|
|
171
|
-
const deadline = Date.now() + ms;
|
|
177
|
+
const deadline = Date.now() + this.bound(ms);
|
|
172
178
|
do {
|
|
173
179
|
if (await predicate()) return true;
|
|
174
180
|
await sleep(25);
|
|
@@ -513,7 +519,7 @@ class Execution {
|
|
|
513
519
|
this.rule(event)?.url === url
|
|
514
520
|
);
|
|
515
521
|
if (!(await this.poll(seen, ms)) && this.side === 'control')
|
|
516
|
-
this.errors.push(`paused: no pause for ${url} within ${ms}ms`);
|
|
522
|
+
this.errors.push(`paused: no pause for ${url} within ${this.bound(ms)}ms`);
|
|
517
523
|
}
|
|
518
524
|
|
|
519
525
|
async answer(event) {
|
|
@@ -651,7 +657,7 @@ class Execution {
|
|
|
651
657
|
if (refused() && !alt) return skipped();
|
|
652
658
|
if (settle.variant) await sleep(this.delay);
|
|
653
659
|
if (settle.title && !this.reader) {
|
|
654
|
-
await sleep(settle.ms ?? 1500);
|
|
660
|
+
await sleep(this.bound(settle.ms ?? 1500));
|
|
655
661
|
return;
|
|
656
662
|
}
|
|
657
663
|
const met = await this.poll(async () => {
|
|
@@ -677,7 +683,7 @@ class Execution {
|
|
|
677
683
|
if (refused() && !alt) return skipped();
|
|
678
684
|
if (settle.required && !met)
|
|
679
685
|
this.errors.push(
|
|
680
|
-
`settle: no ${settle.beacon ? `beacon from ${settle.beacon}` : settle.served ? `request for ${settle.served}` : settle.title ? `title ${settle.title}` : 'load'} within ${settle.ms ?? 1500}ms`
|
|
686
|
+
`settle: no ${settle.beacon ? `beacon from ${settle.beacon}` : settle.served ? `request for ${settle.served}` : settle.title ? `title ${settle.title}` : 'load'} within ${this.bound(settle.ms ?? 1500)}ms`
|
|
681
687
|
);
|
|
682
688
|
}
|
|
683
689
|
|
|
@@ -795,7 +801,10 @@ class Execution {
|
|
|
795
801
|
if (!waits.length) return true;
|
|
796
802
|
let timer;
|
|
797
803
|
const expired = new Promise((resolve) => {
|
|
798
|
-
timer = setTimeout(
|
|
804
|
+
timer = setTimeout(
|
|
805
|
+
() => resolve(false),
|
|
806
|
+
this.bound(this.options.namedResultMs ?? namedResultMs)
|
|
807
|
+
);
|
|
799
808
|
});
|
|
800
809
|
const done = await Promise.race([Promise.all(waits).then(() => true), expired]);
|
|
801
810
|
clearTimeout(timer);
|
|
@@ -814,11 +823,24 @@ class Execution {
|
|
|
814
823
|
.filter((name) => typeof name === 'string' && this.named.has(name));
|
|
815
824
|
if (!names.length || (await this.namedSettled(names))) return true;
|
|
816
825
|
this.errors.push(
|
|
817
|
-
`${names.join(', ')} did not complete within ${this.options.namedResultMs ?? namedResultMs}ms`
|
|
826
|
+
`${names.join(', ')} did not complete within ${this.bound(this.options.namedResultMs ?? namedResultMs)}ms`
|
|
818
827
|
);
|
|
819
828
|
return false;
|
|
820
829
|
}
|
|
821
830
|
|
|
831
|
+
async observe(name, check) {
|
|
832
|
+
const want = (this.side === 'control' ? this.vector.control.expected : this.vector.expected)?.[
|
|
833
|
+
name
|
|
834
|
+
];
|
|
835
|
+
if (want !== true || check?.observer !== 'fixture') return this.check(check);
|
|
836
|
+
let seen = false;
|
|
837
|
+
await this.poll(async () => {
|
|
838
|
+
seen = await this.check(check);
|
|
839
|
+
return seen === true;
|
|
840
|
+
}, observeMs);
|
|
841
|
+
return seen;
|
|
842
|
+
}
|
|
843
|
+
|
|
822
844
|
async check(check) {
|
|
823
845
|
const c = expand(check, this.vars);
|
|
824
846
|
if (!(await this.ready(c))) return false;
|
|
@@ -1022,7 +1044,7 @@ class Execution {
|
|
|
1022
1044
|
for (const [name, observation] of Object.entries(this.vector.observe ?? {})) {
|
|
1023
1045
|
if (!supported(observation.requires, this.options.capabilities)) continue;
|
|
1024
1046
|
if ((observation.dependsOn ?? []).some((dep) => this.refused[dep])) continue;
|
|
1025
|
-
observed[name] = await this.
|
|
1047
|
+
observed[name] = await this.observe(name, observation.check);
|
|
1026
1048
|
if (observation.eitherWhen && this.side === 'guarded')
|
|
1027
1049
|
observed[`${name}.either`] = await this.check(observation.eitherWhen);
|
|
1028
1050
|
}
|
|
@@ -1391,6 +1413,11 @@ function validateBlocked(vectors) {
|
|
|
1391
1413
|
|
|
1392
1414
|
function validate(set, opts) {
|
|
1393
1415
|
if (!opts.fixture?.log) throw new TypeError('fixture.log is required');
|
|
1416
|
+
if (
|
|
1417
|
+
opts.settleScale !== undefined &&
|
|
1418
|
+
!(Number.isFinite(opts.settleScale) && opts.settleScale > 0)
|
|
1419
|
+
)
|
|
1420
|
+
throw new TypeError('settleScale must be a positive number');
|
|
1394
1421
|
for (const vector of set.vectors)
|
|
1395
1422
|
for (const [name, observation] of Object.entries(vector.observe ?? {}))
|
|
1396
1423
|
if (!observation.check) throw new TypeError(`${vector.id}: ${name} lacks a check`);
|
package/guard-vectors.json
CHANGED