Ctrl K
ComunidadGuías…

tests/builder-onboarding-telemetry.unit.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { trackBuilderOnboardingStep } from '../cli/src/build/onboarding/telemetry.ts'

const sendEventMock = vi.hoisted(() => vi.fn())

vi.mock('../cli/src/utils.ts', () => ({
  defaultApiHost: 'https://api.capgo.app',
  getRemoteConfig: vi.fn().mockResolvedValue({ hostApi: 'https://api.capgo.app' }),
  sendEvent: sendEventMock,
}))

const base = { apikey: 'cap_test_key', platform: 'ios', appId: 'com.example.app', orgId: 'org-uuid-1' } as const

describe('trackBuilderOnboardingStep', () => {
  beforeEach(() => {
    sendEventMock.mockReset()
    sendEventMock.mockResolvedValue(undefined)
  })

  it('builds the expected payload and calls sendEvent once', async () => {
    await trackBuilderOnboardingStep({
      ...base,
      step: 'api-key-instructions',
      durationMs: 1234,
      durationStep: 'welcome',
    })

    expect(sendEventMock).toHaveBeenCalledTimes(1)
    const [calledKey, payload] = sendEventMock.mock.calls[0]
    expect(calledKey).toBe('cap_test_key')
    expect(payload).toMatchObject({
      event: 'Builder Onboarding Step',
      channel: 'builder-onboarding',
      org_id: 'org-uuid-1',
      tracking_version: 2,
      tags: {
        step: 'api-key-instructions',
        platform: 'ios',
        app_id: 'com.example.app',
        duration_ms: '1234',
        duration_step: 'welcome',
      },
    })
    expect(payload.tags.error_category).toBeUndefined()
  })

  it('includes error_category only when an error is provided', async () => {
    await trackBuilderOnboardingStep({
      ...base,
      step: 'error',
      error: Object.assign(new Error('Unauthorized'), { status: 401 }),
    })

    const [, payload] = sendEventMock.mock.calls[0]
    expect(payload.tags.error_category).toBe('apple_api_unauthorized')
  })

  it('uses the Android mapper when platform is android', async () => {
    await trackBuilderOnboardingStep({
      ...base,
      step: 'error',
      platform: 'android',
      error: Object.assign(new Error('Bad keystore'), { phase: 'keystore' }),
    })

    const [, payload] = sendEventMock.mock.calls[0]
    expect(payload.tags.error_category).toBe('keystore_invalid')
  })

  it('swallows errors thrown by sendEvent', async () => {
    sendEventMock.mockRejectedValueOnce(new Error('network down'))
    await expect(trackBuilderOnboardingStep({
      ...base,
      step: 'welcome',
    })).resolves.toBeUndefined()
  })

  it('does not include duration_ms when undefined', async () => {
    await trackBuilderOnboardingStep({
      ...base,
      step: 'welcome',
    })

    const [, payload] = sendEventMock.mock.calls[0]
    expect(payload.tags.duration_ms).toBeUndefined()
  })

  it('uses pre-computed errorCategory when provided (skipping the mapper)', async () => {
    await trackBuilderOnboardingStep({
      ...base,
      step: 'error',
      errorCategory: 'profile_creation_failed',
      // error intentionally omitted
    })

    const [, payload] = sendEventMock.mock.calls[0]
    expect(payload.tags.error_category).toBe('profile_creation_failed')
  })
})