diff --git a/packages/browser/src/browser/__tests__/page-enrichment.integration.test.ts b/packages/browser/src/browser/__tests__/page-enrichment.integration.test.ts new file mode 100644 index 000000000..8c44ebe5e --- /dev/null +++ b/packages/browser/src/browser/__tests__/page-enrichment.integration.test.ts @@ -0,0 +1,189 @@ +import { pick } from 'lodash' +import unfetch from 'unfetch' +import { Analytics, AnalyticsBrowser } from '../..' +import { + createMockFetchImplementation, + getPageCtxFixture, +} from '../../test-helpers/fixtures' + +jest.mock('unfetch') +jest.mocked(unfetch).mockImplementation(createMockFetchImplementation()) + +let ajs: Analytics + +beforeEach(async () => { + await AnalyticsBrowser.load({ + writeKey: 'abc_123', + cdnSettings: { integrations: {} }, + }).then(([analytics]) => { + ajs = analytics + }) +}) +describe('Page Enrichment', () => { + test('enriches page calls', async () => { + const ctx = await ajs.page('Checkout', {}) + + expect(ctx.event.properties).toMatchInlineSnapshot(` + Object { + "name": "Checkout", + "path": "/", + "referrer": "", + "search": "", + "title": "", + "url": "http://localhost/", + } + `) + }) + + test('enriches track events with the page context', async () => { + const ctx = await ajs.track('My event', { + banana: 'phone', + }) + + expect(ctx.event.context?.page).toMatchInlineSnapshot(` + Object { + "path": "/", + "referrer": "", + "search": "", + "title": "", + "url": "http://localhost/", + } + `) + }) + + describe('event.properties override behavior', () => { + test('special page properties in event.properties (url, referrer, etc) are copied to context.page', async () => { + const eventProps = getPageCtxFixture() + ;(eventProps as any)['should_not_show_up'] = 'hello' + const ctx = await ajs.page('My Event', eventProps) + const page = ctx.event.context!.page + expect(page).toEqual( + pick(eventProps, ['url', 'path', 'referrer', 'search', 'title']) + ) + }) + + test('special page properties in event.properties (url, referrer, etc) are not copied to context.page in non-page calls', async () => { + const eventProps = getPageCtxFixture() + ;(eventProps as any)['should_not_show_up'] = 'hello' + const ctx = await ajs.track('My Event', eventProps) + const page = ctx.event.context!.page + expect(page).toMatchInlineSnapshot(` + Object { + "path": "/", + "referrer": "", + "search": "", + "title": "", + "url": "http://localhost/", + } + `) + }) + + test('event page properties should not be mutated', async () => { + const eventProps = getPageCtxFixture() + const ctx = await ajs.page('My Event', eventProps) + const page = ctx.event.context!.page + expect(page).toEqual(eventProps) + }) + + test('page properties should have defaults', async () => { + const eventProps = pick(getPageCtxFixture(), ['path', 'referrer']) + const ctx = await ajs.page('My Event', eventProps) + const page = ctx.event.context!.page + expect(page).toEqual({ + ...eventProps, + url: 'http://localhost/', + search: '', + title: '', + }) + }) + + test('undefined / null / empty string properties on event get overridden as usual', async () => { + const eventProps = getPageCtxFixture() + eventProps.referrer = '' + eventProps.path = undefined as any + eventProps.title = null as any + const ctx = await ajs.page('My Event', eventProps) + const page = ctx.event.context!.page + expect(page).toEqual( + expect.objectContaining({ referrer: '', path: undefined, title: null }) + ) + }) + }) + + test('enriches page events with the page context', async () => { + const ctx = await ajs.page( + 'My event', + { banana: 'phone' }, + { page: { url: 'not-localhost' } } + ) + + expect(ctx.event.context?.page).toMatchInlineSnapshot(` + Object { + "path": "/", + "referrer": "", + "search": "", + "title": "", + "url": "not-localhost", + } + `) + }) + test('enriches page events using properties', async () => { + const ctx = await ajs.page('My event', { banana: 'phone', referrer: 'foo' }) + + expect(ctx.event.context?.page).toMatchInlineSnapshot(` + Object { + "path": "/", + "referrer": "foo", + "search": "", + "title": "", + "url": "http://localhost/", + } + `) + }) + + test('in page events, event.name overrides event.properties.name', async () => { + const ctx = await ajs.page('My Event', undefined, undefined, { + name: 'some propery name', + }) + expect(ctx.event.properties!.name).toBe('My Event') + }) + + test('in non-page events, event.name does not override event.properties.name', async () => { + const ctx = await ajs.track('My Event', { + name: 'some propery name', + }) + expect(ctx.event.properties!.name).toBe('some propery name') + }) + + test('enriches identify events with the page context', async () => { + const ctx = await ajs.identify('Netto', { + banana: 'phone', + }) + + expect(ctx.event.context?.page).toMatchInlineSnapshot(` + Object { + "path": "/", + "referrer": "", + "search": "", + "title": "", + "url": "http://localhost/", + } + `) + }) + + test('runs before any other plugin', async () => { + let called = false + + await ajs.addSourceMiddleware(({ payload, next }) => { + called = true + expect(payload.obj?.context?.page).not.toBeFalsy() + next(payload) + }) + + await ajs.track('My event', { + banana: 'phone', + }) + + expect(called).toBe(true) + }) +}) diff --git a/packages/browser/src/browser/index.ts b/packages/browser/src/browser/index.ts index dd70fceab..987d64eac 100644 --- a/packages/browser/src/browser/index.ts +++ b/packages/browser/src/browser/index.ts @@ -30,7 +30,7 @@ import { ClassicIntegrationSource } from '../plugins/ajs-destination/types' import { attachInspector } from '../core/inspector' import { Stats } from '../core/stats' import { setGlobalAnalyticsKey } from '../lib/global-analytics-helper' -import { pageEnrichment } from '../plugins/page-enrichment' +import { envEnrichment } from '../plugins/env-enrichment' export interface LegacyIntegrationConfiguration { /* @deprecated - This does not indicate browser types anymore */ @@ -246,7 +246,7 @@ async function registerPlugins( ).catch(() => []) const toRegister = [ - pageEnrichment, + envEnrichment, validation, ...plugins, ...legacyDestinations, diff --git a/packages/browser/src/plugins/page-enrichment/__tests__/index.test.ts b/packages/browser/src/plugins/env-enrichment/__tests__/index.test.ts similarity index 97% rename from packages/browser/src/plugins/page-enrichment/__tests__/index.test.ts rename to packages/browser/src/plugins/env-enrichment/__tests__/index.test.ts index 74124fb02..af2215320 100644 --- a/packages/browser/src/plugins/page-enrichment/__tests__/index.test.ts +++ b/packages/browser/src/plugins/env-enrichment/__tests__/index.test.ts @@ -1,7 +1,7 @@ import cookie from 'js-cookie' import assert from 'assert' import { Analytics } from '../../../core/analytics' -import { pageEnrichment } from '..' +import { envEnrichment } from '..' import { pick } from '../../../lib/pick' import { SegmentioSettings } from '../../segmentio' import { version } from '../../../generated/version' @@ -13,11 +13,6 @@ import { } from '../../../test-helpers/fixtures/client-hints' import { getDefaultPageContext } from '../../../core/page' -/** - * TODO: move this to top level tests and rename to page-enrichment.integration.test.ts - * These tests test functionality outside of the plugin (e.g. addPageContext) - */ - let ajs: Analytics const helpers = { @@ -46,7 +41,7 @@ describe('Page Enrichment', () => { writeKey: 'abc_123', }) - await ajs.register(pageEnrichment) + await ajs.register(envEnrichment) }) test('enriches page calls', async () => { @@ -252,7 +247,7 @@ describe('Other visitor metadata', () => { options = { apiKey: 'foo' } analytics = new Analytics({ writeKey: options.apiKey }) - await analytics.register(pageEnrichment) + await analytics.register(envEnrichment) }) afterEach(() => { @@ -514,7 +509,7 @@ describe('Other visitor metadata', () => { { disableClientPersistence: true } ) - await analytics.register(pageEnrichment) + await analytics.register(envEnrichment) const ctx = await analytics.track( 'test', diff --git a/packages/browser/src/plugins/page-enrichment/index.ts b/packages/browser/src/plugins/env-enrichment/index.ts similarity index 97% rename from packages/browser/src/plugins/page-enrichment/index.ts rename to packages/browser/src/plugins/env-enrichment/index.ts index 18c24f389..def704ad4 100644 --- a/packages/browser/src/plugins/page-enrichment/index.ts +++ b/packages/browser/src/plugins/env-enrichment/index.ts @@ -119,7 +119,7 @@ const objectToQueryString = (obj: Record): string => { } } -class PageEnrichmentPlugin implements Plugin { +class EnvironmentEnrichmentPlugin implements Plugin { private instance!: Analytics private userAgentData: UADataValues | undefined @@ -194,4 +194,4 @@ class PageEnrichmentPlugin implements Plugin { screen = this.enrich } -export const pageEnrichment = new PageEnrichmentPlugin() +export const envEnrichment = new EnvironmentEnrichmentPlugin() diff --git a/packages/browser/src/plugins/segmentio/__tests__/index.test.ts b/packages/browser/src/plugins/segmentio/__tests__/index.test.ts index fdb9be1d8..ec60ad350 100644 --- a/packages/browser/src/plugins/segmentio/__tests__/index.test.ts +++ b/packages/browser/src/plugins/segmentio/__tests__/index.test.ts @@ -3,7 +3,7 @@ import unfetch from 'unfetch' import { segmentio, SegmentioSettings } from '..' import { Analytics } from '../../../core/analytics' import { Plugin } from '../../../core/plugin' -import { pageEnrichment } from '../../page-enrichment' +import { envEnrichment } from '../../env-enrichment' import cookie from 'js-cookie' jest.mock('unfetch', () => { @@ -24,7 +24,7 @@ describe('Segment.io', () => { analytics = new Analytics({ writeKey: options.apiKey }) segment = await segmentio(analytics, options, {}) - await analytics.register(segment, pageEnrichment) + await analytics.register(segment, envEnrichment) window.localStorage.clear() @@ -55,7 +55,7 @@ describe('Segment.io', () => { } const analytics = new Analytics({ writeKey: options.apiKey }) const segment = await segmentio(analytics, options, {}) - await analytics.register(segment, pageEnrichment) + await analytics.register(segment, envEnrichment) // @ts-ignore test a valid ajsc page call await analytics.page(null, { foo: 'bar' }) diff --git a/packages/browser/src/plugins/segmentio/__tests__/retries.test.ts b/packages/browser/src/plugins/segmentio/__tests__/retries.test.ts index e73ce2ec9..0360b8aa1 100644 --- a/packages/browser/src/plugins/segmentio/__tests__/retries.test.ts +++ b/packages/browser/src/plugins/segmentio/__tests__/retries.test.ts @@ -3,7 +3,7 @@ import { Analytics } from '../../../core/analytics' // @ts-ignore isOffline mocked dependency is accused as unused import { isOffline } from '../../../core/connection' import { Plugin } from '../../../core/plugin' -import { pageEnrichment } from '../../page-enrichment' +import { envEnrichment } from '../../env-enrichment' import { scheduleFlush } from '../schedule-flush' import * as PPQ from '../../../lib/priority-queue/persisted' import * as PQ from '../../../lib/priority-queue' @@ -59,7 +59,7 @@ describe('Segment.io retries', () => { segment = await segmentio(analytics, options, {}) - await analytics.register(segment, pageEnrichment) + await analytics.register(segment, envEnrichment) }) test(`add events to the queue`, async () => { diff --git a/packages/browser/src/test-helpers/fixtures/create-fetch-method.ts b/packages/browser/src/test-helpers/fixtures/create-fetch-method.ts index 3ba5b64c2..85cd7e41e 100644 --- a/packages/browser/src/test-helpers/fixtures/create-fetch-method.ts +++ b/packages/browser/src/test-helpers/fixtures/create-fetch-method.ts @@ -3,7 +3,7 @@ import { createSuccess } from '../factories' import { cdnSettingsMinimal } from './cdn-settings' export const createMockFetchImplementation = ( - cdnSettings: Partial = {} + cdnSettings: Partial = cdnSettingsMinimal ) => { return (...[url, req]: Parameters) => { const reqUrl = url.toString()