deferred-promise-utils.js (2722B)
1 /** 2 * This file co-works with a html file and utils.js to test a promise that 3 * should be deferred during prerendering. 4 * 5 * Usage example: 6 * Suppose the html is "prerender-promise-test.html" 7 * On prerendering page, prerender-promise-test.html?prerendering: 8 * const prerenderEventCollector = new PrerenderEventCollector(); 9 * const promise = {a promise that should be deferred during prerendering}; 10 * prerenderEventCollector.start(promise, {promise name}); 11 * 12 * On the initiator page, prerender-promise-test.html: 13 * execute 14 * `loadInitiatorPage();` 15 */ 16 17 // Collects events that happen relevant to a prerendering page. 18 // An event is added when: 19 // 1. start() is called. 20 // 2. a prerenderingchange event is dispatched on this document. 21 // 3. the promise passed to start() is resolved. 22 // 4. addEvent() is called manually. 23 class PrerenderEventCollector { 24 constructor() { 25 this.eventsSeen_ = []; 26 new PrerenderChannel('close').addEventListener('message', () => { 27 window.close(); 28 }); 29 } 30 31 // Adds an event to `eventsSeen_` along with the prerendering state of the 32 // page. 33 addEvent(eventMessage) { 34 this.eventsSeen_.push( 35 {event: eventMessage, prerendering: document.prerendering}); 36 } 37 38 // Starts collecting events until the promise resolves. Triggers activation by 39 // telling the initiator page that it is ready for activation. 40 async start(promise, promiseName) { 41 assert_true(document.prerendering); 42 this.addEvent(`started waiting ${promiseName}`); 43 promise 44 .then( 45 () => { 46 this.addEvent(`finished waiting ${promiseName}`); 47 }, 48 (error) => { 49 if (error instanceof Error) 50 error = error.name; 51 this.addEvent(`${promiseName} rejected: ${error}`); 52 }) 53 .finally(() => { 54 // Used to communicate with the main test page. 55 const testChannel = new PrerenderChannel('test-channel'); 56 // Send the observed events back to the main test page. 57 testChannel.postMessage(this.eventsSeen_); 58 testChannel.close(); 59 }); 60 document.addEventListener('prerenderingchange', () => { 61 this.addEvent('prerendering change'); 62 }); 63 64 // Post a task to give the implementation a chance to fail in case it 65 // resolves a promise without waiting for activation. 66 setTimeout(() => { 67 // Used to communicate with the initiator page. 68 const prerenderChannel = new PrerenderChannel('prerender-channel'); 69 // Inform the initiator page that this page is ready to be activated. 70 prerenderChannel.postMessage('readyToActivate'); 71 prerenderChannel.close(); 72 }, 0); 73 } 74 }