Files
zeekay d57cb038bd 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

310 lines
11 KiB
TypeScript

import { expect, test } from '../utils/insights-playwright-test-base'
import { createTour, tourTooltip, startWithTours, captureEvent } from './utils'
test.describe('product tours - auto triggers', () => {
test('shows immediately when auto_launch with no event triggers', async ({ page, context }) => {
const tour = createTour({ id: 'auto-immediate', auto_launch: true })
await startWithTours(page, context, [tour])
await expect(tourTooltip(page, 'auto-immediate')).toBeVisible({ timeout: 5000 })
})
test('does not show when auto_launch is false', async ({ page, context }) => {
const tour = createTour({ id: 'no-auto', auto_launch: false })
await startWithTours(page, context, [tour])
await page.waitForTimeout(2000)
await expect(tourTooltip(page, 'no-auto')).not.toBeVisible()
})
test('waits for event trigger before showing', async ({ page, context }) => {
const tour = createTour({
id: 'event-trigger',
auto_launch: true,
conditions: {
events: { values: [{ name: 'trigger_tour_event' }] },
},
})
await startWithTours(page, context, [tour], { waitForApiResponse: true })
await expect(tourTooltip(page, 'event-trigger')).not.toBeVisible()
await captureEvent(page, 'trigger_tour_event')
await expect(tourTooltip(page, 'event-trigger')).toBeVisible({ timeout: 5000 })
})
test('respects autoShowDelaySeconds', async ({ page, context }) => {
const tour = createTour({
id: 'delayed-tour',
auto_launch: true,
conditions: {
autoShowDelaySeconds: 3,
events: { values: [{ name: 'delay_trigger' }] },
},
})
await startWithTours(page, context, [tour], { waitForApiResponse: true })
await captureEvent(page, 'delay_trigger')
await page.waitForTimeout(2000)
await expect(tourTooltip(page, 'delayed-tour')).not.toBeVisible()
await expect(tourTooltip(page, 'delayed-tour')).toBeVisible({ timeout: 5000 })
})
test('cancel event cancels pending delayed tour', async ({ page, context }) => {
const tour = createTour({
id: 'cancel-test',
auto_launch: true,
conditions: {
autoShowDelaySeconds: 5,
events: { values: [{ name: 'start_tour' }] },
cancelEvents: { values: [{ name: 'cancel_tour' }] },
},
})
await startWithTours(page, context, [tour], { waitForApiResponse: true })
await captureEvent(page, 'start_tour')
await page.waitForTimeout(2000)
await captureEvent(page, 'cancel_tour')
await page.waitForTimeout(4000)
await expect(tourTooltip(page, 'cancel-test')).not.toBeVisible()
})
test('selector click trigger shows tour', async ({ page, context }) => {
const tour = createTour({
id: 'click-trigger',
auto_launch: false,
conditions: { selector: '#click-trigger-btn' },
})
await startWithTours(page, context, [tour])
await page.waitForTimeout(2000)
await expect(tourTooltip(page, 'click-trigger')).not.toBeVisible()
await page.click('#click-trigger-btn')
await expect(tourTooltip(page, 'click-trigger')).toBeVisible({ timeout: 5000 })
})
test('click propagates when tour is deleted from API', async ({ page, context }) => {
const tour = createTour({
id: 'deleted-tour',
auto_launch: false,
conditions: { selector: '#click-trigger-btn' },
})
await startWithTours(page, context, [tour])
await page.evaluate(() => {
;(window as any).buttonClickCount = 0
const btn = document.getElementById('click-trigger-btn')
if (btn) {
// eslint-disable-next-line @hanzo/insights/no-add-event-listener
btn.addEventListener('click', () => {
;(window as any).buttonClickCount++
})
}
})
await page.waitForTimeout(2000)
// First click - tour shows, same-element handlers still fire
await page.click('#click-trigger-btn')
await expect(tourTooltip(page, 'deleted-tour')).toBeVisible({ timeout: 5000 })
expect(await page.evaluate(() => (window as any).buttonClickCount)).toBe(1)
// Dismiss the tour
await page.locator('.ph-product-tour-container-deleted-tour .ph-tour-dismiss').click()
await expect(tourTooltip(page, 'deleted-tour')).not.toBeVisible()
// Update the API to return no tours (simulating deletion)
await page.route('**/api/product_tours/**', async (route) => {
await route.fulfill({ json: { product_tours: [] } })
})
// Clear the cache to force a refresh
await page.evaluate(() => {
;(window as any).insights.productTours.clearCache()
})
// Wait for next evaluation cycle to pick up empty tours and clean up listeners
await page.waitForTimeout(2000)
// Click again - tour deleted, click still propagates
await page.click('#click-trigger-btn')
await expect(tourTooltip(page, 'deleted-tour')).not.toBeVisible()
expect(await page.evaluate(() => (window as any).buttonClickCount)).toBe(2)
})
test.describe('event property filtering', () => {
test('triggers when event properties match (exact)', async ({ page, context }) => {
const tour = createTour({
id: 'prop-filter-exact',
auto_launch: true,
conditions: {
events: {
values: [
{
name: 'filtered_event',
propertyFilters: {
plan: { values: ['enterprise'], operator: 'exact' },
},
},
],
},
},
})
await startWithTours(page, context, [tour], { waitForApiResponse: true })
await captureEvent(page, 'filtered_event', { plan: 'enterprise' })
await expect(tourTooltip(page, 'prop-filter-exact')).toBeVisible({ timeout: 5000 })
})
test('does not trigger when event properties do not match', async ({ page, context }) => {
const tour = createTour({
id: 'prop-filter-mismatch',
auto_launch: true,
conditions: {
events: {
values: [
{
name: 'filtered_event',
propertyFilters: {
plan: { values: ['enterprise'], operator: 'exact' },
},
},
],
},
},
})
await startWithTours(page, context, [tour], { waitForApiResponse: true })
await captureEvent(page, 'filtered_event', { plan: 'free' })
await page.waitForTimeout(2000)
await expect(tourTooltip(page, 'prop-filter-mismatch')).not.toBeVisible()
})
test('does not trigger when event is missing required property', async ({ page, context }) => {
const tour = createTour({
id: 'prop-filter-missing',
auto_launch: true,
conditions: {
events: {
values: [
{
name: 'filtered_event',
propertyFilters: {
plan: { values: ['enterprise'], operator: 'exact' },
},
},
],
},
},
})
await startWithTours(page, context, [tour], { waitForApiResponse: true })
await captureEvent(page, 'filtered_event', { other_prop: 'value' })
await page.waitForTimeout(2000)
await expect(tourTooltip(page, 'prop-filter-missing')).not.toBeVisible()
})
test('triggers with contains operator', async ({ page, context }) => {
const tour = createTour({
id: 'prop-filter-contains',
auto_launch: true,
conditions: {
events: {
values: [
{
name: 'filtered_event',
propertyFilters: {
email: { values: ['@company.com'], operator: 'icontains' },
},
},
],
},
},
})
await startWithTours(page, context, [tour], { waitForApiResponse: true })
await captureEvent(page, 'filtered_event', { email: '[email protected]' })
await expect(tourTooltip(page, 'prop-filter-contains')).toBeVisible({ timeout: 5000 })
})
test('triggers with is_not operator', async ({ page, context }) => {
const tour = createTour({
id: 'prop-filter-is-not',
auto_launch: true,
conditions: {
events: {
values: [
{
name: 'filtered_event',
propertyFilters: {
status: { values: ['inactive'], operator: 'is_not' },
},
},
],
},
},
})
await startWithTours(page, context, [tour], { waitForApiResponse: true })
await captureEvent(page, 'filtered_event', { status: 'active' })
await expect(tourTooltip(page, 'prop-filter-is-not')).toBeVisible({ timeout: 5000 })
})
test('multiple property filters must all match', async ({ page, context }) => {
const tour = createTour({
id: 'prop-filter-multi',
auto_launch: true,
conditions: {
events: {
values: [
{
name: 'filtered_event',
propertyFilters: {
plan: { values: ['enterprise'], operator: 'exact' },
region: { values: ['us', 'eu'], operator: 'exact' },
},
},
],
},
},
})
await startWithTours(page, context, [tour], { waitForApiResponse: true })
await captureEvent(page, 'filtered_event', { plan: 'enterprise', region: 'asia' })
await page.waitForTimeout(2000)
await expect(tourTooltip(page, 'prop-filter-multi')).not.toBeVisible()
await captureEvent(page, 'filtered_event', { plan: 'enterprise', region: 'us' })
await expect(tourTooltip(page, 'prop-filter-multi')).toBeVisible({ timeout: 5000 })
})
})
})