Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 6fe1903

Browse files
feat: Adopt the inline bootstrap loader (#797)
* Adopt the inline bootstrap loader: https://developers.google.com/maps/documentation/javascript/load-maps-js-api#dynamic-library-import and provide an importLibrary() alias
1 parent e8d1ecd commit 6fe1903

File tree

2 files changed

+175
-91
lines changed

2 files changed

+175
-91
lines changed

‎src/index.test.ts‎

Lines changed: 65 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ jest.useFakeTimers();
2121
afterEach(() => {
2222
document.getElementsByTagName("html")[0].innerHTML = "";
2323
delete Loader["instance"];
24+
if (window.google) delete window.google;
2425
});
2526

2627
test.each([
@@ -65,59 +66,59 @@ test("uses default id if empty string", () => {
6566
expect(new Loader({ apiKey: "foo", id: "" }).id).toBe(DEFAULT_ID);
6667
});
6768

68-
test("setScript adds a script to head with correct attributes", () => {
69+
test("setScript adds a script to head with correct attributes", async() => {
6970
const loader = new Loader({ apiKey: "foo" });
7071

7172
loader["setScript"]();
73+
await 0;
7274

7375
const script = document.head.childNodes[0] as HTMLScriptElement;
7476

7577
expect(script.id).toEqual(loader.id);
76-
expect(script.src).toEqual(loader.createUrl());
77-
expect(script.defer).toBeTruthy();
78-
expect(script.async).toBeTruthy();
79-
expect(script.onerror).toBeTruthy();
80-
expect(script.type).toEqual("text/javascript");
8178
});
8279

83-
test("setScript does not add second script with same id", () => {
84-
new Loader({ apiKey: "foo", id: "bar" })["setScript"]();
85-
new Loader({ apiKey: "foo", id: "bar" })["setScript"]();
86-
87-
expect(document.head.childNodes.length).toBe(1);
88-
});
89-
90-
test("setScript adds a script with id", () => {
80+
test("setScript adds a script with id", async () => {
9181
const loader = new Loader({ apiKey: "foo", id: "bar" });
9282
loader["setScript"]();
83+
await 0;
9384

9485
const script = document.head.childNodes[0] as HTMLScriptElement;
95-
expect(script.id).toEqual(loader.id);
86+
expect(script.localName).toEqual("script");
87+
expect(loader.id).toEqual("bar");
88+
expect(script.id).toEqual("bar");
89+
});
90+
91+
test("setScript does not add second script with same id", async () => {
92+
new Loader({ apiKey: "foo", id: "bar" })["setScript"]();
93+
new Loader({ apiKey: "foo", id: "bar" })["setScript"]();
94+
await 0;
95+
new Loader({ apiKey: "foo", id: "bar" })["setScript"]();
96+
await 0;
97+
98+
expect(document.head.childNodes.length).toBe(1);
9699
});
97100

98101
test("load should return a promise that resolves even if called twice", () => {
99102
const loader = new Loader({ apiKey: "foo" });
103+
loader.importLibrary = (() => Promise.resolve()) as any;
100104

101105
expect.assertions(1);
102106
const promise = Promise.all([loader.load(), loader.load()]).then(() => {
103107
expect(loader["done"]).toBeTruthy();
104108
});
105109

106-
window.__googleMapsCallback(null);
107-
108110
return promise;
109111
});
110112

111113
test("loadCallback callback should fire", () => {
112114
const loader = new Loader({ apiKey: "foo" });
115+
loader.importLibrary = (() => Promise.resolve()) as any;
113116

114117
expect.assertions(2);
115118
loader.loadCallback((e: Event) => {
116119
expect(loader["done"]).toBeTruthy();
117120
expect(e).toBeUndefined();
118121
});
119-
120-
window.__googleMapsCallback(null);
121122
});
122123

123124
test("script onerror should reject promise", async () => {
@@ -163,82 +164,78 @@ test("script onerror should reject promise with multiple loaders", async () => {
163164
test("script onerror should retry", async () => {
164165
const loader = new Loader({ apiKey: "foo", retries: 1 });
165166
const deleteScript = jest.spyOn(loader, "deleteScript");
167+
loader.importLibrary = (() => Promise.reject(new Error("fake error"))) as any;
166168
const rejection = expect(loader.load()).rejects.toBeInstanceOf(Error);
167169
// eslint-disable-next-line @typescript-eslint/no-empty-function
168-
console.log = jest.fn();
170+
console.error = jest.fn();
169171

170-
loader["loadErrorCallback"](
171-
new ErrorEvent("ErrorEvent(", { error: new Error("") })
172-
);
173-
loader["loadErrorCallback"](
174-
new ErrorEvent("ErrorEvent(", { error: new Error("") })
175-
);
172+
// wait for the first failure
173+
await 0;
174+
expect(loader["errors"].length).toBe(1);
175+
// trigger the retry delay:
176176
jest.runAllTimers();
177177

178178
await rejection;
179+
expect(loader["errors"].length).toBe(2);
179180
expect(loader["done"]).toBeTruthy();
181+
expect(loader["failed"]).toBeTruthy();
180182
expect(loader["loading"]).toBeFalsy();
181-
expect(loader["errors"].length).toBe(2);
182183
expect(deleteScript).toHaveBeenCalledTimes(1);
183-
expect(console.log).toHaveBeenCalledTimes(loader.retries);
184+
expect(console.error).toHaveBeenCalledTimes(loader.retries);
184185
});
185186

186187
test("script onerror should reset retry mechanism with next loader", async () => {
187188
const loader = new Loader({ apiKey: "foo", retries: 1 });
188189
const deleteScript = jest.spyOn(loader, "deleteScript");
190+
loader.importLibrary = (() => Promise.reject(new Error("fake error"))) as any;
189191
// eslint-disable-next-line @typescript-eslint/no-empty-function
190-
console.log = jest.fn();
192+
console.error = jest.fn();
191193

192194
let rejection = expect(loader.load()).rejects.toBeInstanceOf(Error);
193-
loader["loadErrorCallback"](
194-
new ErrorEvent("ErrorEvent(", { error: new Error("") })
195-
);
196-
loader["loadErrorCallback"](
197-
new ErrorEvent("ErrorEvent(", { error: new Error("") })
198-
);
195+
// wait for the first first failure
196+
await 0;
197+
expect(loader["errors"].length).toBe(1);
198+
// trigger the retry delay:
199199
jest.runAllTimers();
200200
await rejection;
201201

202+
// try again...
202203
rejection = expect(loader.load()).rejects.toBeInstanceOf(Error);
203204
expect(loader["done"]).toBeFalsy();
205+
expect(loader["failed"]).toBeFalsy();
204206
expect(loader["loading"]).toBeTruthy();
205207
expect(loader["errors"].length).toBe(0);
206208

207-
loader["loadErrorCallback"](
208-
new ErrorEvent("ErrorEvent(", { error: new Error("") })
209-
);
210-
loader["loadErrorCallback"](
211-
new ErrorEvent("ErrorEvent(", { error: new Error("") })
212-
);
209+
// wait for the second first failure
210+
await 0;
211+
expect(loader["errors"].length).toBe(1);
212+
// trigger the retry delay:
213213
jest.runAllTimers();
214214

215215
await rejection;
216216
expect(deleteScript).toHaveBeenCalledTimes(3);
217-
expect(console.log).toHaveBeenCalledTimes(loader.retries * 2);
217+
expect(console.error).toHaveBeenCalledTimes(loader.retries * 2);
218218
});
219219

220220
test("script onerror should not reset retry mechanism with parallel loaders", async () => {
221221
const loader = new Loader({ apiKey: "foo", retries: 1 });
222222
const deleteScript = jest.spyOn(loader, "deleteScript");
223+
loader.importLibrary = (() => Promise.reject(new Error("fake error"))) as any;
223224
// eslint-disable-next-line @typescript-eslint/no-empty-function
224-
console.log = jest.fn();
225+
console.error = jest.fn();
225226

226227
const rejection1 = expect(loader.load()).rejects.toBeInstanceOf(Error);
227228
const rejection2 = expect(loader.load()).rejects.toBeInstanceOf(Error);
228-
loader["loadErrorCallback"](
229-
new ErrorEvent("ErrorEvent(", { error: new Error("") })
230-
);
231-
loader["loadErrorCallback"](
232-
new ErrorEvent("ErrorEvent(", { error: new Error("") })
233-
);
229+
// wait for the first first failure
230+
await 0;
234231
jest.runAllTimers();
235232

236233
await Promise.all([rejection1, rejection2]);
237234
expect(loader["done"]).toBeTruthy();
238235
expect(loader["loading"]).toBeFalsy();
239236
expect(loader["errors"].length).toBe(2);
240237
expect(deleteScript).toHaveBeenCalledTimes(1);
241-
expect(console.log).toHaveBeenCalledTimes(loader.retries);
238+
expect(console.error).toHaveBeenCalledTimes(loader.retries);
242239
});
243240

244241
test("reset should clear state", () => {
@@ -288,12 +285,12 @@ test("failed getter should be correct", () => {
288285
expect(loader["failed"]).toBeTruthy();
289286
});
290287

291-
test("loader should not reset retry mechanism if successfully loaded", () => {
288+
test("loader should not reset retry mechanism if successfully loaded", async() => {
292289
const loader = new Loader({ apiKey: "foo", retries: 0 });
293290
const deleteScript = jest.spyOn(loader, "deleteScript");
291+
loader.importLibrary = (() => Promise.resolve()) as any;
294292

295-
loader["done"] = true;
296-
expect(loader.load()).resolves.toBeUndefined();
293+
await expect(loader.load()).resolves.not.toBeUndefined();
297294

298295
expect(loader["done"]).toBeTruthy();
299296
expect(loader["loading"]).toBeFalsy();
@@ -344,10 +341,11 @@ test("loader should wait if already loading", () => {
344341
loader.load();
345342
});
346343

347-
test("setScript adds a nonce", () => {
344+
test("setScript adds a nonce", async() => {
348345
const nonce = "bar";
349346
const loader = new Loader({ apiKey: "foo", nonce });
350347
loader["setScript"]();
348+
await 0;
351349
const script = document.head.childNodes[0] as HTMLScriptElement;
352350
expect(script.nonce).toBe(nonce);
353351
});
@@ -371,13 +369,25 @@ test("loader should not warn if done and google.maps is defined", async () => {
371369
expect(console.warn).toHaveBeenCalledTimes(0);
372370
});
373371

374-
test("deleteScript removes script tag from head", () => {
372+
test("deleteScript removes script tag from head", async() => {
375373
const loader = new Loader({ apiKey: "foo" });
376374
loader["setScript"]();
375+
await 0;
377376
expect(document.head.childNodes.length).toBe(1);
378377
loader.deleteScript();
379378
expect(document.head.childNodes.length).toBe(0);
380379
// should work without script existing
381380
loader.deleteScript();
382381
expect(document.head.childNodes.length).toBe(0);
383382
});
383+
384+
test("importLibrary resolves correctly", async () => {
385+
window.google = { maps: {} } as any;
386+
google.maps.importLibrary = async (name) => ({ [name]: "fake" } as any);
387+
388+
const loader = new Loader({ apiKey: "foo" });
389+
const corePromise = loader.importLibrary("core");
390+
391+
const core = await corePromise;
392+
expect(core).toEqual({ core: "fake" });
393+
});

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /