diff --git a/src/ui/tui/__tests__/store.test.ts b/src/ui/tui/__tests__/store.test.ts index 8643a030f..8f779001d 100644 --- a/src/ui/tui/__tests__/store.test.ts +++ b/src/ui/tui/__tests__/store.test.ts @@ -325,6 +325,55 @@ describe('WizardStore', () => { }); }); + it('setCredentials does not fire auth complete for null credentials', () => { + const store = createStore(); + store.setCredentials(null); + expect(wizardCaptureMock).not.toHaveBeenCalledWith( + 'auth complete', + expect.anything(), + ); + }); + + it('setCredentials fires auth complete only once for repeated same credentials', () => { + const store = createStore(); + const creds = { + accessToken: 'tok', + projectApiKey: 'pk', + host: HostResolution.fromApiHost('https://us.i.posthog.com'), + projectId: 42, + }; + store.setCredentials(creds); + store.setCredentials({ + ...creds, + host: HostResolution.fromApiHost('https://us.i.posthog.com'), + }); + const authCalls = wizardCaptureMock.mock.calls.filter( + (call) => call[0] === 'auth complete', + ); + expect(authCalls).toHaveLength(1); + }); + + it('setCredentials fires auth complete again when the project changes', () => { + const store = createStore(); + const host = HostResolution.fromApiHost('https://us.i.posthog.com'); + store.setCredentials({ + accessToken: 'tok', + projectApiKey: 'pk', + host, + projectId: 42, + }); + store.setCredentials({ + accessToken: 'tok', + projectApiKey: 'pk', + host, + projectId: 99, + }); + const authCalls = wizardCaptureMock.mock.calls.filter( + (call) => call[0] === 'auth complete', + ); + expect(authCalls).toHaveLength(2); + }); + it('enableFeature fires feature enabled event', () => { const store = createStore(); store.enableFeature(AdditionalFeature.LLM); diff --git a/src/ui/tui/store.ts b/src/ui/tui/store.ts index eeb818d27..f6b26dd70 100644 --- a/src/ui/tui/store.ts +++ b/src/ui/tui/store.ts @@ -442,13 +442,24 @@ export class WizardStore { } setCredentials(credentials: WizardSession['credentials']): void { + const previous = this.session.credentials; this.$session.setKey('credentials', credentials); if (credentials?.projectId) { analytics.setTag('project_id', credentials.projectId); } - analytics.wizardCapture('auth complete', { - project_id: credentials?.projectId, - }); + // One run writes credentials up to four times (auth, MCP prompts, Slack + // connect, run-wizard). Capture `auth complete` only when real credentials + // first arrive or the project/host actually changes, so the metric counts + // completed auths instead of setter calls. + const changed = + credentials != null && + (previous?.projectId !== credentials.projectId || + previous?.host.apiHost !== credentials.host.apiHost); + if (changed) { + analytics.wizardCapture('auth complete', { + project_id: credentials.projectId, + }); + } this.emitChange(); }