Skip to content

Commit c36e4d3

Browse files
Shivanshu-07claude
andcommitted
feat: PER-7348 add waitForReady() call before serialize()
Adds the readiness gate from percy/cli#2184 to percySnapshot. The SDK now calls PercyDOM.waitForReady() with readiness config (from per-snapshot options or utils.percy.config.snapshot.readiness) before the existing PercyDOM.serialize() call. The serialize call itself is unchanged. Backward compat: typeof guard on PercyDOM.waitForReady means older CLI versions that lack the method skip the readiness step entirely. Graceful degradation: a rejected/thrown waitForReady is logged at debug level and serialize still runs. Disabled preset: { readiness: { preset: 'disabled' } } in snapshot options or config skips the readiness call. Tests cover happy path, backward compat (no waitForReady), disabled preset, and waitForReady rejection. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent bca29b2 commit c36e4d3

2 files changed

Lines changed: 79 additions & 0 deletions

File tree

cypress/e2e/index.cy.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,74 @@ describe('percySnapshot', () => {
407407
});
408408
});
409409

410+
describe('readiness gate (PER-7348)', () => {
411+
// Pre-populate window.PercyDOM so the SDK's `injectPercyDOM` early-returns
412+
// and leaves our stub in place. This lets us observe how the SDK interacts
413+
// with waitForReady / serialize.
414+
const installPercyDOMStub = (stub) => {
415+
cy.window().then((win) => {
416+
win.PercyDOM = stub;
417+
});
418+
};
419+
420+
it('calls waitForReady before serialize when the CLI exposes it', () => {
421+
const calls = [];
422+
installPercyDOMStub({
423+
waitForReady: (cfg) => { calls.push(['waitForReady', cfg]); return Promise.resolve(); },
424+
serialize: (opts) => { calls.push(['serialize', opts]); return { html: { html: '<html></html>' } }; }
425+
});
426+
427+
cy.percySnapshot('readiness-happy-path');
428+
429+
cy.then(() => {
430+
const order = calls.map(([name]) => name);
431+
expect(order.indexOf('waitForReady')).to.be.lessThan(order.indexOf('serialize'));
432+
});
433+
});
434+
435+
it('skips waitForReady when the CLI is old (function is absent)', () => {
436+
const calls = [];
437+
// No waitForReady — simulating an older CLI. serialize must still run.
438+
installPercyDOMStub({
439+
serialize: (opts) => { calls.push(['serialize', opts]); return { html: { html: '<html></html>' } }; }
440+
});
441+
442+
cy.percySnapshot('readiness-backward-compat');
443+
444+
cy.then(() => {
445+
expect(calls.map(([name]) => name)).to.deep.equal(['serialize']);
446+
});
447+
});
448+
449+
it('skips waitForReady when preset is disabled', () => {
450+
const calls = [];
451+
installPercyDOMStub({
452+
waitForReady: (cfg) => { calls.push(['waitForReady', cfg]); return Promise.resolve(); },
453+
serialize: (opts) => { calls.push(['serialize', opts]); return { html: { html: '<html></html>' } }; }
454+
});
455+
456+
cy.percySnapshot('readiness-disabled', { readiness: { preset: 'disabled' } });
457+
458+
cy.then(() => {
459+
expect(calls.map(([name]) => name)).to.deep.equal(['serialize']);
460+
});
461+
});
462+
463+
it('proceeds to serialize when waitForReady rejects', () => {
464+
const calls = [];
465+
installPercyDOMStub({
466+
waitForReady: () => { calls.push(['waitForReady']); return Promise.reject(new Error('readiness failed')); },
467+
serialize: (opts) => { calls.push(['serialize', opts]); return { html: { html: '<html></html>' } }; }
468+
});
469+
470+
cy.percySnapshot('readiness-rejection');
471+
472+
cy.then(() => {
473+
expect(calls.map(([name]) => name)).to.deep.equal(['waitForReady', 'serialize']);
474+
});
475+
});
476+
});
477+
410478
describe('createRegion function', () => {
411479
it('creates a region object with default values', () => {
412480
const region = createRegion();

index.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,17 @@ Cypress.Commands.add('percySnapshot', (name, options = {}) => {
247247

248248
injectPercyDOM(_percyDOMScript);
249249

250+
// Readiness gate — runs before serialize when CLI supports it (PER-7348).
251+
// Uses typeof guard for backward compat with older CLI that lacks waitForReady.
252+
const readinessConfig = options.readiness || utils.percy?.config?.snapshot?.readiness || {};
253+
if (readinessConfig.preset !== 'disabled' && typeof window.PercyDOM?.waitForReady === 'function') {
254+
try {
255+
await window.PercyDOM.waitForReady(readinessConfig);
256+
} catch (e) {
257+
log.debug(`waitForReady failed, proceeding to serialize: ${e?.message || e}`);
258+
}
259+
}
260+
250261
const domSnapshot = window.PercyDOM.serialize({ ...options, dom: doc });
251262
if (width !== null) domSnapshot.width = width;
252263

0 commit comments

Comments
 (0)