diff --git a/src/Rokt-Kit.ts b/src/Rokt-Kit.ts index 0ba7f23..4010419 100644 --- a/src/Rokt-Kit.ts +++ b/src/Rokt-Kit.ts @@ -318,6 +318,11 @@ function generateThankYouElementScript(domain: string | undefined) { function generateBaseUrl(domain: string | undefined) { const resolvedDomain = typeof domain !== 'undefined' ? domain : DEFAULT_ROKT_DOMAIN; + + if (resolvedDomain.includes('://')) { + return resolvedDomain.replace(/\/+$/, ''); + } + const protocol = 'https://'; return [protocol, resolvedDomain].join(''); @@ -331,7 +336,10 @@ function generateReportingUrl(configuredUrl: string | undefined, domain: string return 'https://' + configuredUrl; } - return generateBaseUrl(domain) + endpoint; + const hasNonHttpScheme = domain?.includes('://') && !/^https?:\/\//i.test(domain); + const reportingDomain = hasNonHttpScheme ? undefined : domain; + + return generateBaseUrl(reportingDomain) + endpoint; } function loadRoktScript( @@ -511,6 +519,10 @@ function sendAdBlockMeasurementSignals(domain: string | undefined, version: stri return; } + if (domain && domain.includes('://') && !/^https:\/\//i.test(domain)) { + return; + } + const pageUrl = window.location.href.split('?')[0].split('#')[0]; const params = 'version=' + @@ -520,8 +532,8 @@ function sendAdBlockMeasurementSignals(domain: string | undefined, version: stri '&pageUrl=' + encodeURIComponent(pageUrl); - const existingDomain = domain || 'apps.rokt.com'; - createAutoRemovedIframe('https://' + existingDomain + '/v1/wsdk-init/index.html?' + params); + const existingBaseUrl = domain ? generateBaseUrl(domain) : 'https://apps.rokt.com'; + createAutoRemovedIframe(existingBaseUrl + '/v1/wsdk-init/index.html?' + params); createAutoRemovedIframe( 'https://' + ADBLOCK_CONTROL_DOMAIN + '/v1/wsdk-init/index.html?' + params + '&isControl=true', diff --git a/test/src/tests.spec.ts b/test/src/tests.spec.ts index 7edfc49..6d33747 100644 --- a/test/src/tests.spec.ts +++ b/test/src/tests.spec.ts @@ -4117,6 +4117,24 @@ describe('Rokt Forwarder', () => { ); }); + it('should use a chrome-extension origin verbatim so the launcher loads from the bundled extension', () => { + expect( + (window as any).mParticle.forwarder.testHelpers.generateLauncherScript('chrome-extension://abcdef123/rokt'), + ).toBe('chrome-extension://abcdef123/rokt/wsdk/integrations/launcher.js'); + }); + + it('should trim a trailing slash from a full origin so the path join stays clean', () => { + expect( + (window as any).mParticle.forwarder.testHelpers.generateLauncherScript('chrome-extension://abcdef123/rokt/'), + ).toBe('chrome-extension://abcdef123/rokt/wsdk/integrations/launcher.js'); + }); + + it('should preserve an http origin for local development', () => { + expect((window as any).mParticle.forwarder.testHelpers.generateLauncherScript('http://localhost:8001')).toBe( + 'http://localhost:8001/wsdk/integrations/launcher.js', + ); + }); + it('should return base URL when no extensions are provided', () => { const url = (window as any).mParticle.forwarder.testHelpers.generateLauncherScript(); expect(url).toBe(baseUrl); @@ -4165,6 +4183,13 @@ describe('Rokt Forwarder', () => { const url = (window as any).mParticle.forwarder.testHelpers.generateThankYouElementScript('cname.rokt.com'); expect(url).toBe('https://cname.rokt.com/rokt-elements/rokt-element-thank-you.js'); }); + + it('should use a full custom origin verbatim', () => { + const url = (window as any).mParticle.forwarder.testHelpers.generateThankYouElementScript( + 'chrome-extension://abcdef123/rokt', + ); + expect(url).toBe('chrome-extension://abcdef123/rokt/rokt-elements/rokt-element-thank-you.js'); + }); }); describe('#roktExtensions', () => { @@ -6973,6 +6998,42 @@ describe('Rokt Forwarder', () => { expect(defaultDomainIframe).toBeTruthy(); }); + it('should still create the probe for a full https origin', () => { + Math.random = () => 0.05; + (window as any).__rokt_li_guid__ = 'test-guid-123'; + + (window as any).mParticle.forwarder.testHelpers.sendAdBlockMeasurementSignals( + 'https://custom.rokt.com', + 'test-version', + ); + + const iframes = document.querySelectorAll('iframe'); + const srcs = Array.prototype.map.call(iframes, (iframe: any) => iframe.src) as string[]; + + const httpsDomainIframe = srcs.find( + (src) => src.indexOf('https://custom.rokt.com/v1/wsdk-init/index.html') !== -1, + ); + + expect(httpsDomainIframe).toBeTruthy(); + }); + + it('should not create the probe for a chrome-extension origin', () => { + Math.random = () => 0.05; + (window as any).__rokt_li_guid__ = 'test-guid-123'; + + (window as any).mParticle.forwarder.testHelpers.sendAdBlockMeasurementSignals( + 'chrome-extension://abcdef123/rokt', + 'test-version', + ); + + const iframes = document.querySelectorAll('iframe'); + const srcs = Array.prototype.map.call(iframes, (iframe: any) => iframe.src) as string[]; + + const anyProbe = srcs.find((src) => src.indexOf('/v1/wsdk-init/index.html') !== -1); + + expect(anyProbe).toBeUndefined(); + }); + it('should not create iframes when sampled out', () => { Math.random = () => 0.5; // Above 0.1 threshold (window as any).__rokt_li_guid__ = 'test-guid-123'; @@ -7262,6 +7323,26 @@ describe('Rokt Forwarder', () => { expect(fetchCalls[0].url).toBe('https://apps.rokt-api.com/v1/errors'); }); + it('should use a full https integration domain for the error URL', () => { + const service = new ErrorReportingServiceClass( + { isLoggingEnabled: true, integrationDomain: 'https://custom.rokt.com' }, + '1.0.0', + 'test-guid', + ); + service.report({ message: 'test error', severity: WSDKErrorSeverityConst.ERROR }); + expect(fetchCalls[0].url).toBe('https://custom.rokt.com/v1/errors'); + }); + + it('should fall back to the default error URL for a chrome-extension integration domain', () => { + const service = new ErrorReportingServiceClass( + { isLoggingEnabled: true, integrationDomain: 'chrome-extension://abcdef123/rokt' }, + '1.0.0', + 'test-guid', + ); + service.report({ message: 'test error', severity: WSDKErrorSeverityConst.ERROR }); + expect(fetchCalls[0].url).toBe('https://apps.rokt-api.com/v1/errors'); + }); + it('should include all required fields in the log request body', () => { const service = new ErrorReportingServiceClass({ isLoggingEnabled: true }, 'test-integration', 'test-guid'); service.report({