/** * This file co-works with a html file and utils.js to test a promise that * should be deferred during prerendering. * * Usage example: * Suppose the html is "prerender-promise-test.html" * On prerendering page, prerender-promise-test.html?prerendering: * const prerenderEventCollector = new PrerenderEventCollector(); * const promise = {a promise that should be deferred during prerendering}; * prerenderEventCollector.start(promise, {promise name}); * * On the initiator page, prerender-promise-test.html: * execute * `loadInitiatorPage();` */ // Collects events that happen relevant to a prerendering page. // An event is added when: // 1. start() is called. // 2. a prerenderingchange event is dispatched on this document. // 3. the promise passed to start() is resolved. // 4. addEvent() is called manually. class PrerenderEventCollector { constructor() { this.eventsSeen_ = []; new PrerenderChannel('close').addEventListener('message', () => { window.close(); }); } // Adds an event to `eventsSeen_` along with the prerendering state of the // page. addEvent(eventMessage) { this.eventsSeen_.push( {event: eventMessage, prerendering: document.prerendering}); } // Starts collecting events until the promise resolves. Triggers activation by // telling the initiator page that it is ready for activation. async start(promise, promiseName) { assert_true(document.prerendering); this.addEvent(`started waiting ${promiseName}`); promise .then( () => { this.addEvent(`finished waiting ${promiseName}`); }, (error) => { if (error instanceof Error) error = error.name; this.addEvent(`${promiseName} rejected: ${error}`); }) .finally(() => { // Used to communicate with the main test page. const testChannel = new PrerenderChannel('test-channel'); // Send the observed events back to the main test page. testChannel.postMessage(this.eventsSeen_); testChannel.close(); }); document.addEventListener('prerenderingchange', () => { this.addEvent('prerendering change'); }); // Post a task to give the implementation a chance to fail in case it // resolves a promise without waiting for activation. setTimeout(() => { // Used to communicate with the initiator page. const prerenderChannel = new PrerenderChannel('prerender-channel'); // Inform the initiator page that this page is ready to be activated. prerenderChannel.postMessage('readyToActivate'); prerenderChannel.close(); }, 0); } }