Files
zeekay e43ab1afe7 chore: rename all PostHog references to Insights/Hanzo
Bulk rename across 989 files:
- Package names: posthog-js → @hanzo/insights, posthog-node → @hanzo/insights-node, etc.
- Repository URLs: posthog/posthog-js → hanzoai/insights-js
- Brand names: PostHog → Insights
- File renames: 259 reference JSON files, source files, workflow files
- Directory rename: eslint-plugin-posthog-js → eslint-plugin-insights

Remaining: 8 refs to @posthog/rrweb-* (real npm deps, need fork+publish)
2026-03-06 01:41:12 -08:00

290 lines
8.1 KiB
TypeScript

/**
* @jest-environment jsdom
*/
import { waitForPromises } from '@hanzo/insights-core/testing'
import { Insights } from '../src'
describe('InsightsWeb', () => {
let fetch: jest.Mock
jest.useRealTimers()
beforeEach(() => {
;(global as any).window.fetch = fetch = jest.fn(async (url) => {
let res: any = { status: 'ok' }
if (url.includes('flags')) {
res = {
featureFlags: {
'feature-1': true,
'feature-2': true,
'json-payload': true,
'feature-variant': 'variant',
},
// NOTE: Aren't these supposed to be strings?
featureFlagPayloads: {
'feature-1': {
color: 'blue',
},
'json-payload': {
a: 'payload',
},
'feature-variant': 5,
},
}
}
return {
status: 200,
json: () => Promise.resolve(res),
}
})
})
describe('init', () => {
it('should initialise', async () => {
const insights = new Insights('TEST_API_KEY', {
flushAt: 1,
})
expect(insights.optedOut).toEqual(false)
insights.capture('test')
await waitForPromises()
expect(fetch).toHaveBeenCalledTimes(2)
})
it('should load feature flags on init', async () => {
const insights = new Insights('TEST_API_KEY', {
flushAt: 1,
})
await waitForPromises()
expect(fetch).toHaveBeenCalledWith('https://us.i.insights.com/flags/?v=2', {
body: JSON.stringify({
token: 'TEST_API_KEY',
distinct_id: insights.getDistinctId(),
groups: {},
person_properties: {},
group_properties: {},
$anon_distinct_id: insights.getAnonymousId(),
}),
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
signal: expect.anything(),
})
expect(insights.getFeatureFlags()).toEqual({
'feature-1': true,
'feature-2': true,
'json-payload': true,
'feature-variant': 'variant',
})
expect(insights.getFeatureFlagPayloads()).toEqual({
'feature-1': {
color: 'blue',
},
'json-payload': {
a: 'payload',
},
'feature-variant': 5,
})
})
})
describe('History API tracking', () => {
const originalPushState = window.history.pushState
const originalReplaceState = window.history.replaceState
let setPathname: (pathname: string) => void
beforeEach(() => {
const mockLocation = {
pathname: '/initial-path',
}
Object.defineProperty(window, 'location', {
value: mockLocation,
writable: true,
})
setPathname = (pathname: string) => {
mockLocation.pathname = pathname
}
})
// Reset history methods after each test
afterEach(() => {
window.history.pushState = originalPushState
window.history.replaceState = originalReplaceState
const popstateHandler = (): void => {}
window.addEventListener('popstate', popstateHandler)
window.removeEventListener('popstate', popstateHandler)
})
it('should not patch history methods when captureHistoryEvents is disabled', () => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const insights = new Insights('TEST_API_KEY', {
captureHistoryEvents: false,
})
expect(window.history.pushState).toBe(originalPushState)
expect(window.history.replaceState).toBe(originalReplaceState)
})
it('should patch history methods when captureHistoryEvents is enabled', () => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const insights = new Insights('TEST_API_KEY', {
captureHistoryEvents: true,
})
expect(window.history.pushState).not.toBe(originalPushState)
expect(window.history.replaceState).not.toBe(originalReplaceState)
})
it('should capture pageview events on pushState when pathname changes', async () => {
const insights = new Insights('TEST_API_KEY', {
captureHistoryEvents: true,
flushAt: 1,
})
const captureSpy = jest.spyOn(insights, 'capture')
// Change pathname
setPathname('/test-page')
window.history.pushState({}, '', '/test-page')
expect(captureSpy).toHaveBeenCalledWith(
'$pageview',
expect.objectContaining({
navigation_type: 'pushState',
})
)
})
it('should not capture pageview events on pushState when pathname does not change', async () => {
const insights = new Insights('TEST_API_KEY', {
captureHistoryEvents: true,
flushAt: 1,
})
const captureSpy = jest.spyOn(insights, 'capture')
captureSpy.mockClear()
// Don't change pathname
window.history.pushState({}, '', '/initial-path')
expect(captureSpy).not.toHaveBeenCalled()
})
it('should capture pageview events on replaceState when pathname changes', async () => {
const insights = new Insights('TEST_API_KEY', {
captureHistoryEvents: true,
flushAt: 1,
})
const captureSpy = jest.spyOn(insights, 'capture')
// Change pathname
setPathname('/replaced-page')
window.history.replaceState({}, '', '/replaced-page')
expect(captureSpy).toHaveBeenCalledWith(
'$pageview',
expect.objectContaining({
navigation_type: 'replaceState',
})
)
})
it('should not capture pageview events on replaceState when pathname does not change', async () => {
const insights = new Insights('TEST_API_KEY', {
captureHistoryEvents: true,
flushAt: 1,
})
const captureSpy = jest.spyOn(insights, 'capture')
captureSpy.mockClear()
// Don't change pathname
window.history.replaceState({}, '', '/initial-path')
expect(captureSpy).not.toHaveBeenCalled()
})
it('should capture pageview events on popstate when pathname changes', async () => {
const insights = new Insights('TEST_API_KEY', {
captureHistoryEvents: true,
flushAt: 1,
})
const captureSpy = jest.spyOn(insights, 'capture')
// Change pathname
setPathname('/popstate-page')
window.dispatchEvent(new Event('popstate'))
expect(captureSpy).toHaveBeenCalledWith(
'$pageview',
expect.objectContaining({
navigation_type: 'popstate',
})
)
})
it('should not capture pageview events on popstate when pathname does not change', async () => {
const insights = new Insights('TEST_API_KEY', {
captureHistoryEvents: true,
flushAt: 1,
})
const captureSpy = jest.spyOn(insights, 'capture')
captureSpy.mockClear()
// Don't change pathname
window.dispatchEvent(new Event('popstate'))
expect(captureSpy).not.toHaveBeenCalled()
})
it('should include navigation properties in capture call and rely on getCommonEventProperties', async () => {
const insights = new Insights('TEST_API_KEY', {
captureHistoryEvents: true,
flushAt: 1,
})
const commonEventProps = { $lib: '@hanzo/insights-lite', $lib_version: '1.0.0' }
const getCommonEventPropertiesSpy = jest
.spyOn(insights, 'getCommonEventProperties')
.mockImplementation(() => commonEventProps)
const coreCaptureMethod = jest.spyOn(Insights.prototype, 'capture')
// Change pathname
setPathname('/captured-page')
window.history.pushState({}, '', '/captured-page')
// Will use a mock here for now and rely on the implementation since the tests setup is very simple at the moment
expect(getCommonEventPropertiesSpy).toHaveBeenCalled()
const captureCall = coreCaptureMethod.mock.calls.find(
(call) => call[0] === '$pageview' && call[1] && call[1].navigation_type === 'pushState'
)
expect(captureCall).toBeDefined()
const navigationProperties = {
navigation_type: 'pushState',
}
if (captureCall) {
expect(captureCall[1]).toMatchObject(navigationProperties)
}
})
})
})