Last active
November 20, 2025 17:15
-
-
Save erikakers/67c4341324c9131906dc93eb81fa0213 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; | |
| import { faker } from '@faker-js/faker'; | |
| import UriDecoder from '../utils/uri-decoder'; | |
| // Mock dependencies | |
| vi.mock('../utils/uri-decoder'); | |
| describe('click.ts', () => { | |
| let mockPublish: any; | |
| let mockSubscribe: any; | |
| let mockLogError: any; | |
| let analyticsEventHandler: any; | |
| let trackButtonClick: any; | |
| beforeEach(async () => { | |
| vi.resetModules(); // Ensure fresh module for each test to capture side-effects if needed | |
| mockPublish = vi.fn(); | |
| mockSubscribe = vi.fn(); | |
| mockLogError = vi.fn(); | |
| window.phoenix = { | |
| bus: { | |
| publish: mockPublish, | |
| subscribe: mockSubscribe, | |
| }, | |
| log: { | |
| error: mockLogError, | |
| }, | |
| }; | |
| const originalAddEventListener = document.addEventListener; | |
| document.addEventListener = vi.fn((event, handler) => { | |
| if (event === 'click') { | |
| // We can choose to not attach it to avoid side effects, or attach it. | |
| } else { | |
| originalAddEventListener.call(document, event, handler); | |
| } | |
| }); | |
| // Dynamic import to ensure window.phoenix is ready | |
| const module = await import('./click'); | |
| analyticsEventHandler = module.analyticsEventHandler; | |
| trackButtonClick = module.trackButtonClick; | |
| vi.clearAllMocks(); | |
| document.body.innerHTML = ''; | |
| }); | |
| afterEach(() => { | |
| vi.restoreAllMocks(); | |
| delete (window as any).phoenix; | |
| }); | |
| it('should subscribe to ANALYTICS CLICK on load', async () => { | |
| vi.resetModules(); | |
| const localMockSubscribe = vi.fn(); | |
| window.phoenix = { | |
| bus: { | |
| publish: vi.fn(), | |
| subscribe: localMockSubscribe, | |
| }, | |
| log: { error: vi.fn() } | |
| }; | |
| const module = await import('./click'); | |
| expect(localMockSubscribe).toHaveBeenCalledWith('ANALYTICS', 'CLICK', module.trackButtonClick); | |
| }); | |
| describe('analyticsEventHandler', () => { | |
| // Helper to create a mock event | |
| const createMockEvent = (target: Element) => { | |
| return { | |
| composedPath: () => [target], | |
| target: target, | |
| } as unknown as MouseEvent; | |
| }; | |
| it('should ignore clicks without data-analytics-click', () => { | |
| const div = document.createElement('div'); | |
| const event = createMockEvent(div); | |
| analyticsEventHandler(event); | |
| expect(mockPublish).not.toHaveBeenCalled(); | |
| }); | |
| it('should ignore clicks with data-analytics-click-defer', () => { | |
| const clickId = faker.lorem.slug(); | |
| const button = document.createElement('button'); | |
| button.setAttribute('data-analytics-click', clickId); | |
| button.setAttribute('data-analytics-click-defer', ''); | |
| const event = createMockEvent(button); | |
| analyticsEventHandler(event); | |
| expect(mockPublish).not.toHaveBeenCalled(); | |
| }); | |
| it('should ignore tel: links', () => { | |
| const clickId = faker.lorem.slug(); | |
| const link = document.createElement('a'); | |
| link.href = 'tel:' + faker.phone.number(); | |
| link.setAttribute('data-analytics-click', clickId); | |
| const event = createMockEvent(link); | |
| analyticsEventHandler(event); | |
| expect(mockPublish).not.toHaveBeenCalled(); | |
| }); | |
| it('should track simple clicks', () => { | |
| const clickId = faker.lorem.slug(); | |
| const button = document.createElement('button'); | |
| button.setAttribute('data-analytics-click', clickId); | |
| const event = createMockEvent(button); | |
| analyticsEventHandler(event); | |
| expect(mockPublish).toHaveBeenCalledWith('ANALYTICS', 'FIRE', { | |
| payload: { | |
| analyticsEvent: 'trackClick', | |
| analyticsPayload: { | |
| 'cdl.page.icidList': clickId, | |
| }, | |
| }, | |
| }); | |
| }); | |
| it('should normalize analytics string', () => { | |
| const part1 = faker.lorem.word(); | |
| const part2 = faker.lorem.word(); | |
| const rawString = ` ${part1} | ${part2} `; | |
| const expectedString = `${part1} : ${part2}`; | |
| const button = document.createElement('button'); | |
| button.setAttribute('data-analytics-click', rawString); | |
| const event = createMockEvent(button); | |
| analyticsEventHandler(event); | |
| expect(mockPublish).toHaveBeenCalledWith('ANALYTICS', 'FIRE', { | |
| payload: { | |
| analyticsEvent: 'trackClick', | |
| analyticsPayload: { | |
| 'cdl.page.icidList': expectedString, | |
| }, | |
| }, | |
| }); | |
| }); | |
| it('should include ICID from URL (single)', () => { | |
| const icidValue = faker.string.alphanumeric(10); | |
| const clickId = faker.lorem.slug(); | |
| const link = document.createElement('a'); | |
| link.href = `http://example.com?ICID=${icidValue}`; | |
| link.setAttribute('data-analytics-click', clickId); | |
| (UriDecoder as any).mockImplementation(function () { | |
| return { | |
| getQueryParameter: vi.fn().mockReturnValue({ value: icidValue }), | |
| }; | |
| }); | |
| const event = createMockEvent(link); | |
| analyticsEventHandler(event); | |
| expect(mockPublish).toHaveBeenCalledWith('ANALYTICS', 'FIRE', expect.objectContaining({ | |
| payload: expect.objectContaining({ | |
| analyticsPayload: expect.objectContaining({ | |
| 'cdl.page.icidList': `${clickId} : ${icidValue}`, | |
| }), | |
| }), | |
| })); | |
| }); | |
| it('should include ICID from URL (array)', () => { | |
| const icid1 = faker.string.alphanumeric(5); | |
| const icid2 = faker.string.alphanumeric(5); | |
| const clickId = faker.lorem.slug(); | |
| const link = document.createElement('a'); | |
| link.href = `http://example.com?ICID=${icid1}&ICID=${icid2}`; | |
| link.setAttribute('data-analytics-click', clickId); | |
| (UriDecoder as any).mockImplementation(function () { | |
| return { | |
| getQueryParameter: vi.fn().mockReturnValue([{ value: icid1 }, { value: icid2 }]), | |
| }; | |
| }); | |
| const event = createMockEvent(link); | |
| analyticsEventHandler(event); | |
| expect(mockPublish).toHaveBeenCalledWith('ANALYTICS', 'FIRE', expect.objectContaining({ | |
| payload: expect.objectContaining({ | |
| analyticsPayload: expect.objectContaining({ | |
| 'cdl.page.icidList': `${clickId} : ${icid1},${icid2}`, | |
| }), | |
| }), | |
| })); | |
| }); | |
| it('should include analytics index', () => { | |
| const indexValue = faker.number.int({ min: 1, max: 100 }).toString(); | |
| const clickId = faker.lorem.slug(); | |
| const container = document.createElement('div'); | |
| container.setAttribute('data-analytics-index', indexValue); | |
| const button = document.createElement('button'); | |
| button.setAttribute('data-analytics-click', clickId); | |
| container.appendChild(button); | |
| const event = createMockEvent(button); | |
| analyticsEventHandler(event); | |
| expect(mockPublish).toHaveBeenCalledWith('ANALYTICS', 'FIRE', expect.objectContaining({ | |
| payload: expect.objectContaining({ | |
| analyticsPayload: expect.objectContaining({ | |
| 'cdl.page.icidList': `${clickId} : ${indexValue}`, | |
| }), | |
| }), | |
| })); | |
| }); | |
| it('should parse and include analytics fields', () => { | |
| const clickId = faker.lorem.slug(); | |
| const customKey = faker.lorem.word(); | |
| const customValue = faker.lorem.word(); | |
| const fields = { [customKey]: customValue }; | |
| const button = document.createElement('button'); | |
| button.setAttribute('data-analytics-click', clickId); | |
| button.setAttribute('data-analytics-fields', JSON.stringify(fields)); | |
| const event = createMockEvent(button); | |
| analyticsEventHandler(event); | |
| expect(mockPublish).toHaveBeenCalledWith('ANALYTICS', 'FIRE', expect.objectContaining({ | |
| payload: expect.objectContaining({ | |
| analyticsPayload: expect.objectContaining({ | |
| 'cdl.page.icidList': clickId, | |
| [customKey]: customValue, | |
| }), | |
| }), | |
| })); | |
| }); | |
| it('should log error on invalid analytics fields JSON', () => { | |
| const clickId = faker.lorem.slug(); | |
| const button = document.createElement('button'); | |
| button.setAttribute('data-analytics-click', clickId); | |
| button.setAttribute('data-analytics-fields', '{invalid-json'); | |
| const event = createMockEvent(button); | |
| analyticsEventHandler(event); | |
| expect(mockLogError).toHaveBeenCalled(); | |
| expect(mockPublish).toHaveBeenCalled(); | |
| }); | |
| }); | |
| describe('trackButtonClick', () => { | |
| it('should track button click with basic payload', () => { | |
| const ctaName = faker.commerce.productName(); | |
| trackButtonClick({ | |
| payload: { | |
| ctaName: ctaName, | |
| }, | |
| }); | |
| expect(mockPublish).toHaveBeenCalledWith('ANALYTICS', 'FIRE', { | |
| payload: { | |
| analyticsEvent: 'trackClick', | |
| analyticsPayload: { | |
| 'cdl.page.icidList': `Button Click : ${ctaName}`, | |
| }, | |
| }, | |
| }); | |
| }); | |
| it('should include all optional fields', () => { | |
| const ctaName = faker.commerce.productName(); | |
| const id = faker.string.uuid(); | |
| const cartAdd = faker.commerce.product(); | |
| const flowName = faker.lorem.word(); | |
| const cartOpen = 'true'; | |
| const product = faker.commerce.product(); | |
| trackButtonClick({ | |
| payload: { | |
| ctaName: ctaName, | |
| id: id, | |
| cartAdd: cartAdd, | |
| flowName: flowName, | |
| cartOpen: cartOpen, | |
| product: product, | |
| }, | |
| }); | |
| expect(mockPublish).toHaveBeenCalledWith('ANALYTICS', 'FIRE', expect.objectContaining({ | |
| payload: expect.objectContaining({ | |
| analyticsPayload: expect.objectContaining({ | |
| 'cdl.page.icidList': `Button Click : ${ctaName}`, | |
| 'cdl.cart.id': id, | |
| 'cdl.eventDetails.events.cartAdd': cartAdd, | |
| 'cdl.page.flowName': flowName, | |
| 'cdl.eventDetails.events.cartOpen': cartOpen, | |
| 'cdl.product': product, | |
| }), | |
| }), | |
| })); | |
| }); | |
| it('should publish CLICK_COMPLETE if product is present', () => { | |
| const ctaName = faker.commerce.productName(); | |
| const product = faker.commerce.product(); | |
| trackButtonClick({ | |
| payload: { | |
| ctaName: ctaName, | |
| product: product, | |
| }, | |
| }); | |
| expect(mockPublish).toHaveBeenCalledWith('ANALYTICS', 'CLICK_COMPLETE'); | |
| }); | |
| it('should NOT publish CLICK_COMPLETE if product is missing', () => { | |
| const ctaName = faker.commerce.productName(); | |
| trackButtonClick({ | |
| payload: { | |
| ctaName: ctaName, | |
| }, | |
| }); | |
| expect(mockPublish).not.toHaveBeenCalledWith('ANALYTICS', 'CLICK_COMPLETE'); | |
| }); | |
| }); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment