diff --git a/.changeset/long-plums-repeat.md b/.changeset/long-plums-repeat.md new file mode 100644 index 000000000..f097815f3 --- /dev/null +++ b/.changeset/long-plums-repeat.md @@ -0,0 +1,5 @@ +--- +'@segment/analytics-signals': minor +--- + +add support for analytics.reset() diff --git a/packages/signals/signals-integration-tests/src/tests/signals-vanilla/reset.test.ts b/packages/signals/signals-integration-tests/src/tests/signals-vanilla/reset.test.ts new file mode 100644 index 000000000..bdeb9aa5f --- /dev/null +++ b/packages/signals/signals-integration-tests/src/tests/signals-vanilla/reset.test.ts @@ -0,0 +1,55 @@ +import { test, expect } from '@playwright/test' +import { waitForCondition } from '../../helpers/playwright-utils' +import { IndexPage } from './index-page' +import { pTimeout } from '@segment/analytics-core' + +/** + * If a signal is generated, the signal buffer should be reset + * when the user clicks on the complex button. + */ +const edgeFn = `const processSignal = (signal) => { + // create a custom signal to echo out the current signal buffer + if (signal.type === 'userDefined') { + analytics.track('current signal buffer', { signalBuffer: signals.signalBuffer }) + } + + // clicking the complex button to clear the signal buffer + if (signal.type === 'interaction' && signal.data.eventType === 'click' && signal.data.target?.id === 'complex-button') { + analytics.reset() + } +}` + +test('calls analytics.reset, and resets the signalBuffer after clear', async ({ + page, +}) => { + const indexPage = await new IndexPage().loadAndWait(page, edgeFn) + + await indexPage.addUserDefinedSignal({ num: 1 }) + const resetCalled = page.evaluate(() => { + return new Promise((resolve) => { + window.analytics.on('reset', resolve) + }) + }) + + await waitForCondition(() => indexPage.trackingAPI.getEvents().length > 0, { + errorMessage: + 'No track events found, should have an event with hasSignalsInBuffer: true', + }) + const events = indexPage.trackingAPI.getEvents() + const buffer = events[0].properties!.signalBuffer + expect(buffer[0]).toMatchObject({ type: 'userDefined' }) + expect(buffer[1]).toMatchObject({ type: 'navigation' }) + + indexPage.trackingAPI.clear() + await indexPage.clickComplexButton() + await pTimeout(resetCalled, 5000) + await indexPage.addUserDefinedSignal({ num: 2 }) + await waitForCondition(() => indexPage.trackingAPI.getEvents().length > 0, { + errorMessage: + 'No track events found, should only have one event in the buffer (the current signal)', + }) + const events2 = indexPage.trackingAPI.getEvents() + const buffer2 = events2[0].properties!.signalBuffer + expect(buffer2).toHaveLength(1) + expect(buffer2[0]).toMatchObject({ type: 'userDefined' }) +}) diff --git a/packages/signals/signals/src/core/processor/sandbox.ts b/packages/signals/signals/src/core/processor/sandbox.ts index d183a04c6..e7f81032b 100644 --- a/packages/signals/signals/src/core/processor/sandbox.ts +++ b/packages/signals/signals/src/core/processor/sandbox.ts @@ -18,7 +18,9 @@ export type MethodName = /** * Buffer of any analytics calls made during the processing of a signal */ -export type AnalyticsMethodCalls = Record +export type AnalyticsMethodCalls = Record & { + reset: unknown[] +} /** * Proxy around the analytics client @@ -31,6 +33,7 @@ class AnalyticsRuntime implements AnalyticsRuntimePublicApi { alias: [], screen: [], group: [], + reset: [], } getCalls(): AnalyticsMethodCalls { @@ -115,6 +118,10 @@ class AnalyticsRuntime implements AnalyticsRuntimePublicApi { console.error(err) } } + + reset = () => { + this.calls.reset.push([]) + } } interface CodeSandbox { diff --git a/packages/signals/signals/src/core/signals/signals.ts b/packages/signals/signals/src/core/signals/signals.ts index 26bb54274..425513a7d 100644 --- a/packages/signals/signals/src/core/signals/signals.ts +++ b/packages/signals/signals/src/core/signals/signals.ts @@ -88,6 +88,9 @@ export class Signals implements ISignals { */ async start(analytics: AnyAnalytics): Promise { const analyticsService = new AnalyticsService(analytics) + analyticsService.instance.on('reset', () => { + this.clearStorage() + }) this.globalSettings.update({ edgeFnDownloadURL: analyticsService.edgeFnSettings?.downloadURL, diff --git a/packages/signals/signals/src/test-helpers/mocks/analytics-mock.ts b/packages/signals/signals/src/test-helpers/mocks/analytics-mock.ts index 5503c4bfb..d3eb24a8e 100644 --- a/packages/signals/signals/src/test-helpers/mocks/analytics-mock.ts +++ b/packages/signals/signals/src/test-helpers/mocks/analytics-mock.ts @@ -20,4 +20,6 @@ export const analyticsMock: jest.Mocked = { page: jest.fn(), track: jest.fn(), addSourceMiddleware: jest.fn(), + reset: jest.fn(), + on: jest.fn(), } diff --git a/packages/signals/signals/src/types/analytics-api.ts b/packages/signals/signals/src/types/analytics-api.ts index 27353e959..c201f1a11 100644 --- a/packages/signals/signals/src/types/analytics-api.ts +++ b/packages/signals/signals/src/types/analytics-api.ts @@ -66,6 +66,8 @@ export interface AnyAnalytics { group(...args: any[]): void alias(...args: any[]): void screen(...args: any[]): void + reset(): void + on(name: 'reset', fn: (...args: any[]) => void): void } /** diff --git a/packages/signals/signals/src/types/process-signal.ts b/packages/signals/signals/src/types/process-signal.ts index 24d8b8cd4..0f8b2bfd8 100644 --- a/packages/signals/signals/src/types/process-signal.ts +++ b/packages/signals/signals/src/types/process-signal.ts @@ -14,6 +14,7 @@ export interface AnalyticsRuntimePublicApi { group: (...args: any[]) => void page: (...args: any[]) => void screen: (...args: any[]) => void + reset: () => void } export type ProcessSignalScope = {