diff --git a/RELEASE.rst b/RELEASE.rst index 953dc51b67..15e015e84e 100644 --- a/RELEASE.rst +++ b/RELEASE.rst @@ -1,6 +1,11 @@ Release Notes ============= +Version 0.65.6 +-------------- + +- fix fallback when no language is selected (#3286) + Version 0.65.5 (Released May 04, 2026) -------------- diff --git a/frontends/main/src/app-pages/DashboardPage/ContractContent.test.tsx b/frontends/main/src/app-pages/DashboardPage/ContractContent.test.tsx index bb212cfbbf..a3bdf6cc5f 100644 --- a/frontends/main/src/app-pages/DashboardPage/ContractContent.test.tsx +++ b/frontends/main/src/app-pages/DashboardPage/ContractContent.test.tsx @@ -1677,6 +1677,61 @@ describe("ContractContent", () => { expect(screen.queryByText("Learning Language:")).not.toBeInTheDocument() }) + test("disables CTA for non-enrolled B2B course when no translations and no enrollable runs", async () => { + const { orgX, user, mitxOnlineUser } = setupOrgAndUser() + + const course = factories.courses.course() + const program = factories.programs.program({ + courses: [course.id], + }) + const contracts = createTestContracts(orgX.id, 1, [program.id]) + orgX.contracts = contracts + mitxOnlineUser.b2b_organizations[0].contracts = contracts + + const contractRun = factories.courses.courseRun({ + b2b_contract: contracts[0].id, + language: "en", + run_tag: undefined, + is_enrollable: false, + courseware_url: "https://openedx.example.com/unenrollable-run", + }) + + const courseWithoutTranslations = { + ...course, + courseruns: [contractRun], + language_options: [], + next_run_id: contractRun.id, + next_run: null, + } + + setupOrgDashboardMocks( + orgX, + user, + mitxOnlineUser, + [program], + [courseWithoutTranslations], + contracts, + ) + + setMockResponse.get(urls.enrollment.enrollmentsListV3(), []) + + renderWithProviders( + , + ) + + const programElement = await screen.findByTestId("org-program-root") + const card = await within(programElement).findByTestId( + "enrollment-card-desktop", + ) + + const coursewareButton = within(card).getByTestId("courseware-button") + expect(coursewareButton).toHaveTextContent("Start Module") + expect(coursewareButton).toBeDisabled() + }) + test("displays correct run URL when user is enrolled in one of multiple runs", async () => { const { orgX, user, mitxOnlineUser } = setupOrgAndUser() diff --git a/frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/languageOptions.test.ts b/frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/languageOptions.test.ts index 8b6cb42201..da6805fd6f 100644 --- a/frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/languageOptions.test.ts +++ b/frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/languageOptions.test.ts @@ -392,6 +392,42 @@ describe("languageOptions", () => { expect(selectedEnrollment?.run.courseware_id).toBe(spanishRun.courseware_id) }) + test("matches enrollment when course has no language options", () => { + const run = factories.courses.courseRun({ + id: 7001, + title: "Enrolled Course", + courseware_id: "cw-enrolled-7001", + courseware_url: "https://example.com/cw-enrolled-7001", + is_enrollable: true, + }) + + const course = factories.courses.course({ + courseruns: [run], + next_run_id: run.id, + language_options: [], + }) + + const selectedRun = getCourseRunForSelectedLanguage(course, "") + const enrollment = factories.enrollment.courseEnrollment({ + run: { + id: run.id, + course: { id: course.id, title: course.title }, + title: run.title, + courseware_id: run.courseware_id, + courseware_url: run.courseware_url, + }, + }) + + const selectedEnrollment = getEnrollmentForSelectedLanguage( + [enrollment], + null, + selectedRun, + ) + + expect(selectedRun?.id).toBe(run.id) + expect(selectedEnrollment?.run.id).toBe(run.id) + }) + test("adapts V3 enrollment run into V2-shaped run context", () => { const templateRun = factories.courses.courseRun({ id: 500, diff --git a/frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/languageOptions.ts b/frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/languageOptions.ts index b5f2670640..f9b72c3091 100644 --- a/frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/languageOptions.ts +++ b/frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/languageOptions.ts @@ -235,7 +235,11 @@ const getCourseRunForSelectedLanguage = ( ): CourseRunV2 | null => { const languageOption = getSelectedLanguageOption(course, selectedLanguageKey) if (!languageOption) { - return null + return ( + getBestRun(course, { enrollableOnly: true }) ?? + course.courseruns[0] ?? + null + ) } const matchingRuns = getRunsForLanguageOption(course, languageOption) @@ -267,7 +271,22 @@ const getEnrollmentForSelectedLanguage = ( selectedRun: CourseRunV2 | null, ): CourseRunEnrollmentV3 | null => { if (!selectedLanguageOption) { - return null + if (!selectedRun) { + return null + } + + return ( + enrollments.find((enrollment) => { + if (!enrollment.run) { + return false + } + + return ( + enrollment.run.id === selectedRun.id || + enrollment.run.courseware_id === selectedRun.courseware_id + ) + }) ?? null + ) } return ( diff --git a/main/settings.py b/main/settings.py index ab1b8878e5..00ec603259 100644 --- a/main/settings.py +++ b/main/settings.py @@ -35,7 +35,7 @@ from main.settings_pluggy import * # noqa: F403 from openapi.settings_spectacular import open_spectacular_settings -VERSION = "0.65.5" +VERSION = "0.65.6" log = logging.getLogger()