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

Analytics event: GET_MEDIA #1180

Merged
merged 4 commits into from Apr 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions frontend/src/components/VAudioTrack/layouts/VFullLayout.vue
Expand Up @@ -59,6 +59,7 @@
show-external-icon
:external-icon-size="6"
class="description-bold order-1 my-1 ms-auto flex-shrink-0 lg:order-2"
@click="sendGetMediaEvent"
>
{{ $t("audio-details.weblink") }}
</VButton>
Expand All @@ -72,6 +73,8 @@ import { computed, defineComponent, PropType } from "vue"
import type { AudioDetail } from "~/types/media"
import { timeFmt } from "~/utils/time-fmt"
import { AudioSize, AudioStatus, audioFeatures } from "~/constants/audio"
import { AUDIO } from "~/constants/media"
import { useAnalytics } from "~/composables/use-analytics"

import VButton from "~/components/VButton.vue"
import VLink from "~/components/VLink.vue"
Expand All @@ -96,13 +99,25 @@ export default defineComponent({
},
},
setup(props) {
const { sendCustomEvent } = useAnalytics()

const isSmall = computed(() => props.size === "s")

const sendGetMediaEvent = () => {
sendCustomEvent("GET_MEDIA", {
id: props.audio.id,
provider: props.audio.provider,
mediaType: AUDIO,
})
}

return {
timeFmt,

isSmall,
audioFeatures,

sendGetMediaEvent,
}
},
})
Expand Down
19 changes: 19 additions & 0 deletions frontend/src/pages/image/_id/index.vue
Expand Up @@ -37,6 +37,7 @@
:external-icon-size="6"
has-icon-end
size="large"
@click="sendGetMediaEvent"
>
{{ $t("image-details.weblink") }}
</VButton>
Expand Down Expand Up @@ -85,6 +86,8 @@ import { defineComponent, useRoute } from "@nuxtjs/composition-api"

import { IMAGE } from "~/constants/media"
import type { ImageDetail } from "~/types/media"
import { useAnalytics } from "~/composables/use-analytics"

import { useSingleResultStore } from "~/stores/media/single-result"
import { useRelatedMediaStore } from "~/stores/media/related-media"
import { useSearchStore } from "~/stores/search"
Expand Down Expand Up @@ -206,6 +209,20 @@ export default defineComponent({
isLoadingMainImage.value = false
}
}

const { sendCustomEvent } = useAnalytics()

const sendGetMediaEvent = () => {
if (!image.value) {
return
}
sendCustomEvent("GET_MEDIA", {
id: image.value.id,
provider: image.value.provider,
mediaType: IMAGE,
})
}

return {
image,
hasRelatedMedia,
Expand All @@ -221,6 +238,8 @@ export default defineComponent({
onImageError,
backToSearchPath,
externalIcon,

sendGetMediaEvent,
}
},
async asyncData({ app, error, route, $pinia }) {
Expand Down
21 changes: 18 additions & 3 deletions frontend/src/types/analytics.ts
@@ -1,5 +1,7 @@
import type { MediaType } from "~/constants/media"

/**
* compound type of all custom events sent from the site; Index with `EventName`
* Compound type of all custom events sent from the site; Index with `EventName`
* to get the type of the payload for a specific event.
*
* Conventions:
Expand All @@ -11,8 +13,8 @@
*/
export type Events = {
/**
* Click on one of the images in the gallery on the homepage.
*
* Description: The user clicks on one of the images in the gallery on the homepage.
* Questions:
* - Do users know homepage images are links?
* - Do users find these images interesting?
* - Which set is most interesting for the users?
Expand All @@ -23,6 +25,19 @@ export type Events = {
/** the identifier of the image */
identifier: string
}
/**
* Description: The user clicks the CTA button to the external source to use the image
* Questions:
* - How often do users go to the source after viewing a result?
obulat marked this conversation as resolved.
Show resolved Hide resolved
*/
GET_MEDIA: {
/** the unique ID of the media */
id: string
/** The slug (not the prettified name) of the provider */
provider: string
/** The media type being searched */
mediaType: MediaType
}
}

/**
Expand Down
@@ -1,11 +1,20 @@
import { createLocalVue } from "@vue/test-utils"
import { render, screen } from "@testing-library/vue"
import { fireEvent, render, screen } from "@testing-library/vue"

import { getAudioObj } from "~~/test/unit/fixtures/audio"
import { PiniaVuePlugin, createPinia } from "~~/test/unit/test-utils/pinia"

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

import VFullLayout from "~/components/VAudioTrack/layouts/VFullLayout.vue"

jest.mock("~/composables/use-analytics", () => ({
useAnalytics: jest.fn(() => ({
sendCustomEvent: jest.fn(),
})),
}))

const localVue = createLocalVue()
localVue.use(PiniaVuePlugin)

Expand All @@ -27,4 +36,32 @@ describe("VFullLayout", () => {
const downloadButton = screen.getByText("audio-details.weblink")
expect(downloadButton).toHaveAttribute("href", audio.foreign_landing_url)
})

it("should send GET_MEDIA analytics event on button click", async () => {
const sendCustomEventMock = jest.fn()
useAnalytics.mockImplementation(() => ({
sendCustomEvent: sendCustomEventMock,
}))
const audio = getAudioObj()

render(VFullLayout, {
localVue,
pinia: createPinia(),
propsData: {
audio,
size: "s",
status: "playing",
currentTime: 1,
},
})

const downloadButton = screen.getByText("audio-details.weblink")
await fireEvent.click(downloadButton)

expect(sendCustomEventMock).toHaveBeenCalledWith("GET_MEDIA", {
id: audio.id,
mediaType: AUDIO,
provider: audio.provider,
})
})
})
@@ -0,0 +1,67 @@
import VueI18n from "vue-i18n"

import { createLocalVue } from "@vue/test-utils"
import { fireEvent, render, screen } from "@testing-library/vue"

import { PiniaVuePlugin, createPinia } from "~~/test/unit/test-utils/pinia"
import i18n from "~~/test/unit/test-utils/i18n"

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

import VImageDetailsPage from "~/pages/image/_id/index.vue"

jest.mock("~/composables/use-analytics", () => ({
useAnalytics: jest.fn(() => ({
sendCustomEvent: jest.fn(),
})),
}))

jest.mock("~/stores/media/single-result", () => ({
useSingleResultStore: jest.fn(() => ({
mediaType: "image",
mediaItem: imageObject,
})),
}))

const imageObject = {
id: "123",
provider: "test-provider",
foreign_landing_url: "https://test.com",
frontendMediaType: "image",
}

const localVue = createLocalVue()
localVue.use(PiniaVuePlugin)
localVue.use(VueI18n)

describe("VImageDetailsPage", () => {
it("should send GET_MEDIA analytics event on CTA button click", async () => {
const sendCustomEventMock = jest.fn()
useAnalytics.mockImplementation(() => ({
sendCustomEvent: sendCustomEventMock,
}))

render(VImageDetailsPage, {
localVue,
pinia: createPinia(),
i18n,
mocks: {
$route: {
params: {
meta: "url",
},
},
},
})

const downloadButton = screen.getByText("image-details.weblink")
await fireEvent.click(downloadButton)

expect(sendCustomEventMock).toHaveBeenCalledWith("GET_MEDIA", {
id: imageObject.id,
mediaType: IMAGE,
provider: imageObject.provider,
})
})
})
4 changes: 3 additions & 1 deletion frontend/test/unit/test-utils/i18n.js
Expand Up @@ -5,7 +5,9 @@ const messages = require("~/locales/en.json")
const i18n = new VueI18n({
locale: "en",
fallbackLocale: "en",
messages,
messages: {
en: messages,
},
})

export default i18n