diff --git a/package.json b/package.json index 6f5361a22e2..7f6b969584b 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "make-fetch-happen@npm:^13.0.1": "^14.0.3" }, "dependencies": { - "@artsy/cohesion": "4.386.1", + "@artsy/cohesion": "4.387.0", "@artsy/detect-responsive-traits": "^0.2.0", "@artsy/dismissible": "^0.9.0", "@artsy/express-reloadable": "^1.7.0", diff --git a/src/Apps/Search/SearchApp.tsx b/src/Apps/Search/SearchApp.tsx index d78db8e8a86..56f3a0e32e4 100644 --- a/src/Apps/Search/SearchApp.tsx +++ b/src/Apps/Search/SearchApp.tsx @@ -1,3 +1,9 @@ +import { + ActionType, + ContextModule, + OwnerType, + type SearchedWithNoResults, +} from "@artsy/cohesion" import { Box, FullBleed, @@ -14,7 +20,9 @@ import { Sticky } from "Components/Sticky" import { useRouter } from "System/Hooks/useRouter" import type { SearchApp_viewer$data } from "__generated__/SearchApp_viewer.graphql" import type React from "react" +import { useEffect } from "react" import { createFragmentContainer, graphql } from "react-relay" +import { useTracking } from "react-tracking" import { ZeroState } from "./Components/ZeroState" import { Jump } from "Utils/Hooks/useJump" @@ -58,19 +66,33 @@ export const SearchApp: React.FC> = ({ const artworkCount = artworksConnection?.counts?.total ?? 0 const countWithoutArtworks = - typeAggregation?.reduce((total = 0, aggregation) => { - if (!aggregation) { + typeAggregation?.reduce((total, aggregation) => { + if (!aggregation || aggregation.name === "artwork") { return total } - const { count, name } = aggregation - if (name !== "artwork") { - return total + count - } + return total + aggregation.count }, 0) ?? 0 const hasResults = !!(countWithoutArtworks || artworkCount) const totalCount = countWithoutArtworks + artworkCount + const { trackEvent } = useTracking() + + useEffect(() => { + if (hasResults) { + return + } + + const trackingData: SearchedWithNoResults = { + action: ActionType.searchedWithNoResults, + context_module: ContextModule.searchPageResults, + context_owner_type: OwnerType.search, + query: term, + } + + trackEvent(trackingData) + }, [hasResults, term, trackEvent]) + return ( <> diff --git a/src/Apps/Search/__tests__/SearchApp.jest.tsx b/src/Apps/Search/__tests__/SearchApp.jest.tsx index a3f9bf78f97..c0e0a807b87 100644 --- a/src/Apps/Search/__tests__/SearchApp.jest.tsx +++ b/src/Apps/Search/__tests__/SearchApp.jest.tsx @@ -1,18 +1,21 @@ +import { ActionType, ContextModule, OwnerType } from "@artsy/cohesion" +import { screen } from "@testing-library/react" import { SearchAppFragmentContainer as SearchApp } from "Apps/Search/SearchApp" import { MockBoot } from "DevTools/MockBoot" import { setupTestWrapperTL } from "DevTools/setupTestWrapperTL" import { SystemContextProvider } from "System/Contexts/SystemContext" import type { SearchAppTestQuery } from "__generated__/SearchAppTestQuery.graphql" -import { screen } from "@testing-library/react" import { graphql } from "react-relay" +import { useTracking } from "react-tracking" jest.unmock("react-relay") jest.mock("react-tracking") +let mockLocationQuery: { term?: string } = { term: "andy" } jest.mock("System/Hooks/useRouter", () => ({ useRouter: () => ({ match: { location: { - query: { term: "andy" }, + query: mockLocationQuery, }, }, }), @@ -22,6 +25,17 @@ jest.mock("Utils/Hooks/useMatchMedia", () => ({ __internal__useMatchMedia: () => false, })) +const mockTrackEvent = jest.fn() + +beforeEach(() => { + mockLocationQuery = { term: "andy" } + ;(useTracking as jest.Mock).mockReturnValue({ trackEvent: mockTrackEvent }) +}) + +afterEach(() => { + jest.clearAllMocks() +}) + const { renderWithRelay } = setupTestWrapperTL({ Component: props => { return ( @@ -72,6 +86,35 @@ describe("SearchApp", () => { expect(screen.getByText("More")).toBeInTheDocument() }) + + it("does not track searchedWithNoResults when there are results", async () => { + renderWithRelay({ + Viewer: () => VIEWER_FIXTURE, + }) + + await screen.findByText("521 results for") + + expect(mockTrackEvent).not.toHaveBeenCalled() + }) + + it("shows the zero state and tracks searchedWithNoResults when there are no results", async () => { + renderWithRelay({ + Viewer: () => NO_RESULTS_VIEWER_FIXTURE, + }) + + await screen.findByText( + "Try checking for spelling errors or try another search term.", + ) + + expect(mockTrackEvent).toHaveBeenCalledWith({ + action: ActionType.searchedWithNoResults, + context_module: ContextModule.searchPageResults, + context_owner_type: OwnerType.search, + context_owner_id: undefined, + context_owner_slug: undefined, + query: "andy", + }) + }) }) const VIEWER_FIXTURE = { @@ -94,3 +137,19 @@ const VIEWER_FIXTURE = { ], }, } + +const NO_RESULTS_VIEWER_FIXTURE = { + artworksConnection: { + counts: { + total: 0, + }, + }, + searchConnection: { + aggregations: [ + { + counts: [], + slice: "TYPE", + }, + ], + }, +} diff --git a/yarn.lock b/yarn.lock index 3b3480c3b37..f2e825cc7d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21,12 +21,12 @@ __metadata: languageName: node linkType: hard -"@artsy/cohesion@npm:4.386.1": - version: 4.386.1 - resolution: "@artsy/cohesion@npm:4.386.1" +"@artsy/cohesion@npm:4.387.0": + version: 4.387.0 + resolution: "@artsy/cohesion@npm:4.387.0" dependencies: core-js: "npm:3" - checksum: 10c0/ff6d026935b73f917f942cbb8ab4dc2a1a138a2dcc0bd68f3a67f2c199821e04f996f6420743172f4017d9c8fd10a46fb8135addee9fd82de7419893ec372449 + checksum: 10c0/d3362f65f3b0f8ec94f1f9c2dd9ba37cc254126475f26b257c820b223d27ac58d91dd481f60719f27e53cb15fc3ff832614504739bf3253eef3385919837e2e9 languageName: node linkType: hard @@ -9275,7 +9275,7 @@ __metadata: resolution: "force@workspace:." dependencies: "@artaio/arta-browser": "npm:^2.16.1" - "@artsy/cohesion": "npm:4.386.1" + "@artsy/cohesion": "npm:4.387.0" "@artsy/detect-responsive-traits": "npm:^0.2.0" "@artsy/dismissible": "npm:^0.9.0" "@artsy/express-reloadable": "npm:^1.7.0"