Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions packages/browser/src/__tests__/featureflags.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,136 @@ describe('featureflags', () => {
})
})

describe('device_id in flags requests', () => {
beforeEach(() => {
// Clear persistence before each test in this suite
instance.persistence.unregister('$device_id')
instance.persistence.unregister('$stored_person_properties')
instance.persistence.unregister('$stored_group_properties')

instance._send_request = jest.fn().mockImplementation(({ callback }) =>
callback({
statusCode: 200,
json: {
featureFlags: {
first: 'variant-1',
second: true,
},
},
})
)
})

afterEach(() => {
// Clean up after each test
instance.persistence.unregister('$device_id')
instance.persistence.unregister('$stored_person_properties')
instance.persistence.unregister('$stored_group_properties')
})

it('should include device_id in flags request when available', () => {
instance.persistence.register({
$device_id: 'test-device-uuid-123',
})

featureFlags.reloadFeatureFlags()
jest.runAllTimers()

expect(instance._send_request).toHaveBeenCalledTimes(1)
expect(instance._send_request.mock.calls[0][0].data).toEqual({
token: 'random fake token',
distinct_id: 'blah id',
$device_id: 'test-device-uuid-123',
person_properties: {},
})
})

it('should omit device_id when it is null (cookieless mode)', () => {
instance.persistence.register({
$device_id: null,
})

featureFlags.reloadFeatureFlags()
jest.runAllTimers()

expect(instance._send_request).toHaveBeenCalledTimes(1)
expect(instance._send_request.mock.calls[0][0].data).toEqual({
token: 'random fake token',
distinct_id: 'blah id',
person_properties: {},
})
expect(instance._send_request.mock.calls[0][0].data).not.toHaveProperty('$device_id')
})

it('should omit device_id when it is undefined', () => {
// Don't register device_id at all
featureFlags.reloadFeatureFlags()
jest.runAllTimers()

expect(instance._send_request).toHaveBeenCalledTimes(1)
expect(instance._send_request.mock.calls[0][0].data).toEqual({
token: 'random fake token',
distinct_id: 'blah id',
person_properties: {},
})
expect(instance._send_request.mock.calls[0][0].data).not.toHaveProperty('$device_id')
})

it('should include device_id along with $anon_distinct_id on identify', () => {
instance.persistence.register({
$device_id: 'device-uuid-456',
})

featureFlags.setAnonymousDistinctId('anon_id_789')
featureFlags.reloadFeatureFlags()
jest.runAllTimers()

expect(instance._send_request).toHaveBeenCalledTimes(1)
expect(instance._send_request.mock.calls[0][0].data).toEqual({
token: 'random fake token',
distinct_id: 'blah id',
$device_id: 'device-uuid-456',
$anon_distinct_id: 'anon_id_789',
person_properties: {},
})
})

it('should include device_id with person_properties', () => {
instance.persistence.register({
$device_id: 'device-uuid-999',
})

featureFlags.setPersonPropertiesForFlags({ plan: 'pro', beta_tester: true })
jest.runAllTimers()

expect(instance._send_request).toHaveBeenCalledTimes(1)
expect(instance._send_request.mock.calls[0][0].data).toEqual({
token: 'random fake token',
distinct_id: 'blah id',
$device_id: 'device-uuid-999',
person_properties: { plan: 'pro', beta_tester: true },
})
})

it('should include device_id with group_properties', () => {
instance.persistence.register({
$device_id: 'device-uuid-888',
})

featureFlags.setGroupPropertiesForFlags({ company: { name: 'Acme', seats: 50 } })
jest.runAllTimers()

expect(instance._send_request).toHaveBeenCalledTimes(1)
expect(instance._send_request.mock.calls[0][0].data).toEqual({
token: 'random fake token',
distinct_id: 'blah id',
$device_id: 'device-uuid-888',
person_properties: {},
group_properties: { company: { name: 'Acme', seats: 50 } },
})
})
})

describe('reloadFeatureFlags', () => {
beforeEach(() => {
instance._send_request = jest.fn().mockImplementation(({ callback }) =>
Expand Down
7 changes: 7 additions & 0 deletions packages/browser/src/posthog-featureflags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,8 @@
return
}
const token = this._instance.config.token
const deviceId = this._instance.get_property('$device_id')

const data: Record<string, any> = {
token: token,
distinct_id: this._instance.get_distinct_id(),
Expand All @@ -406,6 +408,11 @@
group_properties: this._instance.get_property(STORED_GROUP_PROPERTIES_KEY),
}

// Add device_id if available (handle cookieless mode where it's null)
if (deviceId !== null && deviceId !== undefined) {

Check failure on line 412 in packages/browser/src/posthog-featureflags.ts

View workflow job for this annotation

GitHub Actions / Lint

Use isUndefined() instead of direct undefined checks

Check failure on line 412 in packages/browser/src/posthog-featureflags.ts

View workflow job for this annotation

GitHub Actions / Lint

Use isNull() instead of direct null checks
data.$device_id = deviceId
}

if (options?.disableFlags || this._instance.config.advanced_disable_feature_flags) {
data.disable_flags = true
}
Expand Down
Loading