Skip to content

Commit

Permalink
fers/me/projects/analytics-next'
Browse files Browse the repository at this point in the history
node 'node_modules/.bin/jest'
'/Users/me/projects/analytics-next/packages/browser/src/browser/__tests__/integration.test.ts'
-c '/Users/me/projects/analytics-next/packages/browser/jest.config.js'
-t 'reset anonymousId is set if a track event is called during pre-init
period'
x analytics reset no longer clears anonymousId bug:wq
  • Loading branch information
silesky committed Nov 4, 2022
1 parent 975fcf0 commit 4e3a058
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 37 deletions.
49 changes: 49 additions & 0 deletions packages/browser/src/browser/__tests__/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import {
} from '../../test-helpers/test-writekeys'
import { PriorityQueue } from '../../lib/priority-queue'
import { getCDN, setGlobalCDNUrl } from '../../lib/parse-cdn'
import {
clearAjsBrowserStorage,
getAjsBrowserStorage,
} from '../../test-helpers/browser-storage'

// eslint-disable-next-line @typescript-eslint/no-explicit-any
let fetchCalls: Array<any>[] = []
Expand Down Expand Up @@ -612,6 +616,51 @@ describe('setAnonymousId', () => {
})
})

describe('reset', () => {
beforeEach(() => {
clearAjsBrowserStorage()
})
afterAll(() => {
clearAjsBrowserStorage()
})
describe('anonymousId', () => {
const getAnonId = () => getAjsBrowserStorage().ajs_anonymous_id

it('is set if an event like track is called during pre-init period', async () => {
const analytics = AnalyticsBrowser.load({ writeKey })
const ctx = await analytics.track('foo')
const id = getAnonId()
expect(id).toBeDefined()
})

it('is set if an event like track is called during pre-init period', async () => {
const analytics = AnalyticsBrowser.load({ writeKey })
const ctx = await analytics.track('foo')
const id = getAnonId()
expect(id).toBeDefined()
})

it('clears anonId if reset is called during pre-init period', async () => {
const analytics = AnalyticsBrowser.load({ writeKey })
const track = analytics.track('foo')
const reset = analytics.reset()
await Promise.all([track, reset])
expect(getAnonId()).toBeFalsy()
})

it('clears anonId if reset is called after initialization is complete', async () => {
const [analytics] = await AnalyticsBrowser.load({ writeKey })
expect(getAnonId()).toBeFalsy()
const track = analytics.track('foo')
expect(typeof getAnonId()).toBe('string')
analytics.reset()
expect(getAnonId()).toBeFalsy()
await track
expect(getAnonId()).toBeFalsy()
})
})
})

describe('addSourceMiddleware', () => {
it('supports registering source middlewares', async () => {
const [analytics] = await AnalyticsBrowser.load({
Expand Down
17 changes: 11 additions & 6 deletions packages/browser/src/core/analytics/__tests__/integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { PriorityQueue } from '../../../lib/priority-queue'
import { MiddlewareParams } from '../../../plugins/middleware'
import { retrieveStoredData } from '../../../test-helpers/retrieve-stored-data'
import {
getAjsBrowserStorage,
clearAjsBrowserStorage,
} from '../../../test-helpers/browser-storage'
import { Context } from '../../context'
import { Plugin } from '../../plugin'
import { EventQueue } from '../../queue/event-queue'
Expand Down Expand Up @@ -224,20 +227,22 @@ describe('Analytics', () => {
})

describe('reset', () => {
beforeEach(() => {
clearAjsBrowserStorage()
})

it('clears user and group data', async () => {
const analytics = new Analytics({ writeKey: '' })

const cookieNames = ['ajs_user_id', 'ajs_anonymous_id', 'ajs_group_id']
const localStorageKeys = ['ajs_user_traits', 'ajs_group_properties']

analytics.user().anonymousId('unknown-user')
analytics.user().id('known-user')
analytics.user().traits({ job: 'engineer' })
analytics.group().id('known-group')
analytics.group().traits({ team: 'analytics' })

// Ensure all cookies/localstorage is written correctly first
let storedData = retrieveStoredData({ cookieNames, localStorageKeys })

let storedData = getAjsBrowserStorage()
expect(storedData).toEqual({
ajs_user_id: 'known-user',
ajs_anonymous_id: 'unknown-user',
Expand All @@ -252,7 +257,7 @@ describe('Analytics', () => {

// Now make sure everything was cleared on reset
analytics.reset()
storedData = retrieveStoredData({ cookieNames, localStorageKeys })
storedData = getAjsBrowserStorage()
expect(storedData).toEqual({})
})
})
Expand Down
42 changes: 42 additions & 0 deletions packages/browser/src/core/events/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,48 @@ describe('Event Factory', () => {
innerProp: '👻',
})
})

describe.skip('anonymousId', () => {
// TODO: the code should be fixed so that these tests can pass -- this eventFactory does not seem to handle these edge cases well.
// When an event is dispatched, there are four places anonymousId can live: event.anonymousId, event.options.anonymousId, event.context.anonymousId, and the user object / localStorage.
// It would be good to have a source of truth
test('accepts an anonymousId', () => {
const track = factory.track('Order Completed', shoes, {
anonymousId: 'foo',
})
expect(track.anonymousId).toBe('foo')
expect(track.context?.anonymousId).toBe('foo')
})

test('custom passed anonymousId should set global user instance', () => {
const id = Math.random().toString()
factory.track('Order Completed', shoes, {
anonymousId: id,
})
expect(user.anonymousId()).toBe(id)
})

test('if two different anonymousIds are passed, should use one on the event', () => {
const track = factory.track('Order Completed', shoes, {
anonymousId: 'bar',
context: {
anonymousId: 'foo',
},
})
expect(track.context?.anonymousId).toBe('bar')
expect(track.anonymousId).toBe('bar')
})

test('should set an anonymousId passed from the context on the event', () => {
const track = factory.track('Order Completed', shoes, {
context: {
anonymousId: 'foo',
},
})
expect(track.context?.anonymousId).toBe('foo')
expect(track.anonymousId).toBe('foo')
})
})
})

describe('normalize', function () {
Expand Down
9 changes: 9 additions & 0 deletions packages/browser/src/core/events/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,15 @@ export class EventFactory {
}

public normalize(event: SegmentEvent): SegmentEvent {
const anonymousIdOverride = event.options?.anonymousId
if (anonymousIdOverride) {
// set anonymousId globally if we encounter an override
//segment.com/docs/connections/sources/catalog/libraries/website/javascript/identity/#override-the-anonymous-id-using-the-options-object
const id = this.user.anonymousId(anonymousIdOverride)
// sync the user to the property on the event itself
event.anonymousId = id
}

const integrationBooleans = Object.keys(event.integrations ?? {}).reduce(
(integrationNames, name) => {
return {
Expand Down
4 changes: 2 additions & 2 deletions packages/browser/src/plugins/segmentio/normalize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ export function normalize(

json.context = json.context ?? json.options ?? {}
const ctx = json.context
const anonId = json.anonymousId

delete json.options
json.writeKey = settings?.apiKey
Expand Down Expand Up @@ -159,7 +158,8 @@ export function normalize(
referrerId(query, ctx, analytics.options.disableClientPersistence ?? false)

json.userId = json.userId || user.id()
json.anonymousId = user.anonymousId(anonId)
json.anonymousId = json.anonymousId || user.anonymousId()

json.sentAt = new Date()

const failed = analytics.queue.failedInitializations || []
Expand Down
70 changes: 70 additions & 0 deletions packages/browser/src/test-helpers/browser-storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import cookie from 'js-cookie'

const ajsCookieNames = [
'ajs_user_id',
'ajs_anonymous_id',
'ajs_group_id',
] as const
const ajsLocalStorageKeys = ['ajs_user_traits', 'ajs_group_properties'] as const

export const getAjsBrowserStorage = () => {
return getBrowserStorage({
cookieNames: ajsCookieNames,
localStorageKeys: ajsLocalStorageKeys,
})
}

export const clearAjsBrowserStorage = () => {
return clearBrowserStorage({
cookieNames: ajsCookieNames,
localStorageKeys: ajsLocalStorageKeys,
})
}

export function getBrowserStorage<
CookieNames extends string,
LSKeys extends string
>({
cookieNames,
localStorageKeys,
}: {
cookieNames: readonly CookieNames[]
localStorageKeys: readonly LSKeys[]
}): Record<CookieNames | LSKeys, string | {}> {
const result = {} as ReturnType<typeof getBrowserStorage>

const cookies = cookie.get()
cookieNames.forEach((name) => {
if (name in cookies) {
result[name] = cookies[name]
}
})

localStorageKeys.forEach((key) => {
const value = localStorage.getItem(key)
if (value !== null && typeof value !== 'undefined') {
result[key] = JSON.parse(value)
}
})

return result
}

export function clearBrowserStorage({
cookieNames,
localStorageKeys, // if no keys are passed, the entire thing is cleared
}: {
cookieNames: string[] | readonly string[]
localStorageKeys?: string[] | readonly string[]
}) {
cookieNames.forEach((name) => {
cookie.remove(name)
})
if (!localStorageKeys) {
localStorage.clear()
} else {
localStorageKeys.forEach((key) => {
localStorage.removeItem(key)
})
}
}
29 changes: 0 additions & 29 deletions packages/browser/src/test-helpers/retrieve-stored-data.ts

This file was deleted.

0 comments on commit 4e3a058

Please sign in to comment.