Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add VIEW_EXTERNAL_SOURCES event #2450

Merged
merged 12 commits into from
Jul 3, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,16 @@
<script lang="ts">
import { computed, defineComponent, PropType, ref, SetupContext } from "vue"

import { storeToRefs } from "pinia"

import { defineEvent } from "~/types/emits"

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

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

import type { MediaType } from "~/constants/media"
import type { ExternalSource } from "~/types/external-source"
Expand Down Expand Up @@ -130,6 +135,8 @@ export default defineComponent({
const sectionRef = ref<HTMLElement | null>(null)
const triggerRef = ref<InstanceType<typeof VButton> | null>(null)
const uiStore = useUiStore()
const searchStore = useSearchStore()
const { sendCustomEvent } = useAnalytics()

const isMd = computed(() => uiStore.isBreakpoint("md"))

Expand All @@ -139,6 +146,11 @@ export default defineComponent({

const isVisible = ref(false)

// Use the `_searchType` from mediaStore because it falls back to ALL_MEDIA
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says something different from what's happening. searhType is being used from the searchStore on L169.

// for unsupported search types.
const mediaStore = useMediaStore()
const { currentPage } = storeToRefs(mediaStore)

const {
close: closeDialog,
open: openDialog,
Expand All @@ -151,6 +163,17 @@ export default defineComponent({
emit: emit as SetupContext["emit"],
})

const eventedOnTriggerClick = () => {
if (!isVisible.value) {
sendCustomEvent("VIEW_EXTERNAL_SOURCES", {
searchType: searchStore.searchType,
zackkrida marked this conversation as resolved.
Show resolved Hide resolved
query: searchStore.searchTerm,
resultPage: currentPage.value || 1,
})
}
onTriggerClick()
}

return {
sectionRef,
triggerRef,
Expand All @@ -159,7 +182,7 @@ export default defineComponent({

closeDialog,
openDialog,
onTriggerClick,
onTriggerClick: eventedOnTriggerClick,
triggerA11yProps,

isVisible,
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/types/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,19 @@ export type Events = {
query: string
}
/**
* Description: When a user opens the external sources popover.
* Questions:
* - How often do users use this feature?
* - Under what conditions to users use this feature? No results?
* Many results, but none they actually select?
*/
VIEW_EXTERNAL_SOURCES: {
/** The media type being searched */
searchType: SearchType
/** The search term */
query: string
}
zackkrida marked this conversation as resolved.
Show resolved Hide resolved
/*
* Description: Whenever the user sets a filter. Filter category and key are the values used in code, not the user-facing filter labels.
* Questions:
* - Do most users filter their searches?
Expand Down
62 changes: 35 additions & 27 deletions frontend/test/playwright/e2e/external-sources.spec.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,46 @@
import { test, expect } from "@playwright/test"

import { goToSearchTerm, setCookies } from "~~/test/playwright/utils/navigation"

test("sends analytics event on external source click", async ({ page }) => {
let eventData: {
name: string
mediaType: string
query: string
} = { name: "", mediaType: "", query: "" }
page.on("request", (req) => {
if (req.method() === "POST") {
const requestData = req.postDataJSON()
if (requestData?.n == "SELECT_EXTERNAL_SOURCE") {
eventData = JSON.parse(requestData?.p)
}
}
})
// Start waiting for new page before clicking.
import { test } from "@playwright/test"

import { goToSearchTerm } from "~~/test/playwright/utils/navigation"

import {
collectAnalyticsEvents,
expectEventPayloadToMatch,
} from "../utils/analytics"
zackkrida marked this conversation as resolved.
Show resolved Hide resolved

test("sends correct analytics events", async ({
page,
context,
}) => {
const pagePromise = page.context().waitForEvent("page")

const mediaType = "image"
const name = "Centre For Ageing Better"
const query = "cat"
const events = collectAnalyticsEvents(context)

await setCookies(page.context(), { analytics: "true" })
await goToSearchTerm(page, "cat", { mode: "SSR", query: "ff_analytics=on" })
await goToSearchTerm(page, "cat", { mode: "SSR" })

await page.getByRole("button", { name: "Source list" }).click()
await page.getByRole("link", { name: "Centre for Ageing Better" }).click()

const newPage = await pagePromise
await newPage.close()

expect(eventData.name).toEqual(name)
expect(eventData.mediaType).toEqual(mediaType)
expect(eventData.query).toEqual(query)
const viewEvent = events.find((event) => event.n === "VIEW_EXTERNAL_SOURCES")
const selectEvent = events.find(
(event) => event.n === "SELECT_EXTERNAL_SOURCE"
)

if (!viewEvent || !selectEvent) {
throw new Error("Analytics events were not triggered properly.")
}

expectEventPayloadToMatch(viewEvent, {
searchType: "all",
query: "cat",
resultPage: 1,
})
expectEventPayloadToMatch(selectEvent, {
name: "Centre For Ageing Better",
mediaType: "image",
query: "cat",
component: "VExternalSourceList",
})
})