Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions src/app/analytics/impact.service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { getProductAmount } from 'views/Checkout/utils';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import {
handleImpactDTCCheckout,
resolvePartnerIdFromUrl,
savePaymentDataInLocalStorage,
trackPaymentConversion,
trackSignUp,
Expand Down Expand Up @@ -490,7 +491,7 @@ describe('handleImpactDTCCheckout', () => {
it('When an affiliate partner is identified, then it includes their information in the tracking event', async () => {
const axiosSpy = vi.spyOn(axios, 'post').mockResolvedValue({});

await handleImpactDTCCheckout({ irclickid, utmMedium });
await handleImpactDTCCheckout({ irclickid, partnerId: utmMedium });

const callArgs = axiosSpy.mock.calls[0][1] as { properties: Record<string, unknown> };
expect(callArgs.properties).toHaveProperty('partner_id', utmMedium);
Expand All @@ -514,7 +515,7 @@ describe('handleImpactDTCCheckout', () => {
const setImpactCookiesSpy = vi.mocked(getCookieMock.setImpactCookies);
vi.spyOn(axios, 'post').mockResolvedValue({});

await handleImpactDTCCheckout({ irclickid, utmMedium });
await handleImpactDTCCheckout({ irclickid, partnerId: utmMedium });

expect(setImpactCookiesSpy).toHaveBeenCalledWith('anon_id', irclickid, utmMedium);
});
Expand All @@ -530,6 +531,32 @@ describe('handleImpactDTCCheckout', () => {
});
});

describe('resolvePartnerIdFromUrl', () => {
it('When utm_content is present, then it returns it as the partner ID', () => {
expect(resolvePartnerIdFromUrl('?utm_content=partner_abc')).toBe('partner_abc');
});

it('When utm_medium is a numeric string, then it returns it as the partner ID', () => {
expect(resolvePartnerIdFromUrl('?utm_medium=312695')).toBe('312695');
});

it('When both utm_content and utm_medium are present, then utm_content takes priority', () => {
expect(resolvePartnerIdFromUrl('?utm_content=partner_abc&utm_medium=312695')).toBe('partner_abc');
});

it('When utm_medium is non-numeric, then it returns null', () => {
expect(resolvePartnerIdFromUrl('?utm_medium=google')).toBeNull();
});

it('When neither utm_content nor utm_medium are present, then it returns null', () => {
expect(resolvePartnerIdFromUrl('?utm_source=newsletter')).toBeNull();
});

it('When the search string is empty, then it returns null', () => {
expect(resolvePartnerIdFromUrl('')).toBeNull();
});
});

describe('uuid library', () => {
it('When calling v4, then it generates a valid UUID', async () => {
const { v4 } = await vi.importActual<typeof import('uuid')>('uuid');
Expand Down Expand Up @@ -815,7 +842,7 @@ describe('handleImpactDTCCheckout - additional coverage', () => {
it('When no affiliate partner is identified, then no partner information is included in the event', async () => {
const axiosSpy = vi.spyOn(axios, 'post').mockResolvedValue({});

await handleImpactDTCCheckout({ irclickid, utmMedium: null });
await handleImpactDTCCheckout({ irclickid, partnerId: null });

const callArgs = axiosSpy.mock.calls[0][1] as { properties: Record<string, unknown> };
expect(callArgs.properties).not.toHaveProperty('partner_id');
Expand Down
24 changes: 20 additions & 4 deletions src/app/analytics/impact.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,19 +70,35 @@ export function savePaymentDataInLocalStorage({
localStorageService.set('isFirstPurchase', String(isFirstPurchase));
}

export function resolvePartnerIdFromUrl(search: string = globalThis.location.search): string | null {
const params = new URLSearchParams(search);
const utmContent = params.get('utm_content');
const utmMedium = params.get('utm_medium');

if (utmContent) {
return utmContent;
}

if (utmMedium && /^\d+$/.test(utmMedium)) {
return utmMedium;
}

return null;
}

export async function handleImpactDTCCheckout({
irclickid,
utmMedium,
partnerId,
}: {
irclickid: string;
utmMedium?: string | null;
partnerId?: string | null;
}): Promise<void> {
try {
const IMPACT_API = envService.getVariable('impactApiUrl');
const existingAnonymousId = getCookie('impactAnonymousId');
const anonymousId = existingAnonymousId || uuidV4();

setImpactCookies(anonymousId, irclickid, utmMedium);
setImpactCookies(anonymousId, irclickid, partnerId);

await axios.post(IMPACT_API, {
anonymousId,
Expand All @@ -97,7 +113,7 @@ export async function handleImpactDTCCheckout({
type: 'page',
properties: {
irclickid,
...(utmMedium && { partner_id: utmMedium }),
...(partnerId && { partner_id: partnerId }),
},
});
} catch (error) {
Expand Down
2 changes: 1 addition & 1 deletion src/views/Checkout/views/CheckoutViewWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ const CheckoutViewWrapper = () => {
localStorageService.set(STORAGE_KEYS.GCLID, gclid);
}
if (irclickid) {
handleImpactDTCCheckout({ irclickid, utmMedium });
handleImpactDTCCheckout({ irclickid, partnerId: utmMedium });
}
referralService.captureUcc();
}, []);
Expand Down
Loading