diff --git a/packages/instantsearch.js/src/lib/InstantSearch.ts b/packages/instantsearch.js/src/lib/InstantSearch.ts index 60892a4b57..052c802563 100644 --- a/packages/instantsearch.js/src/lib/InstantSearch.ts +++ b/packages/instantsearch.js/src/lib/InstantSearch.ts @@ -20,7 +20,10 @@ import { } from './utils'; import version from './version'; -import type { InsightsEvent } from '../middlewares/createInsightsMiddleware'; +import type { + InsightsEvent, + InsightsProps, +} from '../middlewares/createInsightsMiddleware'; import type { RouterProps } from '../middlewares/createRouterMiddleware'; import type { InsightsClient as AlgoliaInsightsClient, @@ -145,7 +148,7 @@ export type InstantSearchOptions< * * @default true */ - insights?: boolean; + insights?: InsightsProps | boolean; /** * the instance of search-insights to use for sending insights events inside @@ -325,7 +328,9 @@ See ${createDocumentationLink({ // This is the default middleware, // any user-provided middleware will be added later and override this one. if (insights) { - this.use(createInsightsMiddleware({ $$internal: true })); + const insightsOptions = typeof insights === 'boolean' ? {} : insights; + insightsOptions.$$internal = true; + this.use(createInsightsMiddleware(insightsOptions)); } if (isMetadataEnabled()) { diff --git a/packages/instantsearch.js/src/lib/__tests__/InstantSearch-test.tsx b/packages/instantsearch.js/src/lib/__tests__/InstantSearch-test.tsx index 726e3963b7..d8ad7f9f27 100644 --- a/packages/instantsearch.js/src/lib/__tests__/InstantSearch-test.tsx +++ b/packages/instantsearch.js/src/lib/__tests__/InstantSearch-test.tsx @@ -14,6 +14,7 @@ import { h, render, createRef } from 'preact'; import { createRenderOptions, createWidget } from '../../../test/createWidget'; import { connectSearchBox, connectPagination } from '../../connectors'; +import { createInsightsMiddleware } from '../../middlewares'; import { index } from '../../widgets'; import InstantSearch from '../InstantSearch'; import { noop, warning } from '../utils'; @@ -427,6 +428,148 @@ See https://www.algolia.com/doc/api-reference/widgets/configure/js/`); expect(search.helper!.lastResults).not.toBe(null); }); + describe('insights middleware', () => { + test('adds insights middleware by default', () => { + const search = new InstantSearch({ + searchClient: createSearchClient(), + indexName: 'test', + }); + + expect( + search.middleware.map(({ instance: { $$type, $$internal } }) => ({ + $$type, + $$internal, + })) + ).toEqual([ + { + $$type: 'ais.insights', + $$internal: true, + }, + ]); + }); + + test('insights: true still adds only one middleware', () => { + const search = new InstantSearch({ + searchClient: createSearchClient(), + indexName: 'test', + insights: true, + }); + + expect( + search.middleware.map(({ instance: { $$type, $$internal } }) => ({ + $$type, + $$internal, + })) + ).toEqual([ + { + $$type: 'ais.insights', + $$internal: true, + }, + ]); + }); + + test('insights: options still creates one middleware only', () => { + const search = new InstantSearch({ + searchClient: createSearchClient(), + indexName: 'test', + insights: { + insightsInitParams: { + useCookie: false, + }, + }, + }); + + expect( + search.middleware.map(({ instance: { $$type, $$internal } }) => ({ + $$type, + $$internal, + })) + ).toEqual([ + { + $$type: 'ais.insights', + $$internal: true, + }, + ]); + }); + + test('insights: options passes options to middleware', () => { + const insightsClient = jest.fn(); + const search = new InstantSearch({ + searchClient: createSearchClient(), + indexName: 'test', + insights: { + insightsClient, + }, + }); + search.start(); + + expect( + search.middleware.map(({ instance: { $$type, $$internal } }) => ({ + $$type, + $$internal, + })) + ).toEqual([ + { + $$type: 'ais.insights', + $$internal: true, + }, + ]); + + // it's called a couple times setting up the middleware + expect(insightsClient).toHaveBeenCalled(); + insightsClient.mockClear(); + + search.sendEventToInsights({ + insightsMethod: 'clickedObjectIDsAfterSearch', + payload: { eventName: 'Add to cart' }, + eventType: 'click', + widgetType: 'ais.hits', + }); + + expect(insightsClient).toHaveBeenCalledTimes(1); + expect(insightsClient).toHaveBeenCalledWith( + 'clickedObjectIDsAfterSearch', + { eventName: 'Add to cart' } + ); + }); + + test('insights: false disables default insights', () => { + const search = new InstantSearch({ + searchClient: createSearchClient(), + indexName: 'test', + insights: false, + }); + + expect( + search.middleware.map(({ instance: { $$type, $$internal } }) => ({ + $$type, + $$internal, + })) + ).toEqual([]); + }); + + test("users' middleware overrides the builtin one", () => { + const search = new InstantSearch({ + searchClient: createSearchClient(), + indexName: 'test', + }); + + search.use(createInsightsMiddleware({})); + + expect( + search.middleware.map(({ instance: { $$type, $$internal } }) => ({ + $$type, + $$internal, + })) + ).toEqual([ + { + $$type: 'ais.insights', + $$internal: false, + }, + ]); + }); + }); + describe('metadata middleware', () => { const defaultUserAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Safari/605.1.15'; diff --git a/packages/instantsearch.js/src/middlewares/createInsightsMiddleware.ts b/packages/instantsearch.js/src/middlewares/createInsightsMiddleware.ts index 542768bf5f..53c2b869e6 100644 --- a/packages/instantsearch.js/src/middlewares/createInsightsMiddleware.ts +++ b/packages/instantsearch.js/src/middlewares/createInsightsMiddleware.ts @@ -27,11 +27,10 @@ export type InsightsEvent = { attribute?: string; }; +type ProvidedInsightsClient = InsightsClient | null | undefined; + export type InsightsProps< - TInsightsClient extends InsightsClient | null | undefined = - | InsightsClient - | null - | undefined + TInsightsClient extends ProvidedInsightsClient = ProvidedInsightsClient > = { insightsClient?: TInsightsClient; insightsInitParams?: { @@ -53,7 +52,7 @@ const ALGOLIA_INSIGHTS_SRC = export type CreateInsightsMiddleware = typeof createInsightsMiddleware; export function createInsightsMiddleware< - TInsightsClient extends null | InsightsClient + TInsightsClient extends ProvidedInsightsClient >(props: InsightsProps = {}): InternalMiddleware { const { insightsClient: _insightsClient,