@percy/sdk-utils 1.32.10 → 1.32.11-beta.1
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/dist/bundle.js +77 -1
- package/dist/index.js +6 -0
- package/dist/serialize-dom.js +76 -1
- package/package.json +3 -3
package/dist/bundle.js
CHANGED
|
@@ -472,6 +472,62 @@
|
|
|
472
472
|
// options,
|
|
473
473
|
// { callback: true, log }
|
|
474
474
|
// );
|
|
475
|
+
// Effective in-page timeout for each readiness preset, mirroring PRESETS in
|
|
476
|
+
// @percy/dom's readiness.js. Duplicated here because the presets live in the
|
|
477
|
+
// browser bundle, and the Node side needs the number to size its own deadline.
|
|
478
|
+
const PRESET_TIMEOUT_MS = {
|
|
479
|
+
balanced: 10000,
|
|
480
|
+
strict: 30000,
|
|
481
|
+
fast: 5000
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
// Added to the in-page timeout before the Node side gives up, so a gate that is
|
|
485
|
+
// merely slow (eval round-trip, check teardown) is never pre-empted.
|
|
486
|
+
const READINESS_DEADLINE_GRACE_MS = 3000;
|
|
487
|
+
|
|
488
|
+
// Wall-clock budget the Node side gives the readiness eval.
|
|
489
|
+
//
|
|
490
|
+
// PercyDOM.waitForReady() bounds itself with an in-page `setTimeout`, and every
|
|
491
|
+
// individual check settles on a `setTimeout`/`setInterval`. On a page whose
|
|
492
|
+
// timers are faked and paused -- Playwright's `page.clock.pauseAt()`, sinon
|
|
493
|
+
// `useFakeTimers`, jest fake timers -- neither the checks nor the gate's own
|
|
494
|
+
// timeout can ever fire, so the eval stays pending for the life of the page and
|
|
495
|
+
// hangs the test that called percySnapshot(). Node is the only side of that
|
|
496
|
+
// boundary guaranteed to have a real clock, so the backstop belongs here.
|
|
497
|
+
function readinessDeadlineMs(readinessConfig = {}) {
|
|
498
|
+
let timeout = readinessConfig.timeoutMs ?? readinessConfig.timeout_ms ?? PRESET_TIMEOUT_MS[readinessConfig.preset] ?? PRESET_TIMEOUT_MS.balanced;
|
|
499
|
+
let max = readinessConfig.maxTimeoutMs ?? readinessConfig.max_timeout_ms;
|
|
500
|
+
if (max != null) timeout = Math.min(timeout, max);
|
|
501
|
+
return timeout + READINESS_DEADLINE_GRACE_MS;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Captured once at module load, because the deadline below must not be the very
|
|
505
|
+
// thing a faked clock disables. jest's and sinon's fake timers replace the
|
|
506
|
+
// *global* `setTimeout` binding, and a bare `setTimeout(...)` call resolves that
|
|
507
|
+
// global at call time -- so a consumer whose Node test process has fake timers
|
|
508
|
+
// installed would schedule the deadline on a frozen clock and hang exactly as
|
|
509
|
+
// before, one layer up from the in-page freeze this gate exists to survive.
|
|
510
|
+
//
|
|
511
|
+
// The capture holds for the ordinary case: the SDK imports this module at
|
|
512
|
+
// require time, before a test body or beforeEach reaches `useFakeTimers()`. It
|
|
513
|
+
// is NOT a guarantee. Fake timers installed before this module is first
|
|
514
|
+
// evaluated -- jest's `fakeTimers: { enableGlobally: true }`, a `useFakeTimers()`
|
|
515
|
+
// call in `setupFiles`, or `resetModules()` + a fresh require under an already
|
|
516
|
+
// faked clock -- capture the fake, and the hang returns. Those consumers need
|
|
517
|
+
// `snapshot.readiness.preset: disabled`.
|
|
518
|
+
//
|
|
519
|
+
// Deliberately not `import { setTimeout } from 'node:timers'`, which would be
|
|
520
|
+
// immune to import order too: this file is imported statically by index.js, and
|
|
521
|
+
// index.js is the rollup entry for the browser bundle (see the package's
|
|
522
|
+
// `browser` field). Node-only code in this package is always reached through a
|
|
523
|
+
// lazy `await ({})` -- `http`/`https` in request.js, `./proxy.js` and its
|
|
524
|
+
// `net`/`tls` imports -- precisely to keep builtins out of that graph. A static
|
|
525
|
+
// builtin import here would not even fail the build, since the rollup config
|
|
526
|
+
// suppresses MISSING_NODE_BUILTINS; it would ship a browser bundle that breaks
|
|
527
|
+
// at runtime, which is worse.
|
|
528
|
+
const nativeSetTimeout = globalThis.setTimeout;
|
|
529
|
+
const nativeClearTimeout = globalThis.clearTimeout;
|
|
530
|
+
const READINESS_DEADLINE_HIT = Symbol('readiness-deadline');
|
|
475
531
|
async function runReadinessGate(evalScript, snapshotOptions = {}, {
|
|
476
532
|
callback = false,
|
|
477
533
|
log
|
|
@@ -481,13 +537,31 @@
|
|
|
481
537
|
const script = waitForReadyScript(config, {
|
|
482
538
|
callback
|
|
483
539
|
});
|
|
540
|
+
const deadline = readinessDeadlineMs(config);
|
|
541
|
+
let timer;
|
|
484
542
|
try {
|
|
485
|
-
|
|
543
|
+
const evaluation = Promise.resolve(evalScript(script));
|
|
544
|
+
// Hitting the deadline abandons `evaluation`, which may still reject much
|
|
545
|
+
// later -- when the driver tears the page down at end of test, say. Swallow
|
|
546
|
+
// that here so it never surfaces as an unhandled rejection.
|
|
547
|
+
evaluation.catch(() => {});
|
|
548
|
+
const result = await Promise.race([evaluation, new Promise(resolve => {
|
|
549
|
+
timer = nativeSetTimeout(() => resolve(READINESS_DEADLINE_HIT), deadline);
|
|
550
|
+
})]);
|
|
551
|
+
if (result === READINESS_DEADLINE_HIT) {
|
|
552
|
+
if (log && typeof log.debug === 'function') {
|
|
553
|
+
log.debug(`waitForReady did not settle within ${deadline}ms, proceeding to serialize. ` + 'If this page fakes or pauses timers (e.g. Playwright page.clock), disable ' + 'the readiness gate with snapshot.readiness.preset: disabled.');
|
|
554
|
+
}
|
|
555
|
+
return null;
|
|
556
|
+
}
|
|
557
|
+
return result;
|
|
486
558
|
} catch (err) {
|
|
487
559
|
if (log && typeof log.debug === 'function') {
|
|
488
560
|
log.debug(`waitForReady failed, proceeding to serialize: ${(err === null || err === void 0 ? void 0 : err.message) || err}`);
|
|
489
561
|
}
|
|
490
562
|
return null;
|
|
563
|
+
} finally {
|
|
564
|
+
nativeClearTimeout(timer);
|
|
491
565
|
}
|
|
492
566
|
}
|
|
493
567
|
|
|
@@ -582,6 +656,7 @@
|
|
|
582
656
|
waitForReadyScript: waitForReadyScript,
|
|
583
657
|
getReadinessConfig: getReadinessConfig,
|
|
584
658
|
isReadinessDisabled: isReadinessDisabled,
|
|
659
|
+
readinessDeadlineMs: readinessDeadlineMs,
|
|
585
660
|
runReadinessGate: runReadinessGate,
|
|
586
661
|
'default': index
|
|
587
662
|
});
|
|
@@ -607,6 +682,7 @@
|
|
|
607
682
|
exports.postComparison = postComparison;
|
|
608
683
|
exports.postPdfSnapshot = postPdfSnapshot;
|
|
609
684
|
exports.postSnapshot = postSnapshot;
|
|
685
|
+
exports.readinessDeadlineMs = readinessDeadlineMs;
|
|
610
686
|
exports.request = request;
|
|
611
687
|
exports.resolveIgnoreSelectors = resolveIgnoreSelectors;
|
|
612
688
|
exports.resolveMaxFrameDepth = resolveMaxFrameDepth;
|
package/dist/index.js
CHANGED
|
@@ -92,6 +92,12 @@ Object.defineProperty(exports, "postSnapshot", {
|
|
|
92
92
|
return _postSnapshot.default;
|
|
93
93
|
}
|
|
94
94
|
});
|
|
95
|
+
Object.defineProperty(exports, "readinessDeadlineMs", {
|
|
96
|
+
enumerable: true,
|
|
97
|
+
get: function () {
|
|
98
|
+
return _serializeDom.readinessDeadlineMs;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
95
101
|
Object.defineProperty(exports, "request", {
|
|
96
102
|
enumerable: true,
|
|
97
103
|
get: function () {
|
package/dist/serialize-dom.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
exports.getReadinessConfig = getReadinessConfig;
|
|
8
8
|
exports.isReadinessDisabled = isReadinessDisabled;
|
|
9
|
+
exports.readinessDeadlineMs = readinessDeadlineMs;
|
|
9
10
|
exports.runReadinessGate = runReadinessGate;
|
|
10
11
|
exports.waitForReadyScript = waitForReadyScript;
|
|
11
12
|
var _percyInfo = _interopRequireDefault(require("./percy-info.js"));
|
|
@@ -116,6 +117,62 @@ function waitForReadyScript(readinessConfig = {}, {
|
|
|
116
117
|
// options,
|
|
117
118
|
// { callback: true, log }
|
|
118
119
|
// );
|
|
120
|
+
// Effective in-page timeout for each readiness preset, mirroring PRESETS in
|
|
121
|
+
// @percy/dom's readiness.js. Duplicated here because the presets live in the
|
|
122
|
+
// browser bundle, and the Node side needs the number to size its own deadline.
|
|
123
|
+
const PRESET_TIMEOUT_MS = {
|
|
124
|
+
balanced: 10000,
|
|
125
|
+
strict: 30000,
|
|
126
|
+
fast: 5000
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Added to the in-page timeout before the Node side gives up, so a gate that is
|
|
130
|
+
// merely slow (eval round-trip, check teardown) is never pre-empted.
|
|
131
|
+
const READINESS_DEADLINE_GRACE_MS = 3000;
|
|
132
|
+
|
|
133
|
+
// Wall-clock budget the Node side gives the readiness eval.
|
|
134
|
+
//
|
|
135
|
+
// PercyDOM.waitForReady() bounds itself with an in-page `setTimeout`, and every
|
|
136
|
+
// individual check settles on a `setTimeout`/`setInterval`. On a page whose
|
|
137
|
+
// timers are faked and paused -- Playwright's `page.clock.pauseAt()`, sinon
|
|
138
|
+
// `useFakeTimers`, jest fake timers -- neither the checks nor the gate's own
|
|
139
|
+
// timeout can ever fire, so the eval stays pending for the life of the page and
|
|
140
|
+
// hangs the test that called percySnapshot(). Node is the only side of that
|
|
141
|
+
// boundary guaranteed to have a real clock, so the backstop belongs here.
|
|
142
|
+
function readinessDeadlineMs(readinessConfig = {}) {
|
|
143
|
+
let timeout = readinessConfig.timeoutMs ?? readinessConfig.timeout_ms ?? PRESET_TIMEOUT_MS[readinessConfig.preset] ?? PRESET_TIMEOUT_MS.balanced;
|
|
144
|
+
let max = readinessConfig.maxTimeoutMs ?? readinessConfig.max_timeout_ms;
|
|
145
|
+
if (max != null) timeout = Math.min(timeout, max);
|
|
146
|
+
return timeout + READINESS_DEADLINE_GRACE_MS;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Captured once at module load, because the deadline below must not be the very
|
|
150
|
+
// thing a faked clock disables. jest's and sinon's fake timers replace the
|
|
151
|
+
// *global* `setTimeout` binding, and a bare `setTimeout(...)` call resolves that
|
|
152
|
+
// global at call time -- so a consumer whose Node test process has fake timers
|
|
153
|
+
// installed would schedule the deadline on a frozen clock and hang exactly as
|
|
154
|
+
// before, one layer up from the in-page freeze this gate exists to survive.
|
|
155
|
+
//
|
|
156
|
+
// The capture holds for the ordinary case: the SDK imports this module at
|
|
157
|
+
// require time, before a test body or beforeEach reaches `useFakeTimers()`. It
|
|
158
|
+
// is NOT a guarantee. Fake timers installed before this module is first
|
|
159
|
+
// evaluated -- jest's `fakeTimers: { enableGlobally: true }`, a `useFakeTimers()`
|
|
160
|
+
// call in `setupFiles`, or `resetModules()` + a fresh require under an already
|
|
161
|
+
// faked clock -- capture the fake, and the hang returns. Those consumers need
|
|
162
|
+
// `snapshot.readiness.preset: disabled`.
|
|
163
|
+
//
|
|
164
|
+
// Deliberately not `import { setTimeout } from 'node:timers'`, which would be
|
|
165
|
+
// immune to import order too: this file is imported statically by index.js, and
|
|
166
|
+
// index.js is the rollup entry for the browser bundle (see the package's
|
|
167
|
+
// `browser` field). Node-only code in this package is always reached through a
|
|
168
|
+
// lazy `await import(...)` -- `http`/`https` in request.js, `./proxy.js` and its
|
|
169
|
+
// `net`/`tls` imports -- precisely to keep builtins out of that graph. A static
|
|
170
|
+
// builtin import here would not even fail the build, since the rollup config
|
|
171
|
+
// suppresses MISSING_NODE_BUILTINS; it would ship a browser bundle that breaks
|
|
172
|
+
// at runtime, which is worse.
|
|
173
|
+
const nativeSetTimeout = globalThis.setTimeout;
|
|
174
|
+
const nativeClearTimeout = globalThis.clearTimeout;
|
|
175
|
+
const READINESS_DEADLINE_HIT = Symbol('readiness-deadline');
|
|
119
176
|
async function runReadinessGate(evalScript, snapshotOptions = {}, {
|
|
120
177
|
callback = false,
|
|
121
178
|
log
|
|
@@ -125,13 +182,31 @@ async function runReadinessGate(evalScript, snapshotOptions = {}, {
|
|
|
125
182
|
const script = waitForReadyScript(config, {
|
|
126
183
|
callback
|
|
127
184
|
});
|
|
185
|
+
const deadline = readinessDeadlineMs(config);
|
|
186
|
+
let timer;
|
|
128
187
|
try {
|
|
129
|
-
|
|
188
|
+
const evaluation = Promise.resolve(evalScript(script));
|
|
189
|
+
// Hitting the deadline abandons `evaluation`, which may still reject much
|
|
190
|
+
// later -- when the driver tears the page down at end of test, say. Swallow
|
|
191
|
+
// that here so it never surfaces as an unhandled rejection.
|
|
192
|
+
evaluation.catch(() => {});
|
|
193
|
+
const result = await Promise.race([evaluation, new Promise(resolve => {
|
|
194
|
+
timer = nativeSetTimeout(() => resolve(READINESS_DEADLINE_HIT), deadline);
|
|
195
|
+
})]);
|
|
196
|
+
if (result === READINESS_DEADLINE_HIT) {
|
|
197
|
+
if (log && typeof log.debug === 'function') {
|
|
198
|
+
log.debug(`waitForReady did not settle within ${deadline}ms, proceeding to serialize. ` + 'If this page fakes or pauses timers (e.g. Playwright page.clock), disable ' + 'the readiness gate with snapshot.readiness.preset: disabled.');
|
|
199
|
+
}
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
return result;
|
|
130
203
|
} catch (err) {
|
|
131
204
|
if (log && typeof log.debug === 'function') {
|
|
132
205
|
log.debug(`waitForReady failed, proceeding to serialize: ${(err === null || err === void 0 ? void 0 : err.message) || err}`);
|
|
133
206
|
}
|
|
134
207
|
return null;
|
|
208
|
+
} finally {
|
|
209
|
+
nativeClearTimeout(timer);
|
|
135
210
|
}
|
|
136
211
|
}
|
|
137
212
|
var _default = exports.default = waitForReadyScript;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@percy/sdk-utils",
|
|
3
|
-
"version": "1.32.
|
|
3
|
+
"version": "1.32.11-beta.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"publishConfig": {
|
|
11
11
|
"access": "public",
|
|
12
|
-
"tag": "
|
|
12
|
+
"tag": "beta"
|
|
13
13
|
},
|
|
14
14
|
"engines": {
|
|
15
15
|
"node": ">=14"
|
|
@@ -54,5 +54,5 @@
|
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"pac-proxy-agent": "^7.0.2"
|
|
56
56
|
},
|
|
57
|
-
"gitHead": "
|
|
57
|
+
"gitHead": "c72c103d81636a59792393576a82c9471686c3c0"
|
|
58
58
|
}
|