Skip to content

Commit d3716bf

Browse files
chore: Remove product page feature flag (#3501)
* reove product page feature flag * Use resource.url for MITx Online CTA links instead of client-side rewrite CallToActionSection rewrote MITx Online course/program URLs to Learn product-page paths client-side (coursePageView/programPageView). The backend MITx Online ETL now populates resource.url with those same Learn product URLs (gated on has_product_page), so the rewrite is a duplicate. Drop the MITx Online branch in getResourceUrl and let those resources fall through to resource.url like every other resource. The result is behavior-preserving for published resources (same path, same-origin so still internal/no-UTM and client-navigated), and resources without a product page now correctly get no link rather than a dead product-page path derived from readable_id. ProgramAsCourseCard keeps its programPageView rewrite: its data comes straight from the MITx Online dashboard API and has no resource.url. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 92c1f8f commit d3716bf

11 files changed

Lines changed: 28 additions & 296 deletions

File tree

frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/ProgramAsCourseCard.test.tsx

Lines changed: 1 addition & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,10 @@ import { waitFor } from "@testing-library/react"
1414
import invariant from "tiny-invariant"
1515
import moment from "moment"
1616
import NiceModal from "@ebay/nice-modal-react"
17-
import { useFeatureFlagEnabled } from "posthog-js/react"
1817
import { UnenrollProgramDialog } from "./DashboardDialogs"
1918
import { mitxonlineLegacyUrl } from "@/common/mitxonline"
2019

2120
jest.mock("posthog-js/react")
22-
const mockedUseFeatureFlagEnabled = jest
23-
.mocked(useFeatureFlagEnabled)
24-
.mockImplementation(() => false)
2521

2622
describe("ProgramAsCourseCard", () => {
2723
setupLocationMock()
@@ -431,47 +427,7 @@ describe("ProgramAsCourseCard", () => {
431427
expect(certButton).not.toBeInTheDocument()
432428
})
433429

434-
test("shows legacy details link in context menu when product pages flag is disabled", async () => {
435-
mockedUseFeatureFlagEnabled.mockReturnValue(false)
436-
const cardData = setupCardData({ includeProgramEnrollment: true })
437-
438-
renderWithProviders(
439-
<ProgramAsCourseCard
440-
courseProgram={cardData.courseProgram}
441-
moduleCourses={cardData.moduleCourses}
442-
moduleEnrollmentsByCourseId={cardData.moduleEnrollmentsByCourseId}
443-
courseProgramEnrollment={cardData.courseProgramEnrollment}
444-
/>,
445-
)
446-
447-
await screen.findByText(cardData.courseProgram.title)
448-
const programCard = screen.getByTestId("program-as-course-card")
449-
await user.click(within(programCard).getAllByLabelText("More options")[0])
450-
451-
const detailsLink = await screen.findByRole("menuitem", {
452-
name: "View Course Details",
453-
})
454-
expect(detailsLink).toHaveAttribute(
455-
"href",
456-
expect.stringContaining(
457-
`/programs/${cardData.courseProgram.readable_id}`,
458-
),
459-
)
460-
expect(detailsLink).toHaveAttribute(
461-
"href",
462-
expect.stringContaining("ecom-service=true"),
463-
)
464-
465-
expect(
466-
screen.getByRole("menuitem", { name: "Program Record" }),
467-
).toHaveAttribute(
468-
"href",
469-
mitxonlineLegacyUrl(`/records/${cardData.courseProgram.id}/`),
470-
)
471-
})
472-
473-
test("shows product-page details link in context menu when product pages flag is enabled", async () => {
474-
mockedUseFeatureFlagEnabled.mockReturnValue(true)
430+
test("shows product-page details link in context menu", async () => {
475431
const cardData = setupCardData({ includeProgramEnrollment: true })
476432

477433
renderWithProviders(
@@ -504,7 +460,6 @@ describe("ProgramAsCourseCard", () => {
504460
})
505461

506462
test("clicking Unenroll menu item opens UnenrollProgramDialog with readable_id", async () => {
507-
mockedUseFeatureFlagEnabled.mockReturnValue(false)
508463
const cardData = setupCardData({ includeProgramEnrollment: true })
509464
invariant(cardData.courseProgramEnrollment)
510465
cardData.courseProgramEnrollment.enrollment_mode = "audit"
@@ -532,7 +487,6 @@ describe("ProgramAsCourseCard", () => {
532487
})
533488

534489
test("does not show Unenroll option in context menu for verified enrollment", async () => {
535-
mockedUseFeatureFlagEnabled.mockReturnValue(false)
536490
const cardData = setupCardData({ includeProgramEnrollment: true })
537491
invariant(cardData.courseProgramEnrollment)
538492
cardData.courseProgramEnrollment.enrollment_mode = "verified"

frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/ProgramAsCourseCard.tsx

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ import { RiAwardFill, RiMore2Line } from "@remixicon/react"
3737
import NiceModal from "@ebay/nice-modal-react"
3838
import { UnenrollProgramDialog } from "./DashboardDialogs"
3939
import { ProgramCertificateButton } from "./ProgramEnrollmentDisplay"
40-
import { useFeatureFlagEnabled } from "posthog-js/react"
41-
import { FeatureFlags } from "@/common/feature_flags"
4240
import { programPageView } from "@/common/urls"
4341

4442
const ProgramCardRoot = styled.div(({ theme }) => ({
@@ -284,15 +282,12 @@ const getContextMenuItems = (
284282
resource: ProgramAsCourse,
285283
enrollmentMode: string | null | undefined,
286284
additionalItems: SimpleMenuItem[] = [],
287-
useProductPages = false,
288285
) => {
289286
const menuItems = []
290-
const detailsUrl = useProductPages
291-
? programPageView({
292-
readable_id: resource.readable_id,
293-
display_mode: "course",
294-
})
295-
: mitxonlineLegacyUrl(`/programs/${resource.readable_id}`)
287+
const detailsUrl = programPageView({
288+
readable_id: resource.readable_id,
289+
display_mode: "course",
290+
})
296291

297292
const courseMenuItems = []
298293

@@ -405,9 +400,6 @@ const ProgramAsCourseCard: React.FC<ProgramAsCourseCardProps> = ({
405400
className,
406401
onUpgradeError,
407402
}) => {
408-
const useProductPages = useFeatureFlagEnabled(
409-
FeatureFlags.MitxOnlineProductPages,
410-
)
411403
const moduleRequirementSection = courseProgram?.req_tree?.find(
412404
(node) => node.data.node_type === "operator",
413405
)
@@ -488,7 +480,6 @@ const ProgramAsCourseCard: React.FC<ProgramAsCourseCardProps> = ({
488480
courseProgram,
489481
courseProgramEnrollment?.enrollment_mode,
490482
contextMenuItems,
491-
useProductPages ?? false,
492483
)
493484

494485
const contextMenu = (

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

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,12 @@ import { notFound } from "next/navigation"
1616
import { useStayUpdatedEnv } from "./test-utils/stayUpdated"
1717

1818
import { useFeatureFlagEnabled } from "posthog-js/react"
19-
import { useFeatureFlagsLoaded } from "@/common/useFeatureFlagsLoaded"
2019
import invariant from "tiny-invariant"
2120
import { getOutlineCoursewareId } from "./util"
2221
import { FeatureFlags } from "@/common/feature_flags"
2322

2423
jest.mock("posthog-js/react")
2524
const mockedUseFeatureFlagEnabled = jest.mocked(useFeatureFlagEnabled)
26-
jest.mock("@/common/useFeatureFlagsLoaded")
27-
const mockedUseFeatureFlagsLoaded = jest.mocked(useFeatureFlagsLoaded)
2825

2926
const makeCourse = factories.courses.course
3027
const makePage = factories.pages.coursePageItem
@@ -107,35 +104,8 @@ const setupApis = ({
107104
describe("CoursePage", () => {
108105
beforeEach(() => {
109106
mockedUseFeatureFlagEnabled.mockReturnValue(true)
110-
mockedUseFeatureFlagsLoaded.mockReturnValue(true)
111107
})
112108

113-
test.each([
114-
{ flagsLoaded: true, isEnabled: true, shouldNotFound: false },
115-
{ flagsLoaded: true, isEnabled: false, shouldNotFound: true },
116-
{ flagsLoaded: false, isEnabled: true, shouldNotFound: false },
117-
{ flagsLoaded: false, isEnabled: false, shouldNotFound: false },
118-
])(
119-
"Calls noFound if and only the feature flag is disabled",
120-
async ({ flagsLoaded, isEnabled, shouldNotFound }) => {
121-
mockedUseFeatureFlagEnabled.mockReturnValue(isEnabled)
122-
mockedUseFeatureFlagsLoaded.mockReturnValue(flagsLoaded)
123-
124-
const course = makeCourse()
125-
const page = makePage({ course_details: course })
126-
setupApis({ course, page })
127-
renderWithProviders(<CoursePage readableId={course.readable_id} />, {
128-
url: `/courses/${course.readable_id}/`,
129-
})
130-
131-
if (shouldNotFound) {
132-
expect(notFound).toHaveBeenCalled()
133-
} else {
134-
expect(notFound).not.toHaveBeenCalled()
135-
}
136-
},
137-
)
138-
139109
test("Page has expected headings", async () => {
140110
const course = makeCourse()
141111
const page = makePage({ course_details: course })

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

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import WhatYoullLearnSection from "./WhatYoullLearnSection"
1919
import HowYoullLearnSection from "./HowYoullLearnSection"
2020
import { DEFAULT_RESOURCE_IMG } from "ol-utilities"
2121
import { isVerifiedEnrollmentMode } from "@/common/mitxonline"
22-
import { useFeatureFlagsLoaded } from "@/common/useFeatureFlagsLoaded"
2322
import CourseInfoBox from "./InfoBoxCourse"
2423
import CourseEnrollmentButton from "./CourseEnrollmentButton"
2524
import CourseOutlineSection from "./CourseOutlineSection"
@@ -54,15 +53,9 @@ const CoursePage: React.FC<CoursePageProps> = ({ readableId }) => {
5453
...coursesQueries.courseOutline(effectiveOutlineCoursewareId ?? ""),
5554
enabled: Boolean(effectiveOutlineCoursewareId),
5655
})
57-
const enabled = useFeatureFlagEnabled(FeatureFlags.MitxOnlineProductPages)
5856
const showCourseOutline = useFeatureFlagEnabled(
5957
FeatureFlags.CourseOutlineSection,
6058
)
61-
const flagsLoaded = useFeatureFlagsLoaded()
62-
63-
if (!enabled) {
64-
return flagsLoaded ? notFound() : null
65-
}
6659

6760
const doneLoading = pages.isSuccess && courses.isSuccess
6861

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

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,14 @@ import {
2626
import { assertHeadings } from "ol-test-utilities"
2727
import ProgramAsCoursePage from "./ProgramAsCoursePage"
2828
import { notFound } from "next/navigation"
29-
import { useFeatureFlagEnabled } from "posthog-js/react"
3029
import {
3130
useStayUpdatedEnv,
3231
PROGRAM_HIDE_STAY_UPDATED_CASES,
3332
} from "./test-utils/stayUpdated"
3433
import invariant from "tiny-invariant"
35-
import { useFeatureFlagsLoaded } from "@/common/useFeatureFlagsLoaded"
3634
import { getIdsFromReqTree } from "@/common/mitxonline"
3735

3836
jest.mock("posthog-js/react")
39-
const mockedUseFeatureFlagEnabled = jest.mocked(useFeatureFlagEnabled)
40-
jest.mock("@/common/useFeatureFlagsLoaded")
41-
const mockedUseFeatureFlagsLoaded = jest.mocked(useFeatureFlagsLoaded)
4237

4338
const makeProgramAsCourse: typeof factories.programs.program = (
4439
overrides = {},
@@ -121,11 +116,6 @@ const setupApis = ({
121116
}
122117

123118
describe("ProgramAsCoursePage", () => {
124-
beforeEach(() => {
125-
mockedUseFeatureFlagEnabled.mockReturnValue(true)
126-
mockedUseFeatureFlagsLoaded.mockReturnValue(true)
127-
})
128-
129119
test("Page has expected headings", async () => {
130120
const reqTree = new RequirementTreeBuilder()
131121
const op = reqTree.addOperator({ operator: "all_of" })

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

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import { pagesQueries } from "api/mitxonline-hooks/pages"
66
import { useQuery } from "@tanstack/react-query"
77
import { styled } from "@mitodl/smoot-design"
88
import { programsQueries } from "api/mitxonline-hooks/programs"
9-
import { useFeatureFlagEnabled } from "posthog-js/react"
10-
import { FeatureFlags } from "@/common/feature_flags"
119
import { notFound } from "next/navigation"
1210
import { HeadingIds, parseReqTree } from "./util"
1311
import type { RequirementItem } from "./util"
@@ -23,7 +21,6 @@ import ProductPageTemplate from "./ProductPageTemplate"
2321
import WhatYoullLearnSection from "./WhatYoullLearnSection"
2422
import HowYoullLearnSection from "./HowYoullLearnSection"
2523
import { DEFAULT_RESOURCE_IMG, pluralize } from "ol-utilities"
26-
import { useFeatureFlagsLoaded } from "@/common/useFeatureFlagsLoaded"
2724
import ProgramAsCourseInfoBox from "./InfoBoxProgramAsCourse"
2825
import ProgramEnrollmentButton from "./ProgramEnrollmentButton"
2926
import { coursesQueries } from "api/mitxonline-hooks/courses"
@@ -241,13 +238,6 @@ const ProgramAsCoursePage: React.FC<ProgramAsCoursePageProps> = ({
241238
enabled: programIds.length > 0,
242239
})
243240

244-
const enabled = useFeatureFlagEnabled(FeatureFlags.MitxOnlineProductPages)
245-
const flagsLoaded = useFeatureFlagsLoaded()
246-
247-
if (!enabled) {
248-
return flagsLoaded ? notFound() : null
249-
}
250-
251241
const isLoading = pages.isLoading || programs.isLoading
252242

253243
if (!page || !program) {

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

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,18 @@ import {
3131
PROGRAM_HIDE_STAY_UPDATED_CASES,
3232
} from "./test-utils/stayUpdated"
3333

34-
import { useFeatureFlagEnabled, usePostHog } from "posthog-js/react"
34+
import { usePostHog } from "posthog-js/react"
3535
import invariant from "tiny-invariant"
36-
import { useFeatureFlagsLoaded } from "@/common/useFeatureFlagsLoaded"
3736
import { getIdsFromReqTree } from "@/common/mitxonline"
3837
import { faker } from "@faker-js/faker/locale/en"
3938
import { PostHogEvents } from "@/common/constants"
4039

4140
jest.mock("posthog-js/react")
42-
const mockedUseFeatureFlagEnabled = jest.mocked(useFeatureFlagEnabled)
4341
const mockCapture = jest.fn()
4442
jest.mocked(usePostHog).mockReturnValue(
4543
// @ts-expect-error Not mocking all of posthog
4644
{ capture: mockCapture },
4745
)
48-
jest.mock("@/common/useFeatureFlagsLoaded")
49-
const mockedUseFeatureFlagsLoaded = jest.mocked(useFeatureFlagsLoaded)
5046

5147
const makeProgram = factories.programs.program
5248
const makePage = factories.pages.programPageItem
@@ -225,36 +221,6 @@ const setupApis = ({
225221
}
226222

227223
describe("ProgramPage", () => {
228-
beforeEach(() => {
229-
mockedUseFeatureFlagEnabled.mockReturnValue(true)
230-
})
231-
232-
test.each([
233-
{ flagsLoaded: true, isEnabled: true, shouldNotFound: false },
234-
{ flagsLoaded: true, isEnabled: false, shouldNotFound: true },
235-
{ flagsLoaded: false, isEnabled: true, shouldNotFound: false },
236-
{ flagsLoaded: false, isEnabled: false, shouldNotFound: false },
237-
])(
238-
"Calls notFound if and only if the feature flag is disabled",
239-
async ({ flagsLoaded, isEnabled, shouldNotFound }) => {
240-
mockedUseFeatureFlagEnabled.mockReturnValue(isEnabled)
241-
mockedUseFeatureFlagsLoaded.mockReturnValue(flagsLoaded)
242-
243-
const program = makeProgram({ ...makeReqs() })
244-
const page = makePage({ program_details: program })
245-
setupApis({ program, page })
246-
renderWithProviders(<ProgramPage readableId={program.readable_id} />, {
247-
url: `/programs/${program.readable_id}/`,
248-
})
249-
250-
if (shouldNotFound) {
251-
expect(notFound).toHaveBeenCalled()
252-
} else {
253-
expect(notFound).not.toHaveBeenCalled()
254-
}
255-
},
256-
)
257-
258224
test("Page has expected headings", async () => {
259225
const program = makeProgram({
260226
...makeReqs({

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

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import { pagesQueries } from "api/mitxonline-hooks/pages"
77
import { useQuery } from "@tanstack/react-query"
88
import { styled } from "@mitodl/smoot-design"
99
import { programsQueries } from "api/mitxonline-hooks/programs"
10-
import { useFeatureFlagEnabled } from "posthog-js/react"
11-
import { FeatureFlags } from "@/common/feature_flags"
1210
import { notFound } from "next/navigation"
1311
import { HeadingIds, parseReqTree, RequirementData } from "./util"
1412
import {
@@ -27,7 +25,6 @@ import type {
2725
CourseWithCourseRunsSerializerV2,
2826
} from "@mitodl/mitxonline-api-axios/v2"
2927
import { DEFAULT_RESOURCE_IMG, pluralize } from "ol-utilities"
30-
import { useFeatureFlagsLoaded } from "@/common/useFeatureFlagsLoaded"
3128
import ProgramInfoBox from "./InfoBoxProgram"
3229
import { coursesQueries } from "api/mitxonline-hooks/courses"
3330
import MitxOnlineResourceCard from "./MitxOnlineResourceCard"
@@ -258,13 +255,6 @@ const ProgramPage: React.FC<ProgramPageProps> = ({ readableId }) => {
258255
enabled: programIds.length > 0,
259256
})
260257

261-
const enabled = useFeatureFlagEnabled(FeatureFlags.MitxOnlineProductPages)
262-
const flagsLoaded = useFeatureFlagsLoaded()
263-
264-
if (!enabled) {
265-
return flagsLoaded ? notFound() : null
266-
}
267-
268258
const isLoading = pages.isLoading || programs.isLoading
269259

270260
if (!page || !program) {

frontends/main/src/common/feature_flags.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ export enum FeatureFlags {
1010
UniversalAI = "universal-ai",
1111
UniversalAISearchBanner = "universal-ai-search-banner",
1212
VideoShorts = "video-shorts",
13-
MitxOnlineProductPages = "mitxonline-product-pages",
1413
CourseOutlineSection = "course-outline-section",
1514
OcwProductPages = "ocw-product-pages",
1615
VideoPlaylistPage = "video-playlist-page",

0 commit comments

Comments
 (0)