diff --git a/browser/commands.ts b/browser/commands.ts deleted file mode 100644 index 5d81bd51..00000000 --- a/browser/commands.ts +++ /dev/null @@ -1,16 +0,0 @@ -class Commands { - constructor(private cmds: Commands | Function[]) { - if (Array.isArray(this.cmds)) { - for (const cmd of this.cmds) { - if (typeof cmd === "function") cmd(); - } - } - } - - push(cmd: Function) { - cmd(); - } -} - -export { Commands }; -export default Commands; diff --git a/browser/sdk.test.ts b/browser/sdk.test.ts new file mode 100644 index 00000000..288c500d --- /dev/null +++ b/browser/sdk.test.ts @@ -0,0 +1,52 @@ +const instance_config = { host: "localhost", site: "test", initPassport: false }; + +function loadBundle() { + jest.isolateModules(() => { + require("./sdk"); + }); +} + +describe("browser bundle", () => { + afterEach(() => { + delete window.optable; + }); + + it("creates the instance from instance_config before draining queued commands", () => { + let seen: unknown; + window.optable = { + cmd: [() => (seen = window.optable!.instance)], + instance_config, + }; + + loadBundle(); + + expect(window.optable.instance).toBeDefined(); + expect(seen).toBe(window.optable.instance); + expect(window.optable.utils).toBeDefined(); + }); + + it("completes setup when a queued command throws", () => { + const spy = jest.spyOn(console, "error").mockImplementation(() => {}); + const after = jest.fn(); + window.optable = { + cmd: [ + () => { + throw new Error("boom"); + }, + after, + ], + instance_config, + }; + + loadBundle(); + + expect(after).toHaveBeenCalledTimes(1); + expect(window.optable.instance).toBeDefined(); + expect(window.optable.utils).toBeDefined(); + + const pushed = jest.fn(); + window.optable.cmd!.push(pushed); + expect(pushed).toHaveBeenCalledTimes(1); + spy.mockRestore(); + }); +}); diff --git a/browser/sdk.ts b/browser/sdk.ts index 731f8b4f..9b46053d 100644 --- a/browser/sdk.ts +++ b/browser/sdk.ts @@ -1,14 +1,14 @@ -import Commands from "./commands"; import { resolveMultiNodeTargeting } from "../lib/core/resolvers/resolveMultiTargeting"; import { secureSignalsFromEids } from "../lib/core/secure-signals"; import OptableSDK, { InitConfig } from "../lib/sdk"; import OptablePrebidAnalytics from "../lib/addons/prebid/analytics"; +import OptableCommands from "../lib/addons/commands"; import "../lib/addons/gpt"; import "../lib/addons/try-identify"; type OptableGlobal = { - cmd: Commands | Function[]; + cmd: OptableCommands | Function[]; SDK: OptableSDK["constructor"]; OptablePrebidAnalytics: typeof OptablePrebidAnalytics; utils: Record; @@ -28,9 +28,11 @@ declare global { window.optable = window.optable || {}; window.optable.SDK = OptableSDK; window.optable.OptablePrebidAnalytics = OptablePrebidAnalytics; -window.optable.cmd = new Commands(window.optable.cmd || []); window.optable.utils = { resolveMultiNodeTargeting, secureSignalsFromEids }; if (window.optable.instance_config) { window.optable.instance = new OptableSDK(window.optable.instance_config); } + +// Drain queued commands last so they can use the instance created above +window.optable.cmd = new OptableCommands(window.optable.cmd || []);