diff --git a/src/helpers/get-zaraz.spec.ts b/src/helpers/get-zaraz.spec.ts index 541643f..e7a860f 100644 --- a/src/helpers/get-zaraz.spec.ts +++ b/src/helpers/get-zaraz.spec.ts @@ -13,12 +13,20 @@ declare global { let windowObj: Window & typeof globalThis; +const logSpy = jest.spyOn(console, 'log'); + beforeAll(() => { windowObj = window; + + logSpy.mockImplementation(); }); afterAll(() => { - window = windowObj; + Object.defineProperty(global, 'window', { + value: windowObj, + }); + + logSpy.mockRestore(); }); describe('getZaraz()', () => { @@ -36,11 +44,57 @@ describe('getZaraz()', () => { }); }); - it('should throw when zaraz does not exist on the window', () => { + it('should queue events when zaraz is not defined', () => { window.zaraz = undefined; + // getZaraz() returns a queue. + getZaraz().track('page_view', { + page_location: 'https://example.com', + page_path: '/', + page_title: 'Home', + }); + + expect(logSpy).toHaveBeenCalledWith( + `Zaraz Web API is not initialized. Queueing events...`, + ); + + window.zaraz = { + track: trackMock, + set: setMock, + ecommerce: ecommerceMock, + }; + + // Triggers the flush. + getZaraz().track('button clicked', { userId: 'ABC-123', value: 200 }); + + expect(logSpy).toHaveBeenCalledWith( + `Zaraz Web API is initialized. Flushing queue...`, + ); + + // Calls the queued events. + expect(trackMock).toHaveBeenCalledWith('page_view', { + page_location: 'https://example.com', + page_path: '/', + page_title: 'Home', + }); + + // Calls the actual event. + expect(trackMock).toHaveBeenCalledWith('button clicked', { + userId: 'ABC-123', + value: 200, + }); + + expect(logSpy).toHaveBeenCalledWith(`Zaraz Web API queue flushed.`); + }); + + it('should throw when window is not defined', () => { + // Set window to undefined. + Object.defineProperty(global, 'window', { + value: undefined, + }); + expect(() => getZaraz()).toThrow( - `Cannot use Zaraz Web API, because window.zaraz is not defined.`, + `Cannot use Zaraz Web API, because window is not defined.`, ); }); }); diff --git a/src/helpers/get-zaraz.ts b/src/helpers/get-zaraz.ts index 7b7abe2..25070a6 100644 --- a/src/helpers/get-zaraz.ts +++ b/src/helpers/get-zaraz.ts @@ -1,12 +1,49 @@ +type QueueItem = { + method: 'track' | 'set' | 'ecommerce'; + arguments: unknown; +}; + +let queue: QueueItem[] = []; + /** - * A utility that checks if zaraz exists on the window object. If not it throws - * an error. Else returns the zaraz object. + * A utility that checks if zaraz exists on the window object. If not it queues + * the events until zaraz is initialized. If zaraz is initialized, it flushes + * the queue. */ export function getZaraz() { - if (typeof window !== 'undefined' && !window?.zaraz) { - throw new Error( - `Cannot use Zaraz Web API, because window.zaraz is not defined.`, - ); + if (typeof window === 'undefined') { + throw new Error(`Cannot use Zaraz Web API, because window is not defined.`); + } + + if (!window?.zaraz) { + // eslint-disable-next-line no-console + console.log(`Zaraz Web API is not initialized. Queueing events...`); + + return { + track: (...args: unknown[]) => { + queue.push({ method: 'track', arguments: args }); + }, + set: (...args: unknown[]) => { + queue.push({ method: 'set', arguments: args }); + }, + ecommerce: (...args: unknown[]) => { + queue.push({ method: 'ecommerce', arguments: args }); + }, + }; + } + + if (queue.length > 0) { + // eslint-disable-next-line no-console + console.log(`Zaraz Web API is initialized. Flushing queue...`); + + queue.forEach((event) => { + window.zaraz[event.method](...(event.arguments as [])); + }); + + // eslint-disable-next-line no-console + console.log(`Zaraz Web API queue flushed.`); + + queue = []; // Clear the queue } return window.zaraz;