Skip to content

Commit

Permalink
feat: decode URI for page view properties
Browse files Browse the repository at this point in the history
  • Loading branch information
yuhao900914 committed Sep 12, 2024
1 parent 812713c commit c4c823b
Show file tree
Hide file tree
Showing 2 changed files with 177 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,32 @@ export const pageViewTrackingPlugin: CreatePageViewTrackingPlugin = (options: Op
let localConfig: BrowserConfig;
const { trackOn, trackHistoryChanges, eventType = defaultPageViewEvent } = options;

const getDecodeURI = (locationStr: string): string => {
let decodedLocationStr = locationStr;
try {
decodedLocationStr = decodeURI(locationStr);
} catch (e) {
/* istanbul ignore next */
loggerProvider?.log('Malformed URI sequence: ', e);
}

return decodedLocationStr;
};

const createPageViewEvent = async (): Promise<Event> => {
const locationHref = getDecodeURI(location.href);
return {
event_type: eventType,
event_properties: {
...(await getCampaignParams()),
'[Amplitude] Page Domain':
/* istanbul ignore next */ (typeof location !== 'undefined' && location.hostname) || '',
'[Amplitude] Page Location':
/* istanbul ignore next */ (typeof location !== 'undefined' && location.href) || '',
'[Amplitude] Page Location': /* istanbul ignore next */ (typeof location !== 'undefined' && locationHref) || '',
'[Amplitude] Page Path':
/* istanbul ignore next */ (typeof location !== 'undefined' && location.pathname) || '',
/* istanbul ignore next */ (typeof location !== 'undefined' && getDecodeURI(location.pathname)) || '',
'[Amplitude] Page Title': /* istanbul ignore next */ (typeof document !== 'undefined' && document.title) || '',
'[Amplitude] Page URL':
/* istanbul ignore next */ (typeof location !== 'undefined' && location.href.split('?')[0]) || '',
/* istanbul ignore next */ (typeof location !== 'undefined' && locationHref.split('?')[0]) || '',
},
};
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,167 @@ describe('pageViewTrackingPlugin', () => {

expect(track).toHaveBeenCalledTimes(1);
});

test('should track dynamic page view with decoded URI location info', async () => {
mockConfig.pageCounter = 0;

const amplitude = createInstance();
const track = jest.spyOn(amplitude, 'track').mockReturnValue({
promise: Promise.resolve({
code: 200,
message: '',
event: {
event_type: '[Amplitude] Page Viewed',
},
}),
});

const oldURL = new URL('https://www.example.com');
mockWindowLocationFromURL(oldURL);
const plugin = pageViewTrackingPlugin();
await plugin.setup?.(mockConfig, amplitude);

// https://www.example.com/home-шеллы?x=test
const newURL = new URL('https://www.example.com/home-%D1%88%D0%B5%D0%BB%D0%BB%D1%8B?x=test');
mockWindowLocationFromURL(newURL);
window.history.pushState(undefined, newURL.href);

// Page view tracking on push state executes async
// Block event loop for 1s before asserting
await new Promise((resolve) => setTimeout(resolve, 1000));

expect(track).toHaveBeenNthCalledWith(1, {
event_properties: {
'[Amplitude] Page Domain': oldURL.hostname,
'[Amplitude] Page Location': oldURL.toString(),
'[Amplitude] Page Path': oldURL.pathname,
'[Amplitude] Page Title': '',
'[Amplitude] Page URL': oldURL.toString(),
},
event_type: '[Amplitude] Page Viewed',
});

expect(track).toHaveBeenNthCalledWith(2, {
event_properties: {
'[Amplitude] Page Domain': newURL.hostname,
'[Amplitude] Page Location': 'https://www.example.com/home-шеллы?x=test',
'[Amplitude] Page Path': '/home-шеллы',
'[Amplitude] Page Title': '',
'[Amplitude] Page URL': 'https://www.example.com/home-шеллы',
},
event_type: '[Amplitude] Page Viewed',
});

expect(track).toHaveBeenCalledTimes(2);
});

test('should track dynamic page view with malformed location info', async () => {
mockConfig.pageCounter = 0;

const amplitude = createInstance();
const track = jest.spyOn(amplitude, 'track').mockReturnValue({
promise: Promise.resolve({
code: 200,
message: '',
event: {
event_type: '[Amplitude] Page Viewed',
},
}),
});

const oldURL = new URL('https://www.example.com');
mockWindowLocationFromURL(oldURL);
const plugin = pageViewTrackingPlugin();
await plugin.setup?.(mockConfig, amplitude);

const malformedPath = '/home-%D1%88%D0%B5%D0BB%D0%BB%D1%8B'; // Invalid encoding string
const malformedURL = `https://www.example.com${malformedPath}`;
const malformedLocation = `https://www.example.com${malformedPath}?x=test`;
const newURL = new URL(malformedLocation);
mockWindowLocationFromURL(newURL);
window.history.pushState(undefined, newURL.href);

// Page view tracking on push state executes async
// Block event loop for 1s before asserting
await new Promise((resolve) => setTimeout(resolve, 1000));

expect(track).toHaveBeenNthCalledWith(1, {
event_properties: {
'[Amplitude] Page Domain': oldURL.hostname,
'[Amplitude] Page Location': oldURL.toString(),
'[Amplitude] Page Path': oldURL.pathname,
'[Amplitude] Page Title': '',
'[Amplitude] Page URL': oldURL.toString(),
},
event_type: '[Amplitude] Page Viewed',
});

expect(track).toHaveBeenNthCalledWith(2, {
event_properties: {
'[Amplitude] Page Domain': newURL.hostname,
'[Amplitude] Page Location': malformedLocation,
'[Amplitude] Page Path': malformedPath,
'[Amplitude] Page Title': '',
'[Amplitude] Page URL': malformedURL,
},
event_type: '[Amplitude] Page Viewed',
});

expect(track).toHaveBeenCalledTimes(2);
});

test('should track dynamic page view with regular location info', async () => {
mockConfig.pageCounter = 0;

const amplitude = createInstance();
const track = jest.spyOn(amplitude, 'track').mockReturnValue({
promise: Promise.resolve({
code: 200,
message: '',
event: {
event_type: '[Amplitude] Page Viewed',
},
}),
});

const oldURL = new URL('https://www.example.com');
mockWindowLocationFromURL(oldURL);
const plugin = pageViewTrackingPlugin();
await plugin.setup?.(mockConfig, amplitude);

const newBaseURL = `https://www.example.com/home-shell`;
const newURL = new URL(`${newBaseURL}?x=test`);
mockWindowLocationFromURL(newURL);
window.history.pushState(undefined, newURL.href);

// Page view tracking on push state executes async
// Block event loop for 1s before asserting
await new Promise((resolve) => setTimeout(resolve, 1000));

expect(track).toHaveBeenNthCalledWith(1, {
event_properties: {
'[Amplitude] Page Domain': oldURL.hostname,
'[Amplitude] Page Location': oldURL.toString(),
'[Amplitude] Page Path': oldURL.pathname,
'[Amplitude] Page Title': '',
'[Amplitude] Page URL': oldURL.toString(),
},
event_type: '[Amplitude] Page Viewed',
});

expect(track).toHaveBeenNthCalledWith(2, {
event_properties: {
'[Amplitude] Page Domain': newURL.hostname,
'[Amplitude] Page Location': newURL.toString(),
'[Amplitude] Page Path': newURL.pathname,
'[Amplitude] Page Title': '',
'[Amplitude] Page URL': newBaseURL,
},
event_type: '[Amplitude] Page Viewed',
});

expect(track).toHaveBeenCalledTimes(2);
});
});

describe('execute', () => {
Expand Down

0 comments on commit c4c823b

Please sign in to comment.