Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions RELEASE.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
Release Notes
=============

Version 0.67.3
--------------

- Adds a feature flag to link ocw course urls to corresponding learn urls (/courses/o/*) (#3263)
- Min score for vector learning resources endpoint (#3285)
- Update dependency granian to v2.7.4 [SECURITY] (#3309)
- Update dependency litellm to v1.83.10 [SECURITY] (#3331)
- Update dependency urllib3 to v2.7.0 [SECURITY] (#3329)

Version 0.67.2 (Released May 12, 2026)
--------------

Expand Down
18 changes: 18 additions & 0 deletions frontends/api/src/generated/v0/api.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

72 changes: 72 additions & 0 deletions frontends/main/src/app-pages/SearchPage/SearchPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
LearningResourcesSearchResponse,
PaginatedLearningResourceOfferorDetailList,
} from "api"
import { ResourceTypeEnum } from "api"
import invariant from "tiny-invariant"
import { Permission } from "api/hooks/user"
import {
Expand Down Expand Up @@ -1084,6 +1085,77 @@ describe("UniversalAIBanner", () => {
})
})

test("Vector Hybrid Search with a query hides pagination and filters results locally", async () => {
const physicsTopic = factories.learningResources.topic({ name: "Physics" })
const chemistryTopic = factories.learningResources.topic({
name: "Chemistry",
})
const resources = [
factories.learningResources.resource({
title: "Physics Result",
resource_type: ResourceTypeEnum.Course,
topics: [physicsTopic],
}),
factories.learningResources.resource({
title: "Chemistry Result",
resource_type: ResourceTypeEnum.Course,
topics: [chemistryTopic],
}),
]

setMockApiResponses({
search: {
count: 2,
metadata: {
aggregations: {
topic: [
{ key: "Physics", doc_count: 1 },
{ key: "Chemistry", doc_count: 1 },
],
resource_type_group: [{ key: "course", doc_count: 2 }],
},
suggestions: [],
},
results: resources,
},
})
setMockResponse.get(urls.userMe.get(), {
is_learning_path_editor: true,
is_authenticated: true,
})

const { location } = renderWithProviders(<SearchPage />, {
url: "?vector_search=true&q=test",
})

await screen.findByText("Physics Result")
await screen.findByText("Chemistry Result")
expect(
screen.queryByRole("navigation", { name: /pagination/i }),
).not.toBeInTheDocument()

const initialVectorRequestCount = makeRequest.mock.calls.filter(
([method, url]) =>
method === "get" && url.includes(urls.search.vectorResources()),
).length

await user.click(screen.getByRole("button", { name: /Topic/i }))
await user.click(screen.getByRole("checkbox", { name: "Physics 1" }))

const searchParams = new URLSearchParams(location.current.search)
expect(searchParams.get("vector_search")).toBe("true")
expect(searchParams.get("q")).toBe("test")
expect(searchParams.get("topic")).toBe("Physics")
expect(await screen.findByText("Physics Result")).toBeVisible()
expect(screen.queryByText("Chemistry Result")).not.toBeInTheDocument()

const finalVectorRequestCount = makeRequest.mock.calls.filter(
([method, url]) =>
method === "get" && url.includes(urls.search.vectorResources()),
).length
expect(finalVectorRequestCount).toBe(initialVectorRequestCount)
})

test("clicking Learn More fires cta_clicked with label and readableId", async () => {
allowConsoleErrors()
mockedUseFeatureFlagEnabled.mockReturnValue(true)
Expand Down
1 change: 1 addition & 0 deletions frontends/main/src/common/feature_flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export enum FeatureFlags {
VideoShorts = "video-shorts",
MitxOnlineProductPages = "mitxonline-product-pages",
CourseOutlineSection = "course-outline-section",
OcwProductPages = "ocw-product-pages",
VideoPlaylistPage = "video-playlist-page",
PodcastDetailPage = "podcast-detail-page",
}
Expand Down
4 changes: 4 additions & 0 deletions frontends/main/src/common/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,7 @@ export const programPageView = (program: {
: PROGRAM_PAGE_VIEW
return generatePath(pattern, { readableId: program.readable_id })
}
export const ocwLearnPageView = (ocwUrl: string) => {
const url = new URL(ocwUrl)
return url.pathname.replace(/^\/courses/, "/courses/o")
}
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,111 @@ describe("CallToActionSection", () => {
})
})

describe("OCW product pages", () => {
const ocwSlug =
"16-01-unified-engineering-i-ii-iii-iv-fall-2005-spring-2006"
const ocwUrl = `https://ocw.mit.edu/courses/${ocwSlug}/`

const ocwResource: typeof factories.learningResources.resource = (
overrides,
) => {
return factories.learningResources.resource({
platform: { code: PlatformEnum.Ocw },
url: ocwUrl,
...overrides,
})
}

it("links to internal OCW page when flag is ON for OCW course", () => {
mockUseFeatureFlagEnabled.mockImplementation(
(flag) => flag === FeatureFlags.OcwProductPages,
)
const resource = ocwResource({ resource_type: ResourceTypeEnum.Course })

renderWithProviders(
<CallToActionSection
imgConfig={IMG_CONFIG}
resource={resource}
shareUrl="https://learn.mit.edu/test"
/>,
)

const link = screen.getByRole("link", { name: "Access Course Materials" })
expect(link).toHaveAttribute("href", `/courses/o/${ocwSlug}`)
expect(link.getAttribute("href")).not.toContain("utm_")
})

it("uses external URL with UTM params when flag is OFF for OCW course", () => {
mockUseFeatureFlagEnabled.mockReturnValue(false)
const resource = ocwResource({ resource_type: ResourceTypeEnum.Course })

renderWithProviders(
<CallToActionSection
imgConfig={IMG_CONFIG}
resource={resource}
shareUrl="https://learn.mit.edu/test"
/>,
)

const link = screen.getByRole("link", { name: "Access Course Materials" })
const href = link.getAttribute("href")
expect(href).toContain("ocw.mit.edu")
expect(href).toContain("utm_source=mit-learn")
expect(href).toContain("utm_medium=referral")
})

it("does not transform non-OCW course URL when only OcwProductPages flag is ON", () => {
mockUseFeatureFlagEnabled.mockImplementation(
(flag) => flag === FeatureFlags.OcwProductPages,
)
const resource = factories.learningResources.resource({
platform: { code: PlatformEnum.Mitxonline },
resource_type: ResourceTypeEnum.Course,
url: "https://courses.mitxonline.mit.edu/learn/course/some-course/",
})

renderWithProviders(
<CallToActionSection
imgConfig={IMG_CONFIG}
resource={resource}
shareUrl="https://learn.mit.edu/test"
/>,
)

const link = screen.getByRole("link", { name: "Learn More" })
const href = link.getAttribute("href")
expect(href).toContain("mitxonline.mit.edu")
expect(href).not.toContain("/courses/o/")
})

it("transforms OCW non-course URL when flag is ON", () => {
mockUseFeatureFlagEnabled.mockImplementation(
(flag) => flag === FeatureFlags.OcwProductPages,
)
const resource = ocwResource({
resource_type: ResourceTypeEnum.Video,
resource_category: "Lecture Video",
url: `${ocwUrl}resources/abc-def`,
})

renderWithProviders(
<CallToActionSection
imgConfig={IMG_CONFIG}
resource={resource}
shareUrl="https://learn.mit.edu/test"
/>,
)

const link = screen.getByRole("link", {
name: "Watch Video",
})
const href = link.getAttribute("href")
expect(href).toContain(`/courses/o/${ocwSlug}/resources/abc-def`)
expect(href).not.toContain("ocw.mit.edu")
expect(href).not.toContain("utm_")
})
})

describe("PostHog integration", () => {
it("calls posthog.capture when CTA link is clicked", () => {
const originalPostHogKey = process.env.NEXT_PUBLIC_POSTHOG_API_KEY
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
videoPlaylistPageView,
podcastPageView,
podcastEpisodePageView,
ocwLearnPageView,
} from "@/common/urls"
import { DisplayModeEnum } from "@mitodl/mitxonline-api-axios/v2"
import { FeatureFlags } from "@/common/feature_flags"
Expand Down Expand Up @@ -313,13 +314,15 @@ const appendUtmParams = (url?: string | null, resourceTitle?: string) => {
const getResourceUrl = (
resource: LearningResource,
{
ocwProductPages,
mitxonlineProductPages,
showVideoPlaylistPage,
showPodcastPage,
}: {
mitxonlineProductPages?: boolean
showVideoPlaylistPage?: boolean
showPodcastPage?: boolean
ocwProductPages?: boolean
},
) => {
if (
Expand Down Expand Up @@ -371,6 +374,15 @@ const getResourceUrl = (
)
}
}

if (
ocwProductPages &&
resource.platform?.code === PlatformEnum.Ocw &&
resource.url
) {
return ocwLearnPageView(resource.url)
}

return resource.url
}

Expand Down Expand Up @@ -398,6 +410,7 @@ const CallToActionSection = ({
const posthog = usePostHog()
const [shareExpanded, setShareExpanded] = useState(false)
const [copyText, setCopyText] = useState("Copy Link")
const ocwProductPages = useFeatureFlagEnabled(FeatureFlags.OcwProductPages)
const mitxonlineProductPages = useFeatureFlagEnabled(
FeatureFlags.MitxOnlineProductPages,
)
Expand Down Expand Up @@ -435,6 +448,7 @@ const CallToActionSection = ({
mitxonlineProductPages,
showVideoPlaylistPage,
showPodcastPage,
ocwProductPages,
}),
resource.title,
)
Expand Down
Loading
Loading