diff --git a/packages/plugin-page-url-enrichment-browser/src/page-url-enrichment.ts b/packages/plugin-page-url-enrichment-browser/src/page-url-enrichment.ts index 48626b175..50eb2bd81 100644 --- a/packages/plugin-page-url-enrichment-browser/src/page-url-enrichment.ts +++ b/packages/plugin-page-url-enrichment-browser/src/page-url-enrichment.ts @@ -84,8 +84,17 @@ export const pageUrlEnrichmentPlugin = (): EnrichmentPlugin => { const saveURLInfo = async () => { if (sessionStorage && isStorageEnabled) { const URLInfo = await sessionStorage.get(URL_INFO_STORAGE_KEY); - const previousURL = URLInfo?.[CURRENT_PAGE_STORAGE_KEY] || ''; const currentURL = getDecodeURI((typeof location !== 'undefined' && location.href) || ''); + const storedCurrentURL = URLInfo?.[CURRENT_PAGE_STORAGE_KEY] || ''; + + let previousURL; + if (currentURL === storedCurrentURL) { + previousURL = URLInfo?.[PREVIOUS_PAGE_STORAGE_KEY] || ''; + } else if (storedCurrentURL) { + previousURL = storedCurrentURL; + } else { + previousURL = document.referrer || ''; + } await sessionStorage.set(URL_INFO_STORAGE_KEY, { [CURRENT_PAGE_STORAGE_KEY]: currentURL, diff --git a/packages/plugin-page-url-enrichment-browser/test/page-url-enrichment.test.ts b/packages/plugin-page-url-enrichment-browser/test/page-url-enrichment.test.ts index de8285563..4040f8a9d 100644 --- a/packages/plugin-page-url-enrichment-browser/test/page-url-enrichment.test.ts +++ b/packages/plugin-page-url-enrichment-browser/test/page-url-enrichment.test.ts @@ -491,6 +491,114 @@ describe('pageUrlEnrichmentPlugin', () => { }); }); + describe('first page load with document.referrer', () => { + test('should set Previous Page Type to "direct" when no referrer exists', async () => { + sessionStorage.clear(); + + Object.defineProperty(document, 'referrer', { + value: '', + configurable: true, + }); + + const newPlugin = pageUrlEnrichmentPlugin(); + await newPlugin.setup?.(mockConfig, mockAmplitude); + + const firstUrl = new URL('https://www.example.com/'); + mockWindowLocationFromURL(firstUrl); + mockDocumentTitle('Home - Example'); + + window.history.replaceState(undefined, ''); + await new Promise((resolve) => setTimeout(resolve, 0)); + + const event = await newPlugin.execute?.({ + event_type: 'Page View', + }); + + expect(event?.event_properties).toMatchObject({ + '[Amplitude] Page Domain': 'www.example.com', + '[Amplitude] Page Location': 'https://www.example.com/', + '[Amplitude] Previous Page Location': '', + '[Amplitude] Previous Page Type': 'direct', + }); + + const urlInfoStr = sessionStorage?.getItem(URL_INFO_STORAGE_KEY) || ''; + const urlInfo = JSON.parse(urlInfoStr); + expect(urlInfo[CURRENT_PAGE_STORAGE_KEY]).toBe('https://www.example.com/'); + expect(urlInfo[PREVIOUS_PAGE_STORAGE_KEY]).toBe(''); + + await newPlugin.teardown?.(); + }); + + test('should preserve external referrer on first page load', async () => { + sessionStorage.clear(); + + Object.defineProperty(document, 'referrer', { + value: 'https://google.com/search', + configurable: true, + }); + + const newPlugin = pageUrlEnrichmentPlugin(); + await newPlugin.setup?.(mockConfig, mockAmplitude); + + const firstUrl = new URL('https://www.example.com/'); + mockWindowLocationFromURL(firstUrl); + mockDocumentTitle('Home - Example'); + + window.history.replaceState(undefined, ''); + await new Promise((resolve) => setTimeout(resolve, 0)); + + const event = await newPlugin.execute?.({ + event_type: 'Page View', + }); + + expect(event?.event_properties).toMatchObject({ + '[Amplitude] Page Domain': 'www.example.com', + '[Amplitude] Page Location': 'https://www.example.com/', + '[Amplitude] Previous Page Location': 'https://google.com/search', + '[Amplitude] Previous Page Type': 'external', + }); + + const urlInfoStr = sessionStorage?.getItem(URL_INFO_STORAGE_KEY) || ''; + const urlInfo = JSON.parse(urlInfoStr); + expect(urlInfo[CURRENT_PAGE_STORAGE_KEY]).toBe('https://www.example.com/'); + expect(urlInfo[PREVIOUS_PAGE_STORAGE_KEY]).toBe('https://google.com/search'); + + await newPlugin.teardown?.(); + }); + + test('should handle history events before first event is tracked', async () => { + sessionStorage.clear(); + + Object.defineProperty(document, 'referrer', { + value: '', + configurable: true, + }); + + const newPlugin = pageUrlEnrichmentPlugin(); + await newPlugin.setup?.(mockConfig, mockAmplitude); + + const firstUrl = new URL('https://www.example.com/home'); + mockWindowLocationFromURL(firstUrl); + + window.history.pushState(undefined, ''); + await new Promise((resolve) => setTimeout(resolve, 0)); + + window.history.replaceState(undefined, ''); + await new Promise((resolve) => setTimeout(resolve, 0)); + + const event = await newPlugin.execute?.({ + event_type: 'Page View', + }); + + expect(event?.event_properties).toMatchObject({ + '[Amplitude] Previous Page Location': '', + '[Amplitude] Previous Page Type': 'direct', + }); + + await newPlugin.teardown?.(); + }); + }); + describe('others', () => { test('should handle when globalScope is not defined', async () => { jest.spyOn(Core, 'getGlobalScope').mockReturnValue(undefined); diff --git a/yarn.lock b/yarn.lock index 59ec84d3b..ca14d2bc8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,15 +7,7 @@ resolved "https://registry.yarnpkg.com/@amplitude/analytics-connector/-/analytics-connector-1.6.4.tgz#8a811ff5c8ee46bdfea0e8f61c7578769b5778ed" integrity sha512-SpIv0IQMNIq6SH3UqFGiaZyGSc7PBZwRdq7lvP0pBxW8i4Ny+8zwI0pV+VMfMHQwWY3wdIbWw5WQphNjpdq1/Q== -"@amplitude/analytics-core@>=1 <2": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@amplitude/analytics-core/-/analytics-core-1.2.8.tgz#eb454effa04d144458035c0db5290d0e13e49b83" - integrity sha512-Krxpr5uvS3HmmjvpYqPfbMbs2kcZZu09L+6KwQnPiofWRzoXWIM217fRfy6aSD/QrAoPGbZjvtVitw9cB7Cx+A== - dependencies: - "@amplitude/analytics-types" "^1.4.0" - tslib "^2.4.1" - -"@amplitude/analytics-types@>=1 <2", "@amplitude/analytics-types@^1.0.0", "@amplitude/analytics-types@^1.3.4", "@amplitude/analytics-types@^1.4.0": +"@amplitude/analytics-types@^1.0.0", "@amplitude/analytics-types@^1.3.4": version "1.4.0" resolved "https://registry.yarnpkg.com/@amplitude/analytics-types/-/analytics-types-1.4.0.tgz#63f84e5ea5e26beeb06745732063e3787194f0d2" integrity sha512-RiMPHBqdrJ8ktTqG+Wzj2htnN/PCG9jGZG0SXtTFnWwVvcAJYbYm55/nrP1TTyrx1OlLhvF2VG3lVUP/xGAU8w==