Skip to content

Commit 5ccb9f3

Browse files
committed
feedback
1 parent 7e1a285 commit 5ccb9f3

10 files changed

Lines changed: 372 additions & 23 deletions

File tree

frontends/main/src/app-pages/HomePage/HomePage.test.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import * as routes from "@/common/urls"
1919
import { FeatureFlags } from "@/common/feature_flags"
2020
import { assertHeadings } from "ol-test-utilities"
2121
import { useFeatureFlagEnabled, usePostHog } from "posthog-js/react"
22+
import { PostHogEvents } from "@/common/constants"
2223

2324
jest.mock("posthog-js/react")
2425
const mockedUseFeatureFlagEnabled = jest.mocked(useFeatureFlagEnabled)
@@ -128,6 +129,27 @@ describe("UAI Announcement Card", () => {
128129
})
129130
assertLinksTo(cta, { pathname: "/programs/program-v1:UAI+B2C" })
130131
})
132+
133+
test("CTA click fires cta_clicked with label and readableId", async () => {
134+
setupAPIs()
135+
mockedUseFeatureFlagEnabled.mockImplementation(
136+
(flag) => flag === FeatureFlags.UniversalAI,
137+
)
138+
process.env.NEXT_PUBLIC_POSTHOG_API_KEY = "test-key"
139+
renderWithProviders(<HomePage heroImageIndex={1} />)
140+
const cta = await screen.findByRole("link", {
141+
name: "Learn about Universal AI",
142+
})
143+
await user.click(cta)
144+
expect(mockedPostHogCapture).toHaveBeenCalledWith(
145+
PostHogEvents.CallToActionClicked,
146+
expect.objectContaining({
147+
label: "Learn about Universal AI",
148+
readableId: "program-v1:UAI+B2C",
149+
}),
150+
)
151+
delete process.env.NEXT_PUBLIC_POSTHOG_API_KEY
152+
})
131153
})
132154

133155
describe("Home Page Hero", () => {

frontends/main/src/app-pages/ProductPages/CourseEnrollmentButton.test.tsx

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ import {
1515
} from "api/mitxonline-test-utils"
1616
import { mitxonlineLegacyUrl } from "@/common/mitxonline"
1717
import * as routes from "@/common/urls"
18+
import { usePostHog } from "posthog-js/react"
19+
import { PostHogEvents } from "@/common/constants"
20+
21+
jest.mock("posthog-js/react", () => ({
22+
...jest.requireActual("posthog-js/react"),
23+
usePostHog: jest.fn(),
24+
}))
25+
const mockCapture = jest.fn()
26+
jest.mocked(usePostHog).mockReturnValue(
27+
// @ts-expect-error Not mocking all of posthog
28+
{ capture: mockCapture },
29+
)
1830

1931
const makeCourse = mitxFactories.courses.course
2032
const makeRun = mitxFactories.courses.courseRun
@@ -505,4 +517,68 @@ describe("CourseEnrollmentButton", () => {
505517
expect(button).toBeInTheDocument()
506518
expect(button).toBeDisabled()
507519
})
520+
521+
describe("PostHog tracking", () => {
522+
beforeEach(() => {
523+
process.env.NEXT_PUBLIC_POSTHOG_API_KEY = "test-key"
524+
mockCapture.mockClear()
525+
})
526+
afterEach(() => {
527+
delete process.env.NEXT_PUBLIC_POSTHOG_API_KEY
528+
})
529+
530+
test("fires cta_clicked when authenticated user clicks enroll", async () => {
531+
const run = makeRun({
532+
is_archived: false,
533+
is_enrollable: true,
534+
enrollment_modes: [makeEnrollmentMode({ requires_payment: false })],
535+
})
536+
const course = makeCourse({ next_run_id: run.id, courseruns: [run] })
537+
setMockResponse.get(
538+
urls.userMe.get(),
539+
makeUser({ is_authenticated: true }),
540+
)
541+
542+
renderWithProviders(<CourseEnrollmentButton course={course} />)
543+
await user.click(
544+
await screen.findByRole("button", { name: "Enroll for Free" }),
545+
)
546+
547+
expect(mockCapture).toHaveBeenCalledWith(
548+
PostHogEvents.CallToActionClicked,
549+
expect.objectContaining({
550+
resourceId: course.id,
551+
readableId: course.readable_id,
552+
resourceType: "course",
553+
}),
554+
)
555+
})
556+
557+
test("fires cta_clicked when unauthenticated user clicks enroll", async () => {
558+
const run = makeRun({
559+
is_archived: false,
560+
is_enrollable: true,
561+
enrollment_modes: [makeEnrollmentMode({ requires_payment: false })],
562+
})
563+
const course = makeCourse({ next_run_id: run.id, courseruns: [run] })
564+
setMockResponse.get(
565+
urls.userMe.get(),
566+
makeUser({ is_authenticated: false }),
567+
)
568+
569+
renderWithProviders(<CourseEnrollmentButton course={course} />)
570+
await user.click(
571+
await screen.findByRole("button", { name: "Enroll for Free" }),
572+
)
573+
574+
expect(mockCapture).toHaveBeenCalledWith(
575+
PostHogEvents.CallToActionClicked,
576+
expect.objectContaining({
577+
resourceId: course.id,
578+
readableId: course.readable_id,
579+
resourceType: "course",
580+
}),
581+
)
582+
})
583+
})
508584
})

frontends/main/src/app-pages/ProductPages/CourseEnrollmentButton.tsx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,16 @@ const CourseEnrollmentButton: React.FC<CourseEnrollmentButtonProps> = ({
9393
const handleClick: React.MouseEventHandler<HTMLButtonElement> = (e) => {
9494
if (me.isLoading) {
9595
return
96-
} else if (me.data?.is_authenticated) {
97-
if (process.env.NEXT_PUBLIC_POSTHOG_API_KEY) {
98-
posthog.capture(PostHogEvents.CallToActionClicked, {
99-
resourceId: course.id,
100-
readableId: course.readable_id,
101-
resourceType: "course",
102-
label: getButtonText(nextRun, price?.finalPrice),
103-
})
104-
}
96+
}
97+
if (process.env.NEXT_PUBLIC_POSTHOG_API_KEY) {
98+
posthog.capture(PostHogEvents.CallToActionClicked, {
99+
resourceId: course.id,
100+
readableId: course.readable_id,
101+
resourceType: "course",
102+
label: getButtonText(nextRun, price?.finalPrice),
103+
})
104+
}
105+
if (me.data?.is_authenticated) {
105106
if (enrollmentDecision.type === "dialog") {
106107
NiceModal.show(CourseEnrollmentDialog, { course })
107108
} else if (enrollmentDecision.type === "checkout") {

frontends/main/src/app-pages/ProductPages/ProgramEnrollmentButton.test.tsx

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,21 @@ import {
1313
urls as mitxUrls,
1414
factories as mitxFactories,
1515
} from "api/mitxonline-test-utils"
16-
import { useFeatureFlagEnabled } from "posthog-js/react"
16+
import { useFeatureFlagEnabled, usePostHog } from "posthog-js/react"
1717
import { programView } from "@/common/urls"
1818
import { mitxonlineLegacyUrl } from "@/common/mitxonline"
1919
import * as routes from "@/common/urls"
20+
import { PostHogEvents } from "@/common/constants"
2021

2122
jest.mock("posthog-js/react")
2223
const mockedUseFeatureFlagEnabled = jest
2324
.mocked(useFeatureFlagEnabled)
2425
.mockImplementation(() => false)
26+
const mockCapture = jest.fn()
27+
jest.mocked(usePostHog).mockReturnValue(
28+
// @ts-expect-error Not mocking all of posthog
29+
{ capture: mockCapture },
30+
)
2531

2632
const makeProgram = mitxFactories.programs.program
2733
const makeProgramEnrollment = mitxFactories.enrollment.programEnrollmentV3
@@ -399,4 +405,70 @@ describe("ProgramEnrollmentButton", () => {
399405

400406
screen.getByTestId("signup-popover")
401407
})
408+
409+
describe("PostHog tracking", () => {
410+
beforeEach(() => {
411+
process.env.NEXT_PUBLIC_POSTHOG_API_KEY = "test-key"
412+
mockCapture.mockClear()
413+
})
414+
afterEach(() => {
415+
delete process.env.NEXT_PUBLIC_POSTHOG_API_KEY
416+
})
417+
418+
test("fires cta_clicked when authenticated user clicks enroll", async () => {
419+
const program = makeProgram({
420+
enrollment_modes: [makeEnrollmentMode({ requires_payment: false })],
421+
})
422+
setMockResponse.get(mitxUrls.programEnrollments.enrollmentsListV3(), [])
423+
setMockResponse.get(
424+
urls.userMe.get(),
425+
makeUser({ is_authenticated: true }),
426+
)
427+
428+
renderWithProviders(
429+
<ProgramEnrollmentButton program={program} variant="primary" />,
430+
)
431+
await user.click(
432+
await screen.findByRole("button", { name: "Enroll for Free" }),
433+
)
434+
435+
expect(mockCapture).toHaveBeenCalledWith(
436+
PostHogEvents.CallToActionClicked,
437+
expect.objectContaining({
438+
resourceId: program.id,
439+
readableId: program.readable_id,
440+
resourceType: "program",
441+
}),
442+
)
443+
})
444+
445+
test("fires cta_clicked when unauthenticated user clicks enroll", async () => {
446+
const program = makeProgram({
447+
enrollment_modes: [makeEnrollmentMode({ requires_payment: false })],
448+
})
449+
setMockResponse.get(mitxUrls.programEnrollments.enrollmentsListV3(), [], {
450+
code: 403,
451+
})
452+
setMockResponse.get(
453+
urls.userMe.get(),
454+
makeUser({ is_authenticated: false }),
455+
)
456+
457+
renderWithProviders(
458+
<ProgramEnrollmentButton program={program} variant="primary" />,
459+
)
460+
await user.click(
461+
await screen.findByRole("button", { name: "Enroll for Free" }),
462+
)
463+
464+
expect(mockCapture).toHaveBeenCalledWith(
465+
PostHogEvents.CallToActionClicked,
466+
expect.objectContaining({
467+
resourceId: program.id,
468+
readableId: program.readable_id,
469+
resourceType: "program",
470+
}),
471+
)
472+
})
473+
})
402474
})

frontends/main/src/app-pages/ProductPages/ProgramEnrollmentButton.tsx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,16 @@ const ProgramEnrollmentButton: React.FC<ProgramEnrollmentButtonProps> = ({
8787
const handleClick: React.MouseEventHandler<HTMLButtonElement> = (e) => {
8888
if (enrollments.isLoading || me.isLoading) {
8989
return
90-
} else if (me.data?.is_authenticated) {
91-
if (process.env.NEXT_PUBLIC_POSTHOG_API_KEY) {
92-
posthog.capture(PostHogEvents.CallToActionClicked, {
93-
resourceId: program.id,
94-
readableId: program.readable_id,
95-
resourceType: "program",
96-
label: getEnrollButtonText(),
97-
})
98-
}
90+
}
91+
if (process.env.NEXT_PUBLIC_POSTHOG_API_KEY) {
92+
posthog.capture(PostHogEvents.CallToActionClicked, {
93+
resourceId: program.id,
94+
readableId: program.readable_id,
95+
resourceType: "program",
96+
label: getEnrollButtonText(),
97+
})
98+
}
99+
if (me.data?.is_authenticated) {
99100
if (enrollmentType === "paid" && program.products[0]) {
100101
replaceBasketItem.mutate(program.products[0].id)
101102
} else if (enrollmentType === "free") {

frontends/main/src/app-pages/ProductPages/ProgramPage.test.tsx

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@ import type {
1616
CourseWithCourseRunsSerializerV2,
1717
} from "@mitodl/mitxonline-api-axios/v2"
1818
import { DisplayModeEnum } from "@mitodl/mitxonline-api-axios/v2"
19-
import { renderWithProviders, waitFor, screen, within } from "@/test-utils"
19+
import {
20+
renderWithProviders,
21+
waitFor,
22+
screen,
23+
within,
24+
user,
25+
} from "@/test-utils"
2026
import ProgramPage from "./ProgramPage"
2127
import { assertHeadings } from "ol-test-utilities"
2228
import { notFound } from "next/navigation"
@@ -25,14 +31,20 @@ import {
2531
PROGRAM_HIDE_STAY_UPDATED_CASES,
2632
} from "./test-utils/stayUpdated"
2733

28-
import { useFeatureFlagEnabled } from "posthog-js/react"
34+
import { useFeatureFlagEnabled, usePostHog } from "posthog-js/react"
2935
import invariant from "tiny-invariant"
3036
import { useFeatureFlagsLoaded } from "@/common/useFeatureFlagsLoaded"
3137
import { getIdsFromReqTree } from "@/common/mitxonline"
3238
import { faker } from "@faker-js/faker/locale/en"
39+
import { PostHogEvents } from "@/common/constants"
3340

3441
jest.mock("posthog-js/react")
3542
const mockedUseFeatureFlagEnabled = jest.mocked(useFeatureFlagEnabled)
43+
const mockCapture = jest.fn()
44+
jest.mocked(usePostHog).mockReturnValue(
45+
// @ts-expect-error Not mocking all of posthog
46+
{ capture: mockCapture },
47+
)
3648
jest.mock("@/common/useFeatureFlagsLoaded")
3749
const mockedUseFeatureFlagsLoaded = jest.mocked(useFeatureFlagsLoaded)
3850

@@ -640,6 +652,31 @@ describe("ProgramPage", () => {
640652
})
641653
})
642654

655+
test("clicking a requirement card fires course_card_clicked", async () => {
656+
const program = makeProgram({
657+
...makeReqs({ required: { count: 1, title: "Required" } }),
658+
})
659+
const page = makePage({ program_details: program })
660+
const { courses } = setupApis({ program, page })
661+
process.env.NEXT_PUBLIC_POSTHOG_API_KEY = "test-key"
662+
mockCapture.mockClear()
663+
664+
renderWithProviders(<ProgramPage readableId={program.readable_id} />)
665+
666+
const course = courses[0]
667+
const card = await screen.findByRole("heading", { name: course.title })
668+
await user.click(card)
669+
670+
expect(mockCapture).toHaveBeenCalledWith(
671+
PostHogEvents.CourseCardClicked,
672+
expect.objectContaining({
673+
resourceId: course.id,
674+
readableId: course.readable_id,
675+
}),
676+
)
677+
delete process.env.NEXT_PUBLIC_POSTHOG_API_KEY
678+
})
679+
643680
describe("Stay Updated button", () => {
644681
useStayUpdatedEnv()
645682

frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,21 @@ import invariant from "tiny-invariant"
1616
import { Permission } from "api/hooks/user"
1717
import { assertHeadings, ControlledPromise } from "ol-test-utilities"
1818
import { act } from "@testing-library/react"
19-
import { useFeatureFlagEnabled } from "posthog-js/react"
19+
import { useFeatureFlagEnabled, usePostHog } from "posthog-js/react"
20+
import { PostHogEvents } from "@/common/constants"
2021

2122
jest.mock("posthog-js/react", () => ({
2223
...jest.requireActual("posthog-js/react"),
2324
useFeatureFlagEnabled: jest.fn(),
24-
usePostHog: jest.fn(() => ({ capture: jest.fn() })),
25+
usePostHog: jest.fn(),
2526
}))
2627

2728
const mockedUseFeatureFlagEnabled = jest.mocked(useFeatureFlagEnabled)
29+
const mockCapture = jest.fn()
30+
jest.mocked(usePostHog).mockReturnValue(
31+
// @ts-expect-error Not mocking all of posthog
32+
{ capture: mockCapture },
33+
)
2834

2935
const DEFAULT_SEARCH_RESPONSE: LearningResourcesSearchResponse = {
3036
count: 0,
@@ -1060,4 +1066,24 @@ describe("UniversalAIBanner", () => {
10601066
expect(screen.queryByText("Universal AI")).not.toBeInTheDocument()
10611067
expect(screen.queryByText("New on MIT Learn")).not.toBeInTheDocument()
10621068
})
1069+
1070+
test("clicking Learn More fires cta_clicked with label and readableId", async () => {
1071+
mockedUseFeatureFlagEnabled.mockReturnValue(true)
1072+
mockCapture.mockClear()
1073+
process.env.NEXT_PUBLIC_POSTHOG_API_KEY = "test-key"
1074+
setMockApiResponses({ search: { count: 10 } })
1075+
renderWithProviders(<SearchPage />)
1076+
const link = await screen.findByRole("link", {
1077+
name: "Learn more about Universal AI",
1078+
})
1079+
await user.click(link)
1080+
expect(mockCapture).toHaveBeenCalledWith(
1081+
PostHogEvents.CallToActionClicked,
1082+
expect.objectContaining({
1083+
label: "Learn More",
1084+
readableId: "program-v1:UAI+B2C",
1085+
}),
1086+
)
1087+
delete process.env.NEXT_PUBLIC_POSTHOG_API_KEY
1088+
})
10631089
})

0 commit comments

Comments
 (0)