helpers.js (3468B)
1 // Test helpers used by multiple Web Locks API tests. 2 (() => { 3 4 // Generate a unique resource identifier, using the script path and 5 // test case name. This is useful to avoid lock interference between 6 // test cases. 7 let res_num = 0; 8 self.uniqueName = (testCase, prefix) => { 9 return `${self.location.pathname}-${prefix}-${testCase.name}-${++res_num}`; 10 }; 11 self.uniqueNameByQuery = () => { 12 const prefix = new URL(location.href).searchParams.get('prefix'); 13 // Add randomness to prevent tests from timing out on leaked locks from 14 // previous sub-tests in the same file. 15 return `${prefix}-${++res_num}-${Math.random()}`; 16 } 17 18 // Inject an iframe showing the given url into the page, and resolve 19 // the returned promise when the frame is loaded. 20 self.iframe = url => new Promise(resolve => { 21 const element = document.createElement('iframe'); 22 element.addEventListener( 23 'load', () => { resolve(element); }, { once: true }); 24 element.src = url; 25 document.documentElement.appendChild(element); 26 }); 27 28 // Post a message to the target frame, and resolve the returned 29 // promise when a response comes back. The posted data is annotated 30 // with unique id to track the response. This assumes the use of 31 // 'iframe.html' as the frame, which implements this protocol. 32 let next_request_id = 0; 33 self.postToFrameAndWait = (frame, data) => { 34 const iframe_window = frame.contentWindow; 35 data.rqid = next_request_id++; 36 iframe_window.postMessage(data, '*'); 37 return new Promise(resolve => { 38 const listener = event => { 39 if (event.source !== iframe_window || event.data.rqid !== data.rqid) 40 return; 41 self.removeEventListener('message', listener); 42 resolve(event.data); 43 }; 44 self.addEventListener('message', listener); 45 }); 46 }; 47 48 // Post a message to the target worker, and resolve the returned 49 // promise when a response comes back. The posted data is annotated 50 // with unique id to track the response. This assumes the use of 51 // 'worker.js' as the worker, which implements this protocol. 52 self.postToWorkerAndWait = (worker, data) => { 53 return new Promise(resolve => { 54 data.rqid = next_request_id++; 55 worker.postMessage(data); 56 const listener = event => { 57 if (event.data.rqid !== data.rqid) 58 return; 59 worker.removeEventListener('message', listener); 60 resolve(event.data); 61 }; 62 worker.addEventListener('message', listener); 63 }); 64 }; 65 66 /** 67 * Request a lock and hold it until the subtest ends. 68 * @param {*} t test runner object 69 * @param {string} name lock name 70 * @param {LockOptions=} options lock options 71 * @returns 72 */ 73 self.requestLockAndHold = (t, name, options = {}) => { 74 let [promise, resolve] = self.makePromiseAndResolveFunc(); 75 const released = navigator.locks.request(name, options, () => promise); 76 // Add a cleanup function that releases the lock by resolving the promise, 77 // and then waits until the lock is really released, to avoid contaminating 78 // following tests with temporarily held locks. 79 t.add_cleanup(() => { 80 resolve(); 81 // Cleanup shouldn't fail if the request is aborted. 82 return released.catch(() => undefined); 83 }); 84 return released; 85 }; 86 87 self.makePromiseAndResolveFunc = () => { 88 let resolve; 89 const promise = new Promise(r => { resolve = r; }); 90 return [promise, resolve]; 91 }; 92 93 })();