diff --git a/client/src/utils/domReady.test.js b/client/src/utils/domReady.test.js new file mode 100644 index 0000000000..275f8bcc4f --- /dev/null +++ b/client/src/utils/domReady.test.js @@ -0,0 +1,36 @@ +import { domReady } from './domReady'; + +jest.useFakeTimers(); + +describe('domReady', () => { + it('should resolve as true if the DOM is not in loading state', () => { + expect(document.readyState).toEqual('complete'); + + return domReady().then(() => { + expect(document.readyState).toEqual('complete'); + }); + }); + + it('should resolve as true if the DOM loading but then completes loading', () => { + let trackingValue = null; + + Object.defineProperty(document, 'readyState', { + value: 'loading', + }); + + expect(document.readyState).toEqual('loading'); + + setTimeout(() => { + trackingValue = true; + document.dispatchEvent(new CustomEvent('DOMContentLoaded')); + }, 5); + + const promise = domReady(); + + jest.runAllTimers(); + + return promise.then(() => { + expect(trackingValue).toEqual(true); + }); + }); +}); diff --git a/client/src/utils/domReady.ts b/client/src/utils/domReady.ts new file mode 100644 index 0000000000..36edce2633 --- /dev/null +++ b/client/src/utils/domReady.ts @@ -0,0 +1,16 @@ +/** + * Returns a promise that resolves once the DOM is ready for interaction. + */ +const domReady = async () => + new Promise((resolve) => { + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => resolve(), { + once: true, + passive: true, + }); + } else { + resolve(); + } + }); + +export { domReady };