Skip to content

Commit 06620da

Browse files
committed
feat(core): CHECKOUT-2739 Add ScriptLoader responsible for loading JS files asynchronously
1 parent d06a228 commit 06620da

4 files changed

Lines changed: 77 additions & 0 deletions

File tree

src/create-script-loader.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import ScriptLoader from './script-loader';
2+
3+
export default function createScriptLoader(): ScriptLoader {
4+
return new ScriptLoader(document);
5+
}

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { default as ScriptLoader } from './script-loader';
2+
export { default as createScriptLoader } from './create-script-loader';

src/script-loader.spec.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import ScriptLoader from './script-loader';
2+
3+
describe('ScriptLoader', () => {
4+
let loader: ScriptLoader;
5+
let script: HTMLScriptElement;
6+
7+
beforeEach(() => {
8+
script = document.createElement('script');
9+
10+
jest.spyOn(document, 'createElement').mockImplementation(() => script);
11+
jest.spyOn(document.body, 'appendChild').mockImplementation((element) =>
12+
element.onreadystatechange(new Event('readystatechange'))
13+
);
14+
15+
loader = new ScriptLoader(document);
16+
});
17+
18+
afterEach(() => {
19+
jest.restoreAllMocks();
20+
});
21+
22+
it('attaches script tag to document', async () => {
23+
await loader.loadScript('https://code.jquery.com/jquery-3.2.1.min.js');
24+
25+
expect(document.body.appendChild).toHaveBeenCalledWith(script);
26+
expect(script.src).toEqual('https://code.jquery.com/jquery-3.2.1.min.js');
27+
});
28+
29+
it('resolves promise if script is loaded', async () => {
30+
const output = await loader.loadScript('https://code.jquery.com/jquery-3.2.1.min.js');
31+
32+
expect(output).toBeInstanceOf(Event);
33+
});
34+
35+
it('rejects promise if script is not loaded', async () => {
36+
jest.spyOn(document.body, 'appendChild').mockImplementation(
37+
(element) => element.onerror(new Event('error'))
38+
);
39+
40+
try {
41+
await loader.loadScript('https://code.jquery.com/jquery-3.2.1.min.js');
42+
} catch (output) {
43+
expect(output).toBeInstanceOf(Event);
44+
}
45+
});
46+
});

src/script-loader.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
export default class ScriptLoader {
2+
constructor(
3+
private _document: Document
4+
) {}
5+
6+
loadScript(src: string): Promise<Event> {
7+
return new Promise((resolve, reject) => {
8+
const script = this._document.createElement('script') as LegacyHTMLScriptElement;
9+
10+
script.onload = (event) => resolve(event);
11+
script.onreadystatechange = (event) => resolve(event);
12+
script.onerror = (event) => reject(event);
13+
script.async = true;
14+
script.src = src;
15+
16+
this._document.body.appendChild(script);
17+
});
18+
}
19+
}
20+
21+
interface LegacyHTMLScriptElement extends HTMLScriptElement {
22+
// `onreadystatechange` is needed to support legacy IE
23+
onreadystatechange: (this: HTMLElement, event: Event) => any;
24+
}

0 commit comments

Comments
 (0)