diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 3246b2f..973a06a 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -65,6 +65,8 @@ interface PageEvent { pageUrl: string; sourceMessageId: string; timestamp: number; + pageTitle?: string; + canonicalUrl?: string; activeTimeOnSite?: number; // Derived at transmission not at capture based // on the next page view's activeTimeOnSite, @@ -508,6 +510,15 @@ function sanitizeUrl(href: string): string { } } +function readCanonicalUrl(): string | undefined { + const link = document.querySelector('link[rel="canonical"]'); + const href = link?.href; + if (!href) { + return undefined; + } + return sanitizeUrl(href); +} + function generateIntegrationName(customIntegrationName?: string): string { const coreSdkVersion = mp().getVersion(); const kitVersion = process.env.PACKAGE_VERSION; @@ -917,6 +928,16 @@ class RoktKit implements KitInterface { timestamp: event.Timestamp, }; + const pageTitle = document.title; + if (pageTitle) { + pageView.pageTitle = pageTitle; + } + + const canonicalUrl = readCanonicalUrl(); + if (canonicalUrl) { + pageView.canonicalUrl = canonicalUrl; + } + if (Number.isFinite(event.ActiveTimeOnSite)) { pageView.activeTimeOnSite = event.ActiveTimeOnSite; } @@ -970,6 +991,14 @@ class RoktKit implements KitInterface { timestamp: pageView.timestamp, }; + if (pageView.pageTitle !== undefined) { + pageEvent.pageTitle = pageView.pageTitle; + } + + if (pageView.canonicalUrl !== undefined) { + pageEvent.canonicalUrl = pageView.canonicalUrl; + } + const activeTimeOnSite = pageView.activeTimeOnSite; const hasActiveTime = activeTimeOnSite !== undefined && Number.isFinite(activeTimeOnSite); if (hasActiveTime) { diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index dc18813..63ea72d 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -5687,6 +5687,79 @@ describe('Rokt Forwarder', () => { ]); }); + it('captures the page title and canonical URL when present', async () => { + document.title = 'Home Page Title'; + const canonical = document.createElement('link'); + canonical.setAttribute('rel', 'canonical'); + canonical.setAttribute('href', 'https://example.com/canonical?tracking=abc#section'); + document.head.appendChild(canonical); + + try { + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + (window as any).mParticle.forwarder.process({ + EventName: 'Home Page', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-title', + Timestamp: 1712345678000, + }); + + expect(readStoredPageViews()).toEqual([ + { + pageUrl: window.location.href, + sourceMessageId: 'source-message-id-title', + timestamp: 1712345678000, + pageTitle: 'Home Page Title', + canonicalUrl: 'https://example.com/canonical#section', + }, + ]); + } finally { + document.title = ''; + document.head.removeChild(canonical); + } + }); + + it('omits pageTitle and canonicalUrl when neither is available', async () => { + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + (window as any).mParticle.forwarder.process({ + EventName: 'Home Page', + EventCategory: EventType.Unknown, + EventDataType: MessageType.PageView, + SourceMessageId: 'source-message-id-bare', + Timestamp: 1712345678000, + }); + + expect(readStoredPageViews()).toEqual([ + { + pageUrl: window.location.href, + sourceMessageId: 'source-message-id-bare', + timestamp: 1712345678000, + }, + ]); + }); + it('omits activeTimeOnSite when the source value is non-finite', async () => { await (window as any).mParticle.forwarder.init( { @@ -5989,6 +6062,46 @@ describe('Rokt Forwarder', () => { ]); }); + it('carries pageTitle and canonicalUrl through selectPlacements when stored', async () => { + window.localStorage.setItem( + 'mpPageViews', + JSON.stringify([ + { + pageUrl: 'https://example.com/a', + sourceMessageId: 'source-message-id-a', + timestamp: 1712345678000, + pageTitle: 'Page A', + canonicalUrl: 'https://example.com/canonical-a', + }, + ]), + ); + + await (window as any).mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {}, + ); + + await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled); + + await (window as any).mParticle.forwarder.selectPlacements({ attributes: {} }); + + const forwardedAttributes = (window as any).mParticle.Rokt.selectPlacementsOptions.attributes; + expect(JSON.parse(forwardedAttributes.page_events)).toEqual([ + { + pageUrl: 'https://example.com/a', + sourceMessageId: 'source-message-id-a', + timestamp: 1712345678000, + pageTitle: 'Page A', + canonicalUrl: 'https://example.com/canonical-a', + }, + ]); + }); + it('does not add a page_events attribute when no page views are stored', async () => { await (window as any).mParticle.forwarder.init( {