diff --git a/src/renderer/src/components/setup-guide/use-setup-guide-telemetry.test.ts b/src/renderer/src/components/setup-guide/use-setup-guide-telemetry.test.ts index aa01fec94..8fcd2ebc6 100644 --- a/src/renderer/src/components/setup-guide/use-setup-guide-telemetry.test.ts +++ b/src/renderer/src/components/setup-guide/use-setup-guide-telemetry.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { afterEach, describe, expect, it, vi } from 'vitest' import type { FeatureWallSetupProgress } from '../feature-wall/feature-wall-setup-progress' import { FEATURE_WALL_SETUP_STEP_IDS, @@ -10,7 +10,6 @@ import { recordSetupGuideStepCompletionTelemetry } from './use-setup-guide-telemetry' -const consoleInfoMock = vi.hoisted(() => vi.fn()) const trackMock = vi.hoisted(() => vi.fn()) vi.mock('@/lib/telemetry', () => ({ @@ -18,17 +17,12 @@ vi.mock('@/lib/telemetry', () => ({ })) afterEach(() => { - consoleInfoMock.mockClear() trackMock.mockClear() vi.restoreAllMocks() vi.unstubAllGlobals() }) describe('setup guide step completion telemetry', () => { - beforeEach(() => { - vi.spyOn(console, 'info').mockImplementation(consoleInfoMock) - }) - it('seeds startup-hydrated completed steps without backfilling completion events', () => { vi.stubGlobal('localStorage', createMemoryStorage()) const state = createSetupGuideStepCompletionTelemetryState() @@ -72,8 +66,7 @@ describe('setup guide step completion telemetry', () => { setupGuideVisible: true }) - expect(consoleInfoMock).toHaveBeenCalledWith( - '[feature-education-telemetry:test-mode]', + expect(trackMock).toHaveBeenCalledWith( 'setup_guide_step_completed', { step_id: 'notifications', @@ -83,7 +76,6 @@ describe('setup guide step completion telemetry', () => { setup_guide_visible: true } ) - expect(trackMock).not.toHaveBeenCalled() }) }) diff --git a/src/renderer/src/lib/feature-education-telemetry.test.ts b/src/renderer/src/lib/feature-education-telemetry.test.ts index 9b7e60e13..7ec2bc6e2 100644 --- a/src/renderer/src/lib/feature-education-telemetry.test.ts +++ b/src/renderer/src/lib/feature-education-telemetry.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { afterEach, describe, expect, it, vi } from 'vitest' import { getSetupGuideStepSection, persistEmittedSetupGuideStepId, @@ -9,7 +9,6 @@ import { trackTerminalPaneSplit } from './feature-education-telemetry' -const consoleInfoMock = vi.hoisted(() => vi.fn()) const trackMock = vi.hoisted(() => vi.fn()) vi.mock('./telemetry', () => ({ @@ -17,7 +16,6 @@ vi.mock('./telemetry', () => ({ })) afterEach(() => { - consoleInfoMock.mockClear() trackMock.mockClear() vi.restoreAllMocks() vi.unstubAllGlobals() @@ -25,8 +23,6 @@ afterEach(() => { }) describe('feature education telemetry helpers', () => { - beforeEachConsoleInfo() - it('adds stable tour-depth fields to contextual tour outcomes', () => { trackContextualTourOutcome({ tourId: 'workspace-agent-sessions', @@ -38,7 +34,7 @@ describe('feature education telemetry helpers', () => { definedStepCount: 5 }) - expectLoggedFeatureEducationTelemetry('contextual_tour_outcome', { + expectTrackedFeatureEducationTelemetry('contextual_tour_outcome', { tour_id: 'workspace-agent-sessions', source: 'setup_guide_parallel_work', outcome: 'completed', @@ -47,7 +43,6 @@ describe('feature education telemetry helpers', () => { furthest_step_index: 5, defined_step_count: 5 }) - expect(trackMock).not.toHaveBeenCalled() }) it('omits stable tour-depth fields before any defined step is reached', () => { @@ -59,14 +54,13 @@ describe('feature education telemetry helpers', () => { totalSteps: 3 }) - expectLoggedFeatureEducationTelemetry('contextual_tour_outcome', { + expectTrackedFeatureEducationTelemetry('contextual_tour_outcome', { tour_id: 'workspace-agent-sessions', source: 'setup_guide_parallel_work', outcome: 'cancelled', steps_seen: 0, total_steps: 3 }) - expect(trackMock).not.toHaveBeenCalled() }) it('keeps setup guide close counts schema-valid if durable progress decreases', () => { @@ -79,7 +73,7 @@ describe('feature education telemetry helpers', () => { activeStepId: 'notifications' }) - expectLoggedFeatureEducationTelemetry('setup_guide_closed', { + expectTrackedFeatureEducationTelemetry('setup_guide_closed', { source: 'help_menu', outcome: 'dismissed', initial_completed_count: 4, @@ -87,7 +81,6 @@ describe('feature education telemetry helpers', () => { total_steps: 8, active_step_id: 'notifications' }) - expect(trackMock).not.toHaveBeenCalled() }) it('tracks setup guide step completion with bounded section and count fields', () => { @@ -98,14 +91,13 @@ describe('feature education telemetry helpers', () => { setupGuideVisible: true }) - expectLoggedFeatureEducationTelemetry('setup_guide_step_completed', { + expectTrackedFeatureEducationTelemetry('setup_guide_step_completed', { step_id: 'split-terminal', section_id: 'parallel-work', completed_count: 8, total_steps: 8, setup_guide_visible: true }) - expect(trackMock).not.toHaveBeenCalled() expect(getSetupGuideStepSection('notifications')).toBe('setup') }) @@ -123,11 +115,10 @@ describe('feature education telemetry helpers', () => { it('tracks terminal pane split with explicit source and direction', () => { trackTerminalPaneSplit({ source: 'keyboard', direction: 'horizontal' }) - expectLoggedFeatureEducationTelemetry('terminal_pane_split', { + expectTrackedFeatureEducationTelemetry('terminal_pane_split', { source: 'keyboard', direction: 'horizontal' }) - expect(trackMock).not.toHaveBeenCalled() }) it('caps terminal pane split telemetry by source and direction for each UTC day', () => { @@ -140,28 +131,25 @@ describe('feature education telemetry helpers', () => { trackTerminalPaneSplit({ source: 'keyboard', direction: 'vertical' }) trackTerminalPaneSplit({ source: 'context_menu', direction: 'horizontal' }) - expect(consoleInfoMock).toHaveBeenCalledTimes(3) - expect(consoleInfoMock).toHaveBeenNthCalledWith( + expect(trackMock).toHaveBeenCalledTimes(3) + expect(trackMock).toHaveBeenNthCalledWith( 1, - '[feature-education-telemetry:test-mode]', 'terminal_pane_split', { source: 'keyboard', direction: 'horizontal' } ) - expect(consoleInfoMock).toHaveBeenNthCalledWith( + expect(trackMock).toHaveBeenNthCalledWith( 2, - '[feature-education-telemetry:test-mode]', 'terminal_pane_split', { source: 'keyboard', direction: 'vertical' } ) - expect(consoleInfoMock).toHaveBeenNthCalledWith( + expect(trackMock).toHaveBeenNthCalledWith( 3, - '[feature-education-telemetry:test-mode]', 'terminal_pane_split', { source: 'context_menu', @@ -172,23 +160,12 @@ describe('feature education telemetry helpers', () => { vi.setSystemTime(new Date('2026-06-03T00:00:00.000Z')) trackTerminalPaneSplit({ source: 'keyboard', direction: 'horizontal' }) - expect(consoleInfoMock).toHaveBeenCalledTimes(4) - expect(trackMock).not.toHaveBeenCalled() + expect(trackMock).toHaveBeenCalledTimes(4) }) }) -function beforeEachConsoleInfo(): void { - beforeEach(() => { - vi.spyOn(console, 'info').mockImplementation(consoleInfoMock) - }) -} - -function expectLoggedFeatureEducationTelemetry(name: string, props: Record): void { - expect(consoleInfoMock).toHaveBeenCalledWith( - '[feature-education-telemetry:test-mode]', - name, - props - ) +function expectTrackedFeatureEducationTelemetry(name: string, props: Record): void { + expect(trackMock).toHaveBeenCalledWith(name, props) } function createMemoryStorage(): Storage { diff --git a/src/renderer/src/lib/feature-education-telemetry.ts b/src/renderer/src/lib/feature-education-telemetry.ts index 7bf281073..e65fe1265 100644 --- a/src/renderer/src/lib/feature-education-telemetry.ts +++ b/src/renderer/src/lib/feature-education-telemetry.ts @@ -12,6 +12,7 @@ import { isFeatureWallSetupStepId, type FeatureWallSetupStepId } from '../../../shared/feature-wall-setup-steps' +import { track } from './telemetry' const SETUP_GUIDE_TELEMETRY_COMPLETED_STEPS_STORAGE_KEY = 'orca.setupGuideTelemetryCompletedSteps.v1' @@ -20,7 +21,6 @@ const SETUP_GUIDE_PARALLEL_WORK_STEP_IDS = new Set([ 'split-terminal', 'two-worktrees' ]) -const FEATURE_EDUCATION_TELEMETRY_LOG_LABEL = '[feature-education-telemetry:test-mode]' type FeatureEducationTelemetryEventName = Extract< EventName, @@ -37,7 +37,7 @@ export function trackContextualTourShown(args: { source: string | null | undefined wasFeaturePreviouslyInteracted: boolean }): void { - logFeatureEducationTelemetry('contextual_tour_shown', { + emitFeatureEducationTelemetry('contextual_tour_shown', { tour_id: args.tourId, source: normalizeFeatureEducationSource(args.source), was_feature_previously_interacted: args.wasFeaturePreviouslyInteracted @@ -53,7 +53,7 @@ export function trackContextualTourOutcome(args: { furthestStepIndex?: number definedStepCount?: number }): void { - logFeatureEducationTelemetry('contextual_tour_outcome', { + emitFeatureEducationTelemetry('contextual_tour_outcome', { tour_id: args.tourId, source: normalizeFeatureEducationSource(args.source), outcome: args.outcome, @@ -75,7 +75,7 @@ export function trackSetupGuideOpened(args: { firstIncompleteStepId: FeatureWallSetupStepId | 'none' }): SetupGuideSource { const source = normalizeSetupGuideSource(args.source) - logFeatureEducationTelemetry('setup_guide_opened', { + emitFeatureEducationTelemetry('setup_guide_opened', { source, initial_completed_count: clampSetupGuideStepCount(args.initialCompletedCount), total_steps: 8, @@ -97,7 +97,7 @@ export function trackSetupGuideClosed(args: { initialCompletedCount, clampSetupGuideStepCount(args.finalCompletedCount) ) - logFeatureEducationTelemetry('setup_guide_closed', { + emitFeatureEducationTelemetry('setup_guide_closed', { source: args.source, outcome: args.outcome, initial_completed_count: initialCompletedCount, @@ -113,7 +113,7 @@ export function trackSetupGuideStepCompleted(args: { totalSteps: number setupGuideVisible: boolean }): void { - logFeatureEducationTelemetry('setup_guide_step_completed', { + emitFeatureEducationTelemetry('setup_guide_step_completed', { step_id: args.stepId, section_id: getSetupGuideStepSection(args.stepId), completed_count: clampSetupGuideStepCount(args.completedCount, 1), @@ -129,7 +129,7 @@ export function trackTerminalPaneSplit(args: { if (!reserveTerminalPaneSplitTelemetry(args.source, args.direction)) { return } - logFeatureEducationTelemetry('terminal_pane_split', { + emitFeatureEducationTelemetry('terminal_pane_split', { source: args.source, direction: args.direction }) @@ -198,13 +198,11 @@ export function getSetupGuideStepSection(id: FeatureWallSetupStepId): 'parallel- return SETUP_GUIDE_PARALLEL_WORK_STEP_IDS.has(id) ? 'parallel-work' : 'setup' } -function logFeatureEducationTelemetry( +function emitFeatureEducationTelemetry( name: N, props: EventProps ): void { - // Why: this branch is validating the new telemetry payloads without sending - // product analytics; keep the payload shape exact for reviewer inspection. - console.info(FEATURE_EDUCATION_TELEMETRY_LOG_LABEL, name, props) + track(name, props) } function clampTourStepCount(value: number, min = 0): number {