Skip to content

Commit

Permalink
Add SUBMIT_SEARCH event (#2120)
Browse files Browse the repository at this point in the history
  • Loading branch information
obulat authored May 25, 2023
1 parent f0d0441 commit f138b15
Show file tree
Hide file tree
Showing 10 changed files with 256 additions and 9 deletions.
8 changes: 7 additions & 1 deletion frontend/src/components/VFourOhFour.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import { useMeta, useRouter } from "@nuxtjs/composition-api"
import { useSearchStore } from "~/stores/search"
import { useAnalytics } from "~/composables/use-analytics"
import { ALL_MEDIA } from "~/constants/media"
import VLink from "~/components/VLink.vue"
Expand All @@ -65,8 +66,13 @@ export default defineComponent({
const searchStore = useSearchStore()
const router = useRouter()
const { sendCustomEvent } = useAnalytics()
const handleSearch = (searchTerm) => {
if (!searchTerm) return
sendCustomEvent("SUBMIT_SEARCH", {
searchType: ALL_MEDIA,
query: searchTerm,
})
router.push(searchStore.updateSearchPath({ type: ALL_MEDIA, searchTerm }))
}
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/components/VHeader/VHeaderDesktop.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import { useUiStore } from "~/stores/ui"
import { IsHeaderScrolledKey, IsSidebarVisibleKey } from "~/types/provides"
import { useAnalytics } from "~/composables/use-analytics"
import { useSearch } from "~/composables/use-search"
import { ensureFocus } from "~/utils/reakit-utils/focus"
Expand Down Expand Up @@ -89,7 +90,10 @@ export default defineComponent({
const isFetching = computed(() => mediaStore.fetchState.isFetching)
const { updateSearchState, searchTerm, searchStatus } = useSearch()
const { sendCustomEvent } = useAnalytics()
const { updateSearchState, searchTerm, searchStatus } =
useSearch(sendCustomEvent)
const clearSearchTerm = () => {
searchTerm.value = ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ import { keycodes } from "~/constants/key-codes"
import { IsHeaderScrolledKey } from "~/types/provides"
import { useAnalytics } from "~/composables/use-analytics"
import { useDialogControl } from "~/composables/use-dialog-control"
import { useSearch } from "~/composables/use-search"
Expand Down Expand Up @@ -162,7 +163,10 @@ export default defineComponent({
const isFetching = computed(() => mediaStore.fetchState.isFetching)
const { updateSearchState, searchTerm, searchStatus } = useSearch()
const { sendCustomEvent } = useAnalytics()
const { updateSearchState, searchTerm, searchStatus } =
useSearch(sendCustomEvent)
const handleSearch = async () => {
window.scrollTo({ top: 0, left: 0, behavior: "auto" })
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/composables/use-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import { useRouter } from "@nuxtjs/composition-api"

import { useSearchStore } from "~/stores/search"
import { useMediaStore } from "~/stores/media"

import { useI18nResultsCount } from "~/composables/use-i18n-utilities"
import { useMatchSearchRoutes } from "~/composables/use-match-routes"
import type { EventName, Events } from "~/types/analytics"

export const useSearch = () => {
export const useSearch = (
sendCustomEvent: <T extends EventName>(name: T, payload: Events[T]) => void
) => {
const mediaStore = useMediaStore()
const searchStore = useSearchStore()
const router = useRouter()
Expand Down Expand Up @@ -56,6 +60,11 @@ export const useSearch = () => {
if (searchTerm.value === "") return
if (!searchTermChanged.value && isSearchRoute.value) return

sendCustomEvent("SUBMIT_SEARCH", {
searchType: searchStore.searchType,
query: searchTerm.value,
})

const searchPath = searchStore.updateSearchPath({
searchTerm: searchTerm.value,
})
Expand Down
19 changes: 14 additions & 5 deletions frontend/src/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,30 @@
</template>

<script lang="ts">
import { computed, onMounted, ref } from "vue"
import { computed, defineComponent, onMounted, ref } from "vue"
import { defineComponent, useMeta, useRouter } from "@nuxtjs/composition-api"
import { useMeta, useRouter } from "@nuxtjs/composition-api"
import {
ALL_MEDIA,
SupportedSearchType,
supportedSearchTypes,
} from "~/constants/media"
import { useAnalytics } from "~/composables/use-analytics"
import { useLayout } from "~/composables/use-layout"
import { useMediaStore } from "~/stores/media"
import { useSearchStore } from "~/stores/search"
import { useUiStore } from "~/stores/ui"
import VHomeGallery from "~/components/VHomeGallery/VHomeGallery.vue"
import VHomepageContent from "~/components/VHomepageContent.vue"
export default defineComponent({
name: "HomePage",
components: {
VHomeGallery: () => import("~/components/VHomeGallery/VHomeGallery.vue"),
VHomepageContent: () => import("~/components/VHomepageContent.vue"),
VHomeGallery,
VHomepageContent,
},
setup() {
const router = useRouter()
Expand All @@ -44,6 +48,8 @@ export default defineComponent({
const searchStore = useSearchStore()
const uiStore = useUiStore()
const { sendCustomEvent } = useAnalytics()
useMeta({
meta: [
{ hid: "theme-color", name: "theme-color", content: "#ffe033" },
Expand Down Expand Up @@ -73,7 +79,10 @@ export default defineComponent({
}
const handleSearch = (searchTerm: string) => {
if (!searchTerm) return
sendCustomEvent("SUBMIT_SEARCH", {
searchType: searchType.value,
query: searchTerm,
})
router.push(
searchStore.updateSearchPath({
Expand Down
14 changes: 14 additions & 0 deletions frontend/src/types/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@ import type { ReportReason } from "~/constants/content-report"
* - Questions that are answered by the event must be listed as bullet points.
*/
export type Events = {
/**
* Description: A search performed by the user
* Questions:
* - How many searches do we serve per day/month/year?
* - What are the most popular searches in Openverse?
* - Which media types are the most popular?
* - Do most users search from the homepage, or internal searchbar?
*/
SUBMIT_SEARCH: {
/** The media type being searched */
searchType: SearchType
/** The search term */
query: string
}
/**
* Description: The user clicks on one of the images in the gallery on the homepage.
* Questions:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { fireEvent } from "@testing-library/vue"

import { ref } from "vue"

import { render } from "~~/test/unit/test-utils/render"

import { useAnalytics } from "~/composables/use-analytics"

import { IsHeaderScrolledKey, IsSidebarVisibleKey } from "~/types/provides"

import VHeaderDesktop from "~/components/VHeader/VHeaderDesktop.vue"

jest.mock("~/composables/use-analytics", () => ({
useAnalytics: jest.fn(),
}))
jest.mock("~/composables/use-match-routes", () => ({
// mocking `Ref<boolean>` as `{ value: boolean }`
useMatchSearchRoutes: jest.fn(() => ({ matches: { value: true } })),
}))

describe("VHeaderDesktop", () => {
const routerMock = { push: jest.fn() }
const sendCustomEventMock = jest.fn()
window.scrollTo = jest.fn()

let options = null

beforeEach(() => {
useAnalytics.mockImplementation(() => ({
sendCustomEvent: sendCustomEventMock,
}))
options = {
mocks: { $router: routerMock },
stubs: ["ClientOnly"],
provide: {
[IsHeaderScrolledKey]: ref(false),
[IsSidebarVisibleKey]: ref(false),
},
}
})
it("sends SUBMIT_SEARCH analytics event when submitted", async () => {
const screen = render(VHeaderDesktop, options)
const input = screen.getByRole("combobox")

await fireEvent.update(input, "cat")
await fireEvent.submit(input)

expect(sendCustomEventMock).toHaveBeenCalledWith("SUBMIT_SEARCH", {
query: "cat",
searchType: "all",
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { fireEvent } from "@testing-library/vue"
import { ref } from "vue"

import { render } from "~~/test/unit/test-utils/render"

import { useAnalytics } from "~/composables/use-analytics"

import { IsHeaderScrolledKey, IsSidebarVisibleKey } from "~/types/provides"

import VHeaderMobile from "~/components/VHeader/VHeaderMobile/VHeaderMobile.vue"

jest.mock("~/composables/use-analytics", () => ({
useAnalytics: jest.fn(),
}))
jest.mock("~/composables/use-match-routes", () => ({
// mocking `Ref<boolean>` as `{ value: boolean }`
useMatchSearchRoutes: jest.fn(() => ({ matches: { value: true } })),
}))

describe("VHeaderMobile", () => {
const routerMock = { push: jest.fn() }
const sendCustomEventMock = jest.fn()
useAnalytics.mockImplementation(() => ({
sendCustomEvent: sendCustomEventMock,
}))
window.scrollTo = jest.fn()

let options = null

beforeEach(() => {
options = {
mocks: { $router: routerMock },
stubs: ["ClientOnly"],
provide: {
[IsHeaderScrolledKey]: ref(false),
[IsSidebarVisibleKey]: ref(false),
},
}
})
it("sends SUBMIT_SEARCH analytics event when submitted", async () => {
const screen = render(VHeaderMobile, options)
const input = screen.getByRole("combobox")

await fireEvent.update(input, "cat")
await fireEvent.submit(input)

expect(sendCustomEventMock).toHaveBeenCalledWith("SUBMIT_SEARCH", {
query: "cat",
searchType: "all",
})
})
})
37 changes: 37 additions & 0 deletions frontend/test/unit/specs/components/four-oh-four.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { fireEvent, screen } from "@testing-library/vue"

import { render } from "~~/test/unit/test-utils/render"

import { useAnalytics } from "~/composables/use-analytics"

import VFourOhFour from "~/components/VFourOhFour.vue"

jest.mock("~/composables/use-analytics", () => ({
useAnalytics: jest.fn(),
}))
describe("VFourOhFour", () => {
let options
const sendCustomEventMock = jest.fn()
useAnalytics.mockImplementation(() => ({
sendCustomEvent: sendCustomEventMock,
}))
const query = "cat"
beforeEach(() => {
options = {
mocks: { $router: { push: jest.fn() } },
}
})

it("should send SUBMIT_SEARCH analytics event when search submitted", async () => {
render(VFourOhFour, options)

const input = screen.getByRole("searchbox")
await fireEvent.update(input, query)
await fireEvent.submit(input)

expect(sendCustomEventMock).toHaveBeenCalledWith("SUBMIT_SEARCH", {
query,
searchType: "all",
})
})
})
59 changes: 59 additions & 0 deletions frontend/test/unit/specs/pages/index-page.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { fireEvent, screen } from "@testing-library/vue"

import { render } from "~~/test/unit/test-utils/render"

import IndexPage from "~/pages/index.vue"

import { useAnalytics } from "~/composables/use-analytics"

jest.mock("~/composables/use-analytics", () => ({
useAnalytics: jest.fn(),
}))
describe("IndexPage", () => {
let options
const sendCustomEventMock = jest.fn()
useAnalytics.mockImplementation(() => ({
sendCustomEvent: sendCustomEventMock,
}))
const query = "cat"
beforeEach(() => {
options = {
mocks: { $router: { push: jest.fn() } },
stubs: ["VHomeGallery"],
}
})

it("should send SUBMIT_SEARCH analytics event when search submitted", async () => {
render(IndexPage, options)

const input = screen.getByRole("searchbox")
await fireEvent.update(input, query)
await fireEvent.submit(input)

expect(sendCustomEventMock).toHaveBeenCalledWith("SUBMIT_SEARCH", {
query,
searchType: "all",
})
})

it("should send SUBMIT_SEARCH analytics event with correct mediaType when search submitted", async () => {
render(IndexPage, options)

const button = screen.getByRole("button", {
name: "Select a content type: All content",
})
await fireEvent.click(button)

const audio = screen.getByRole("radio", { name: "Audio" })
await fireEvent.click(audio)

const input = screen.getByRole("searchbox")
await fireEvent.update(input, query)
await fireEvent.submit(input)

expect(sendCustomEventMock).toHaveBeenCalledWith("SUBMIT_SEARCH", {
query,
searchType: "audio",
})
})
})

0 comments on commit f138b15

Please sign in to comment.