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
8 changes: 8 additions & 0 deletions RELEASE.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
Release Notes
=============

Version 0.66.5
--------------

- fix: remove the top border when there is only single item and removed the current episode from the more episode list (#3299)
- adds a "Receipt" menu item (#3260)
- feat: add podcast episode page and few fixes in podcast page (#3283)
- use Intl to get native language names (#3297)

Version 0.66.3 (Released May 05, 2026)
--------------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1443,7 +1443,7 @@ describe("ContractContent", () => {
expect(card).toHaveTextContent("Module in English")

await user.click(languageSelect)
await user.click(await screen.findByRole("option", { name: "Español" }))
await user.click(await screen.findByRole("option", { name: "español" }))

await waitFor(() => {
expect(root.getByTestId("enrollment-card-desktop")).toHaveTextContent(
Expand Down Expand Up @@ -1549,7 +1549,7 @@ describe("ContractContent", () => {
expect(card).toHaveTextContent("Collection English")

await user.click(languageSelect)
await user.click(await screen.findByRole("option", { name: "Español" }))
await user.click(await screen.findByRole("option", { name: "español" }))

await waitFor(() => {
expect(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2138,5 +2138,202 @@ describe.each([
screen.getByRole("menuitem", { name: "Unenroll" }),
).toBeInTheDocument()
})

test("Receipt menu item appears for verified course run enrollment", async () => {
setupUserApis()
const course = mitxOnlineCourse()
const run = course.courseruns[0]
const enrollment = mitxonline.factories.enrollment.courseEnrollment({
grades: [mitxonline.factories.enrollment.grade({ passed: true })],
enrollment_mode: EnrollmentMode.Verified,
run: { ...run, course },
})

renderWithProviders(
<DashboardCard
resource={{
type: DashboardType.CourseRunEnrollment,
data: enrollment,
}}
/>,
)

const card = getCard()
const contextMenuButton = within(card).getByRole("button", {
name: "More options",
})
await user.click(contextMenuButton)

expect(
screen.getByRole("menuitem", { name: "Receipt" }),
).toBeInTheDocument()
})

test("Receipt menu item does not appear for audit course run enrollment", async () => {
setupUserApis()
const course = mitxOnlineCourse()
const run = course.courseruns[0]
const enrollment = mitxonline.factories.enrollment.courseEnrollment({
grades: [],
enrollment_mode: EnrollmentMode.Audit,
run: { ...run, course },
})

renderWithProviders(
<DashboardCard
resource={{
type: DashboardType.CourseRunEnrollment,
data: enrollment,
}}
/>,
)

const card = getCard()
const contextMenuButton = within(card).getByRole("button", {
name: "More options",
})
await user.click(contextMenuButton)

expect(
screen.queryByRole("menuitem", { name: "Receipt" }),
).not.toBeInTheDocument()
})

test("Receipt menu item links to correct MITx Online URL for verified course run enrollment", async () => {
setupUserApis()
const course = mitxOnlineCourse()
const run = mitxonline.factories.courses.courseRun({ id: 42 })
const enrollment = mitxonline.factories.enrollment.courseEnrollment({
grades: [mitxonline.factories.enrollment.grade({ passed: true })],
enrollment_mode: EnrollmentMode.Verified,
run: { ...run, course },
})

const windowOpenSpy = jest
.spyOn(window, "open")
.mockImplementation(() => null)

renderWithProviders(
<DashboardCard
resource={{
type: DashboardType.CourseRunEnrollment,
data: enrollment,
}}
/>,
)

const card = getCard()
const contextMenuButton = within(card).getByRole("button", {
name: "More options",
})
await user.click(contextMenuButton)

const receiptItem = screen.getByRole("menuitem", { name: "Receipt" })
await user.click(receiptItem)

expect(windowOpenSpy).toHaveBeenCalledWith(
mitxonlineLegacyUrl("/orders/receipt/by-run/42/"),
"_blank",
"noopener,noreferrer",
)
windowOpenSpy.mockRestore()
})

test("Receipt menu item appears for verified program enrollment", async () => {
setupUserApis()
const program = mitxonline.factories.programs.simpleProgram()
const programEnrollment =
mitxonline.factories.enrollment.programEnrollmentV3({
program,
enrollment_mode: EnrollmentMode.Verified,
})

renderWithProviders(
<DashboardCard
resource={{
type: DashboardType.ProgramEnrollment,
data: programEnrollment,
}}
/>,
)

const card = getCard()
const contextMenuButton = within(card).getByRole("button", {
name: "More options",
})
await user.click(contextMenuButton)

expect(
screen.getByRole("menuitem", { name: "Receipt" }),
).toBeInTheDocument()
})

test("Receipt menu item does not appear for audit program enrollment", async () => {
setupUserApis()
const program = mitxonline.factories.programs.simpleProgram()
const programEnrollment =
mitxonline.factories.enrollment.programEnrollmentV3({
program,
enrollment_mode: EnrollmentMode.Audit,
})

renderWithProviders(
<DashboardCard
resource={{
type: DashboardType.ProgramEnrollment,
data: programEnrollment,
}}
/>,
)

const card = getCard()
const contextMenuButton = within(card).getByRole("button", {
name: "More options",
})
await user.click(contextMenuButton)

expect(
screen.queryByRole("menuitem", { name: "Receipt" }),
).not.toBeInTheDocument()
})

test("Receipt menu item links to correct MITx Online URL for verified program enrollment", async () => {
setupUserApis()
const program = mitxonline.factories.programs.simpleProgram({ id: 99 })
const programEnrollment =
mitxonline.factories.enrollment.programEnrollmentV3({
program,
enrollment_mode: EnrollmentMode.Verified,
})

const windowOpenSpy = jest
.spyOn(window, "open")
.mockImplementation(() => null)

renderWithProviders(
<DashboardCard
resource={{
type: DashboardType.ProgramEnrollment,
data: programEnrollment,
}}
/>,
)

const card = getCard()
const contextMenuButton = within(card).getByRole("button", {
name: "More options",
})
await user.click(contextMenuButton)

const receiptItem = screen.getByRole("menuitem", { name: "Receipt" })
await user.click(receiptItem)

expect(windowOpenSpy).toHaveBeenCalledWith(
mitxonlineLegacyUrl("/orders/receipt/by-program/99/"),
"_blank",
"noopener,noreferrer",
)
windowOpenSpy.mockRestore()
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { useFeatureFlagEnabled } from "posthog-js/react"
import { FeatureFlags } from "@/common/feature_flags"

import { EnrollmentStatusIndicator } from "./EnrollmentStatusIndicator"
import { getReceiptMenuItem } from "./receiptMenuItem"
import {
EmailSettingsDialog,
JustInTimeDialog,
Expand All @@ -37,10 +38,10 @@ import { mitxUserQueries } from "api/mitxonline-hooks/user"
import { useQuery } from "@tanstack/react-query"
import { coursePageView, programPageView, programView } from "@/common/urls"
import {
mitxonlineLegacyUrl,
getCourseEnrollmentAction,
getEnrollmentType,
isVerifiedEnrollmentMode,
mitxonlineLegacyUrl,
} from "@/common/mitxonline"
import { useReplaceBasketItem } from "api/mitxonline-hooks/baskets"
import { EnrollmentStatus, getBestRun, getEnrollmentStatus } from "./helpers"
Expand Down Expand Up @@ -220,6 +221,12 @@ const getContextMenuItems = (
},
})
}

const receiptMenuItem = getReceiptMenuItem(
resource.data.enrollment_mode,
`/orders/receipt/by-program/${program.id}/`,
)
if (receiptMenuItem) menuItems.push(receiptMenuItem)
}
if (resource.type === DashboardType.CourseRunEnrollment) {
const detailsUrl = useProductPages
Expand Down Expand Up @@ -259,6 +266,12 @@ const getContextMenuItems = (
},
)

const receiptMenuItem = getReceiptMenuItem(
resource.data.enrollment_mode,
`/orders/receipt/by-run/${resource.data.run.id}/`,
)
if (receiptMenuItem) courseMenuItems.push(receiptMenuItem)

menuItems.push(...courseMenuItems)
}
return [...menuItems, ...additionalItems]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2543,7 +2543,7 @@ describe("EnrollmentDisplay", () => {
expect(card).toHaveTextContent("Start Course")

await user.click(languageSelect)
await user.click(await screen.findByRole("option", { name: "Español" }))
await user.click(await screen.findByRole("option", { name: "español" }))

const desktopCard = await screen.findByTestId("enrollment-card-desktop")
await within(desktopCard).findByText("Modulo en Espanol")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import * as mitxonline from "api/mitxonline-test-utils"
import { EnrollmentModeEnum } from "@mitodl/mitxonline-api-axios/v2"
import { mitxonlineLegacyUrl } from "@/common/mitxonline"
import { DashboardType, getContextMenuItems } from "./ModuleCard"

describe("ModuleCard context menu receipt item", () => {
test("shows Receipt item for verified enrollment", () => {
const course = mitxonline.factories.courses.course()
const run = mitxonline.factories.courses.courseRun({ id: 42 })
const enrollment = mitxonline.factories.enrollment.courseEnrollment({
enrollment_mode: EnrollmentModeEnum.Verified,
run: { ...run, course },
})

const items = getContextMenuItems("Test Course", {
type: DashboardType.CourseRunEnrollment,
data: enrollment,
})

expect(items.some((item) => item.label === "Receipt")).toBe(true)
})

test("does not show Receipt item for audit enrollment", () => {
const course = mitxonline.factories.courses.course()
const run = mitxonline.factories.courses.courseRun({ id: 42 })
const enrollment = mitxonline.factories.enrollment.courseEnrollment({
enrollment_mode: EnrollmentModeEnum.Audit,
run: { ...run, course },
})

const items = getContextMenuItems("Test Course", {
type: DashboardType.CourseRunEnrollment,
data: enrollment,
})

expect(items.some((item) => item.label === "Receipt")).toBe(false)
})

test("Receipt item opens the expected MITx Online URL", () => {
const course = mitxonline.factories.courses.course()
const run = mitxonline.factories.courses.courseRun({ id: 42 })
const enrollment = mitxonline.factories.enrollment.courseEnrollment({
enrollment_mode: EnrollmentModeEnum.Verified,
run: { ...run, course },
})
const windowOpenSpy = jest
.spyOn(window, "open")
.mockImplementation(() => null)

const items = getContextMenuItems("Test Course", {
type: DashboardType.CourseRunEnrollment,
data: enrollment,
})
const receiptItem = items.find((item) => item.label === "Receipt")

receiptItem?.onClick?.()

expect(windowOpenSpy).toHaveBeenCalledWith(
mitxonlineLegacyUrl("/orders/receipt/by-run/42/"),
"_blank",
"noopener,noreferrer",
)
windowOpenSpy.mockRestore()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
} from "@/common/mitxonline"
import { useReplaceBasketItem } from "api/mitxonline-hooks/baskets"
import { EnrollmentStatus, getBestRun, getEnrollmentStatus } from "./helpers"
import { getReceiptMenuItem } from "./receiptMenuItem"
import {
CourseWithCourseRunsSerializerV2,
CourseRunEnrollmentV3,
Expand Down Expand Up @@ -230,6 +231,12 @@ const getContextMenuItems = (
},
)

const receiptMenuItem = getReceiptMenuItem(
resource.data.enrollment_mode,
`/orders/receipt/by-run/${resource.data.run.id}/`,
)
if (receiptMenuItem) courseMenuItems.push(receiptMenuItem)

menuItems.push(...courseMenuItems)
}
return [...menuItems, ...additionalItems]
Expand Down
Loading
Loading