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

Version 0.71.16
---------------

- adding wait=False to relevant calls (#3529)
- feat(vector_search): embeddings deadline resilience (retry + finalize tail) (#3526)
- feat: show duplicate emails and stats summary in B2B seat assignment modal (#3509)
- add card type and end date (#3504)
- Retry embeddings tasks on Qdrant gRPC DEADLINE_EXCEEDED (#3520)
- chore: Remove product page feature flag (#3501)
- Ingest contentfile archives for all runs (two-tier indexing) (#3503)
- Program certificate title from CMS "Certificate Title" (behind flag) (#3512)
- remove semantic chunker and dependance on langchain-experimental (#3514)

Version 0.71.12 (Released June 23, 2026)
---------------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,23 @@ import CertificatePage from "./CertificatePage"
import { CertificateType } from "@/common/certificateUtils"
import SharePopover from "@/components/SharePopover/SharePopover"
import * as mitxonline from "api/mitxonline-test-utils"
import { useFeatureFlagEnabled } from "posthog-js/react"
import {
FACEBOOK_SHARE_BASE_URL,
TWITTER_SHARE_BASE_URL,
LINKEDIN_SHARE_BASE_URL,
} from "@/common/urls"

jest.mock("posthog-js/react", () => ({
...jest.requireActual("posthog-js/react"),
useFeatureFlagEnabled: jest.fn(),
}))
const mockedUseFeatureFlagEnabled = jest.mocked(useFeatureFlagEnabled)

describe("CertificatePage", () => {
beforeEach(() => {
// Default the CMS-title flag ON; individual tests override as needed.
mockedUseFeatureFlagEnabled.mockReturnValue(true)
const mitxUser = mitxonline.factories.user.user()
setMockResponse.get(mitxonline.urls.userMe.get(), mitxUser)
})
Expand Down Expand Up @@ -84,9 +93,11 @@ describe("CertificatePage", () => {
await screen.findAllByText(certificate.uuid)
})

it("renders a program certificate", async () => {
it("renders a program certificate with the CMS product name as the title", async () => {
const certificate = factories.mitxonline.programCertificate()
certificate.program.program_type = "Program"
certificate.certificate_page.product_name =
"Custom Program Certificate Title"
setMockResponse.get(
mitxonline.urls.certificates.programCertificatesRetrieve({
uuid: certificate.uuid,
Expand All @@ -101,7 +112,7 @@ describe("CertificatePage", () => {
/>,
)

await screen.findAllByText(certificate.program.title)
await screen.findAllByText("Custom Program Certificate Title")
const badge = await screen.findByTestId("certificate-badge-label")
expect(within(badge).getByText("Program")).toBeInTheDocument()
expect(within(badge).getByText("Certificate")).toBeInTheDocument()
Expand All @@ -117,6 +128,50 @@ describe("CertificatePage", () => {
await screen.findAllByText(certificate.uuid)
})

it("falls back to the program title when the certificate page has no product name", async () => {
const certificate = factories.mitxonline.programCertificate()
certificate.program.program_type = "Program"
certificate.certificate_page.product_name = ""
setMockResponse.get(
mitxonline.urls.certificates.programCertificatesRetrieve({
uuid: certificate.uuid,
}),
certificate,
)
renderWithProviders(
<CertificatePage
certificateType={CertificateType.Program}
uuid={certificate.uuid}
pageUrl={`https://${process.env.NEXT_PUBLIC_ORIGIN}/certificate/program/${certificate.uuid}`}
/>,
)

await screen.findAllByText(certificate.program.title)
})

it("shows the program title (not the CMS title) when the flag is off", async () => {
mockedUseFeatureFlagEnabled.mockReturnValue(false)
const certificate = factories.mitxonline.programCertificate()
certificate.program.program_type = "Program"
certificate.certificate_page.product_name = "Custom CMS Title"
setMockResponse.get(
mitxonline.urls.certificates.programCertificatesRetrieve({
uuid: certificate.uuid,
}),
certificate,
)
renderWithProviders(
<CertificatePage
certificateType={CertificateType.Program}
uuid={certificate.uuid}
pageUrl={`https://${process.env.NEXT_PUBLIC_ORIGIN}/certificate/program/${certificate.uuid}`}
/>,
)

await screen.findAllByText(certificate.program.title)
expect(screen.queryByText("Custom CMS Title")).not.toBeInTheDocument()
})

it("renders a MicroMasters program certificate badge with the registered mark", async () => {
const certificate = factories.mitxonline.programCertificate()
certificate.program.program_type = "MicroMasters®"
Expand Down Expand Up @@ -166,7 +221,7 @@ describe("CertificatePage", () => {
/>,
)

await screen.findAllByText(certificate.program.title)
await screen.findAllByText(certificate.user.name!)

expect(
screen.queryByRole("button", { name: "Download PDF" }),
Expand Down Expand Up @@ -204,7 +259,7 @@ describe("CertificatePage", () => {
/>,
)

await screen.findAllByText(certificate.program.title)
await screen.findAllByText(certificate.user.name!)

await screen.findByRole("button", { name: "Download PDF" })
await screen.findByRole("button", { name: "Share" })
Expand Down
43 changes: 32 additions & 11 deletions frontends/main/src/app-pages/CertificatePage/CertificatePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,16 @@ import SharePopover from "@/components/SharePopover/SharePopover"
import { DigitalCredentialDialog } from "./DigitalCredentialDialog"
import {
getCertificateInfo,
getCertificateTitle,
getCertificateBadgeLines,
getCertificateBadgeTypography,
getVerifiableCredentialLinkedInURL,
getCertificateLinkedInUrl,
getVerifiableCredentialDownloadAPIURL,
CertificateType,
} from "@/common/certificateUtils"
import { useFeatureFlagEnabled } from "posthog-js/react"
import { FeatureFlags } from "@/common/feature_flags"

const Page = styled.div(({ theme }) => ({
backgroundImage: `url(${backgroundImage.src})`,
Expand Down Expand Up @@ -626,11 +629,11 @@ const Certificate = ({

const CourseCertificate = ({
certificate,
title,
}: {
certificate: V2CourseRunCertificate
title: string
}) => {
const title = certificate.course_run.course.title

const userName = certificate.user.name

const signatories = certificate.certificate_page.signatory_items
Expand All @@ -649,11 +652,11 @@ const CourseCertificate = ({

const ProgramCertificate = ({
certificate,
title,
}: {
certificate: V2ProgramCertificate
title: string
}) => {
const title = certificate.program.title

const userName = certificate.user.name

const ceus = certificate.certificate_page.CEUs
Expand Down Expand Up @@ -685,6 +688,11 @@ const CertificatePage: React.FC<{

const { data: userData } = useQuery(mitxUserQueries.me())

// Gates whether the certificate title comes from the CMS "Certificate Title"
// (product_name) field or the program/course title. Defaults to the
// program/course title until the flag loads / is enabled.
const useCmsTitle = !!useFeatureFlagEnabled(FeatureFlags.CmsCertificateTitle)

const {
data: courseCertificateData,
isLoading: isCourseLoading,
Expand Down Expand Up @@ -750,6 +758,18 @@ const CertificatePage: React.FC<{
return notFound()
}

let title = ""
if (certificateType === CertificateType.Course) {
title = courseCertificateData?.course_run.course.title ?? ""
} else if (useCmsTitle) {
title = getCertificateTitle(
programCertificateData?.certificate_page?.product_name,
programCertificateData?.program.title ?? "",
)
} else {
title = programCertificateData?.program.title ?? ""
}

const download = async () => {
const res = await fetch(`/certificate/${certificateType}/${uuid}/pdf`)
const blob = await res.blob()
Expand All @@ -763,11 +783,6 @@ const CertificatePage: React.FC<{
window.URL.revokeObjectURL(url)
}

const title =
certificateType === CertificateType.Course
? courseCertificateData?.course_run.course.title
: programCertificateData?.program.title

const { displayType } = getCertificateInfo(
programCertificateData?.program?.program_type,
)
Expand Down Expand Up @@ -849,9 +864,15 @@ const CertificatePage: React.FC<{
) : null}
<PrintContainer ref={contentRef}>
{certificateType === CertificateType.Course ? (
<CourseCertificate certificate={courseCertificateData!} />
<CourseCertificate
certificate={courseCertificateData!}
title={title}
/>
) : (
<ProgramCertificate certificate={programCertificateData!} />
<ProgramCertificate
certificate={programCertificateData!}
title={title}
/>
)}
</PrintContainer>
<Note>
Expand Down
Loading
Loading