Skip to content

Commit

Permalink
feat: Refactor GDPR stuff into a more readable consent class (#1176)
Browse files Browse the repository at this point in the history
  • Loading branch information
benjackwhite authored May 28, 2024
1 parent 21014ca commit 02eb718
Show file tree
Hide file tree
Showing 10 changed files with 350 additions and 928 deletions.
184 changes: 184 additions & 0 deletions src/__tests__/consent.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import _posthog, { PostHog, PostHogConfig } from '../loader-module'
import { uuidv7 } from '../uuidv7'

import { isNull } from '../utils/type-utils'
import { document, assignableWindow, navigator } from '../utils/globals'

const DEFAULT_PERSISTENCE_PREFIX = `__ph_opt_in_out_`
const CUSTOM_PERSISTENCE_PREFIX = `𝓶𝓶𝓶𝓬𝓸𝓸𝓴𝓲𝓮𝓼`

function deleteAllCookies() {
const cookies = document!.cookie.split(';')

for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i]
const eqPos = cookie.indexOf('=')
const name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie
document!.cookie = name + '=;expires=Thu, 01 Jan 1970 00:00:00 GMT'
}
}

describe('consentManager', () => {
const createPostHog = (config: Partial<PostHogConfig> = {}) => {
const posthog = _posthog.init('testtoken', { ...config }, uuidv7())!
posthog.debug()
return posthog
}

let posthog: PostHog

beforeEach(() => {
posthog = createPostHog()
posthog.reset()
})

afterEach(() => {
document!.getElementsByTagName('html')[0].innerHTML = ''
assignableWindow.localStorage.clear()
deleteAllCookies()
})

it('should start default opted in', () => {
expect(posthog.has_opted_in_capturing()).toBe(true)
expect(posthog.has_opted_out_capturing()).toBe(false)

expect(posthog.persistence?.disabled).toBe(false)
expect(posthog.sessionPersistence?.disabled).toBe(false)
})

it('should start default opted out if setting given', () => {
posthog = createPostHog({ opt_out_capturing_by_default: true })
expect(posthog.has_opted_in_capturing()).toBe(false)
expect(posthog.has_opted_out_capturing()).toBe(true)

expect(posthog.persistence?.disabled).toBe(false)
expect(posthog.sessionPersistence?.disabled).toBe(false)
})

it('should start default opted out if setting given and disable storage', () => {
posthog = createPostHog({ opt_out_capturing_by_default: true, opt_out_persistence_by_default: true })
expect(posthog.has_opted_in_capturing()).toBe(false)
expect(posthog.has_opted_out_capturing()).toBe(true)

expect(posthog.persistence?.disabled).toBe(true)
expect(posthog.sessionPersistence?.disabled).toBe(true)
})

it('should enable or disable persistence when changing opt out status', () => {
posthog = createPostHog({ opt_out_capturing_by_default: true, opt_out_persistence_by_default: true })
expect(posthog.has_opted_in_capturing()).toBe(false)
expect(posthog.persistence?.disabled).toBe(true)

posthog.opt_in_capturing()
expect(posthog.has_opted_in_capturing()).toBe(true)
expect(posthog.persistence?.disabled).toBe(false)

posthog.opt_out_capturing()
expect(posthog.has_opted_in_capturing()).toBe(false)
expect(posthog.persistence?.disabled).toBe(true)
})

describe('with do not track setting', () => {
beforeEach(() => {
;(navigator as any).doNotTrack = '1'
})

it('should respect it if explicitly set', () => {
posthog = createPostHog({ respect_dnt: true })
expect(posthog.has_opted_in_capturing()).toBe(false)
})

it('should not respect it if not explicitly set', () => {
expect(posthog.has_opted_in_capturing()).toBe(true)
})
})

describe.each([`cookie`, `localStorage`] as PostHogConfig['opt_out_capturing_persistence_type'][])(
`%s`,
(persistenceType) => {
function assertPersistenceValue(
value: string | number | null,
persistencePrefix = DEFAULT_PERSISTENCE_PREFIX
) {
const token = posthog.config.token
const expected = persistencePrefix + token
if (persistenceType === `cookie`) {
if (isNull(value)) {
expect(document!.cookie).not.toContain(expected + `=`)
} else {
expect(document!.cookie).toContain(expected + `=${value}`)
}
} else {
if (isNull(value)) {
expect(assignableWindow.localStorage.getItem(expected)).toBeNull()
} else {
expect(assignableWindow.localStorage.getItem(expected)).toBe(`${value}`)
}
}
}

beforeEach(() => {
posthog = createPostHog({
opt_out_capturing_persistence_type: persistenceType,
persistence: persistenceType,
})
})

describe(`common consent functions`, () => {
it(`should set a persistent value marking the user as opted-in for a given token`, () => {
posthog.opt_in_capturing()
assertPersistenceValue(1)
})

it(`should set a persistent value marking the user as opted-out for a given token`, () => {
posthog.opt_out_capturing()
assertPersistenceValue(0)
})

it(`should capture an event recording the opt-in action`, () => {
const onCapture = jest.fn()
posthog.on('eventCaptured', onCapture)

posthog.opt_in_capturing()
expect(onCapture).toHaveBeenCalledWith(expect.objectContaining({ event: '$opt_in' }))

onCapture.mockClear()
const captureEventName = `єνєηт`
const captureProperties = { '𝖕𝖗𝖔𝖕𝖊𝖗𝖙𝖞': `𝓿𝓪𝓵𝓾𝓮` }

posthog.opt_in_capturing({ captureEventName, captureProperties })
expect(onCapture).toHaveBeenCalledWith(
expect.objectContaining({
event: captureEventName,
properties: expect.objectContaining(captureProperties),
})
)
})

it(`should allow use of a custom "persistence prefix" string (with correct default behavior)`, () => {
posthog = createPostHog({
opt_out_capturing_persistence_type: persistenceType,
opt_out_capturing_cookie_prefix: CUSTOM_PERSISTENCE_PREFIX,
})
posthog.opt_out_capturing()
posthog.opt_in_capturing()

assertPersistenceValue(null)
assertPersistenceValue(1, CUSTOM_PERSISTENCE_PREFIX)

posthog.opt_out_capturing()

assertPersistenceValue(null)
assertPersistenceValue(0, CUSTOM_PERSISTENCE_PREFIX)
})

it(`should clear the persisted value`, () => {
posthog.opt_in_capturing()
assertPersistenceValue(1)
posthog.reset()
assertPersistenceValue(null)
})
})
}
)
})
1 change: 1 addition & 0 deletions src/__tests__/extensions/replay/sessionrecording.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ describe('SessionRecording', () => {
sessionManager: sessionManager,
requestRouter: new RequestRouter({ config } as any),
_addCaptureHook: jest.fn(),
consent: { isOptedOut: () => false },
} as unknown as PostHog

// defaults
Expand Down
Loading

0 comments on commit 02eb718

Please sign in to comment.