From 6785cf3e99785e3f8d95791131f1794910b98a3e Mon Sep 17 00:00:00 2001 From: zamanafzal Date: Mon, 18 May 2026 17:10:43 +0500 Subject: [PATCH 1/5] feat: Handle nullable page field from MITx Online client update --- frontends/api/package.json | 2 +- frontends/main/package.json | 2 +- .../DashboardPage/ContractContent.test.tsx | 25 +++++++++++++++++++ .../ProductPages/CoursePage.test.tsx | 9 +++++++ .../src/app-pages/ProductPages/CoursePage.tsx | 4 +-- .../ProductPages/ProductSummary.test.tsx | 7 ++++++ .../app-pages/ProductPages/ProductSummary.tsx | 10 +++++--- .../ProductPages/ProgramAsCoursePage.test.tsx | 11 ++++++++ .../ProductPages/ProgramAsCoursePage.tsx | 4 +-- .../ProductPages/ProgramPage.test.tsx | 9 +++++++ .../app-pages/ProductPages/ProgramPage.tsx | 4 +-- yarn.lock | 12 ++++----- 12 files changed, 81 insertions(+), 18 deletions(-) diff --git a/frontends/api/package.json b/frontends/api/package.json index d76f4d52a5..91eba12d45 100644 --- a/frontends/api/package.json +++ b/frontends/api/package.json @@ -29,7 +29,7 @@ "ol-test-utilities": "0.0.0" }, "dependencies": { - "@mitodl/mitxonline-api-axios": "2026.5.1", + "@mitodl/mitxonline-api-axios": "https://github.com/mitodl/mitxonline-api-clients/raw/96cb88a7aeac5ec38aeaf688b0784a0abeb7af47/src/typescript/mitxonline-api-axios/package.tgz", "@tanstack/react-query": "^5.66.0", "axios": "^1.12.2", "tiny-invariant": "^1.3.3" diff --git a/frontends/main/package.json b/frontends/main/package.json index 6774f02fb2..76ce610b23 100644 --- a/frontends/main/package.json +++ b/frontends/main/package.json @@ -14,7 +14,7 @@ "@emotion/styled": "^11.11.0", "@floating-ui/react": "^0.27.16", "@mitodl/course-search-utils": "^3.5.2", - "@mitodl/mitxonline-api-axios": "2026.5.1", + "@mitodl/mitxonline-api-axios": "https://github.com/mitodl/mitxonline-api-clients/raw/96cb88a7aeac5ec38aeaf688b0784a0abeb7af47/src/typescript/mitxonline-api-axios/package.tgz", "@mitodl/smoot-design": "^6.27.0", "@mui/material": "^6.4.5", "@mui/material-nextjs": "^6.4.3", diff --git a/frontends/main/src/app-pages/DashboardPage/ContractContent.test.tsx b/frontends/main/src/app-pages/DashboardPage/ContractContent.test.tsx index 08876330da..8ad9c55394 100644 --- a/frontends/main/src/app-pages/DashboardPage/ContractContent.test.tsx +++ b/frontends/main/src/app-pages/DashboardPage/ContractContent.test.tsx @@ -1910,4 +1910,29 @@ describe("ContractContent", () => { const coursewareButton = within(card).getByTestId("courseware-button") expect(coursewareButton).toHaveAttribute("href", enrolledRun.courseware_url) }) + + it("renders without crashing when program.page is null", async () => { + const { orgX, programA, programB } = setupProgramsAndCourses() + const contract = orgX.contracts[0] + + setMockResponse.get( + urls.programs.programsList({ + org_id: orgX.id, + contract_id: contract.id, + page_size: 30, + }), + { + results: [ + { ...programA, page: null }, + { ...programB, page: null }, + ], + }, + ) + + renderWithProviders( + , + ) + + await screen.findByRole("heading", { name: orgX.name }) + }) }) diff --git a/frontends/main/src/app-pages/ProductPages/CoursePage.test.tsx b/frontends/main/src/app-pages/ProductPages/CoursePage.test.tsx index 97bd76d09f..24da3c7eda 100644 --- a/frontends/main/src/app-pages/ProductPages/CoursePage.test.tsx +++ b/frontends/main/src/app-pages/ProductPages/CoursePage.test.tsx @@ -483,4 +483,13 @@ describe("CoursePage", () => { ).not.toBeInTheDocument() }) }) + + test("Renders without crashing when course_details.page is null", async () => { + const course = makeCourse({ page: null }) + const page = makePage({ course_details: course }) + setupApis({ course, page }) + renderWithProviders() + + await screen.findByRole("heading", { name: page.title }) + }) }) diff --git a/frontends/main/src/app-pages/ProductPages/CoursePage.tsx b/frontends/main/src/app-pages/ProductPages/CoursePage.tsx index b011bfe24d..3b1480728c 100644 --- a/frontends/main/src/app-pages/ProductPages/CoursePage.tsx +++ b/frontends/main/src/app-pages/ProductPages/CoursePage.tsx @@ -75,13 +75,13 @@ const CoursePage: React.FC = ({ readableId }) => { } const imageSrc = - page.course_details.page.feature_image_src || DEFAULT_RESOURCE_IMG + page.course_details.page?.feature_image_src || DEFAULT_RESOURCE_IMG return ( } diff --git a/frontends/main/src/app-pages/ProductPages/ProductSummary.test.tsx b/frontends/main/src/app-pages/ProductPages/ProductSummary.test.tsx index 1741b4d2f7..9185874658 100644 --- a/frontends/main/src/app-pages/ProductPages/ProductSummary.test.tsx +++ b/frontends/main/src/app-pages/ProductPages/ProductSummary.test.tsx @@ -1185,6 +1185,13 @@ describe("ProgramSummary", () => { } }, ) + + test("Does not crash and hides duration row when program page is null", () => { + const program = factories.programs.program({ page: null }) + renderWithProviders() + + expect(screen.queryByTestId(TestIds.DurationRow)).toBeNull() + }) }) describe("Pacing Row", () => { diff --git a/frontends/main/src/app-pages/ProductPages/ProductSummary.tsx b/frontends/main/src/app-pages/ProductPages/ProductSummary.tsx index 7e4e4a594c..6f9923b0a0 100644 --- a/frontends/main/src/app-pages/ProductPages/ProductSummary.tsx +++ b/frontends/main/src/app-pages/ProductPages/ProductSummary.tsx @@ -694,8 +694,8 @@ const ProgramDurationRow: React.FC = ({ program, ...others }) => { - const duration = program.page.length ?? "" - const effort = program.page.effort ?? "" + const duration = program.page?.length ?? "" + const effort = program.page?.effort ?? "" if (!duration) return null const display = [duration, effort].filter(Boolean).join(", ") @@ -780,10 +780,12 @@ const ProgramCertificateBox: React.FC<{ program: V2ProgramDetail }> = ({ : {formatPrice(price, { avoidCents: true })} - {program.page.financial_assistance_form_url ? ( + {program.page?.financial_assistance_form_url ? ( { ).not.toBeInTheDocument() }) }) + + test("Renders without crashing when program_details.page is null", async () => { + const program = makeProgramAsCourse({ page: null }) + const page = makePage({ program_details: program }) + setupApis({ program, page }) + renderWithProviders( + , + ) + + await screen.findByRole("heading", { name: page.title }) + }) }) diff --git a/frontends/main/src/app-pages/ProductPages/ProgramAsCoursePage.tsx b/frontends/main/src/app-pages/ProductPages/ProgramAsCoursePage.tsx index b7080c0f2f..deca2685bb 100644 --- a/frontends/main/src/app-pages/ProductPages/ProgramAsCoursePage.tsx +++ b/frontends/main/src/app-pages/ProductPages/ProgramAsCoursePage.tsx @@ -258,7 +258,7 @@ const ProgramAsCoursePage: React.FC = ({ } const imageSrc = - page.program_details.page.feature_image_src || DEFAULT_RESOURCE_IMG + page.program_details.page?.feature_image_src || DEFAULT_RESOURCE_IMG const dataLoading = (courseIds.length > 0 && !courses.isSuccess) || @@ -271,7 +271,7 @@ const ProgramAsCoursePage: React.FC = ({ shortDescription={ } imageSrc={imageSrc} diff --git a/frontends/main/src/app-pages/ProductPages/ProgramPage.test.tsx b/frontends/main/src/app-pages/ProductPages/ProgramPage.test.tsx index 7246de5194..805a57e11a 100644 --- a/frontends/main/src/app-pages/ProductPages/ProgramPage.test.tsx +++ b/frontends/main/src/app-pages/ProductPages/ProgramPage.test.tsx @@ -798,4 +798,13 @@ describe("ProgramPage", () => { ).not.toBeInTheDocument() }) }) + + test("Renders without crashing when program_details.page is null", async () => { + const program = makeProgram({ ...makeReqs(), page: null }) + const page = makePage({ program_details: program }) + setupApis({ program, page }) + renderWithProviders() + + await screen.findByRole("heading", { name: page.title }) + }) }) diff --git a/frontends/main/src/app-pages/ProductPages/ProgramPage.tsx b/frontends/main/src/app-pages/ProductPages/ProgramPage.tsx index e9c3cfc689..abea97baae 100644 --- a/frontends/main/src/app-pages/ProductPages/ProgramPage.tsx +++ b/frontends/main/src/app-pages/ProductPages/ProgramPage.tsx @@ -275,7 +275,7 @@ const ProgramPage: React.FC = ({ readableId }) => { } const imageSrc = - page.program_details.page.feature_image_src || DEFAULT_RESOURCE_IMG + page.program_details.page?.feature_image_src || DEFAULT_RESOURCE_IMG const dataLoading = (courseIds.length > 0 && !courses.isSuccess) || @@ -289,7 +289,7 @@ const ProgramPage: React.FC = ({ readableId }) => { shortDescription={ } imageSrc={imageSrc} diff --git a/yarn.lock b/yarn.lock index d2ac5fe0cb..0ae9729f16 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3332,13 +3332,13 @@ __metadata: languageName: node linkType: hard -"@mitodl/mitxonline-api-axios@npm:2026.5.1": - version: 2026.5.1 - resolution: "@mitodl/mitxonline-api-axios@npm:2026.5.1" +"@mitodl/mitxonline-api-axios@https://github.com/mitodl/mitxonline-api-clients/raw/96cb88a7aeac5ec38aeaf688b0784a0abeb7af47/src/typescript/mitxonline-api-axios/package.tgz": + version: 2026.5.6 + resolution: "@mitodl/mitxonline-api-axios@https://github.com/mitodl/mitxonline-api-clients/raw/96cb88a7aeac5ec38aeaf688b0784a0abeb7af47/src/typescript/mitxonline-api-axios/package.tgz" dependencies: "@types/node": "npm:^20.11.19" axios: "npm:^1.6.5" - checksum: 10/6eb179298221fc2801ce4e3c0589de0378c68b7a32503101de675a85f6e3569c78ec6cb4b1bc5aa85094637532a34d10c110a4a9cdfa96c525dc4362e0236d5c + checksum: 10/1d42c26f29b2694656877b5b3e70a97fc5075130b777d4aa83a7f91f0c4862df416aadebd88f30b2ecf9eee3a7981c27433842171f28a3cc78c416444804b9f3 languageName: node linkType: hard @@ -8939,7 +8939,7 @@ __metadata: resolution: "api@workspace:frontends/api" dependencies: "@faker-js/faker": "npm:^10.0.0" - "@mitodl/mitxonline-api-axios": "npm:2026.5.1" + "@mitodl/mitxonline-api-axios": "https://github.com/mitodl/mitxonline-api-clients/raw/96cb88a7aeac5ec38aeaf688b0784a0abeb7af47/src/typescript/mitxonline-api-axios/package.tgz" "@tanstack/react-query": "npm:^5.66.0" "@testing-library/react": "npm:^16.3.0" axios: "npm:^1.12.2" @@ -16184,7 +16184,7 @@ __metadata: "@floating-ui/react": "npm:^0.27.16" "@happy-dom/jest-environment": "npm:^20.1.0" "@mitodl/course-search-utils": "npm:^3.5.2" - "@mitodl/mitxonline-api-axios": "npm:2026.5.1" + "@mitodl/mitxonline-api-axios": "https://github.com/mitodl/mitxonline-api-clients/raw/96cb88a7aeac5ec38aeaf688b0784a0abeb7af47/src/typescript/mitxonline-api-axios/package.tgz" "@mitodl/smoot-design": "npm:^6.27.0" "@mui/material": "npm:^6.4.5" "@mui/material-nextjs": "npm:^6.4.3" From 612c46a7e274f99e5fd3f0bc9fcdec1ac23ea0ce Mon Sep 17 00:00:00 2001 From: zamanafzal Date: Mon, 18 May 2026 18:07:14 +0500 Subject: [PATCH 2/5] fix: incorporate feedback --- .../main/src/app-pages/DashboardPage/ContractContent.test.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontends/main/src/app-pages/DashboardPage/ContractContent.test.tsx b/frontends/main/src/app-pages/DashboardPage/ContractContent.test.tsx index 8ad9c55394..6cea57b8dd 100644 --- a/frontends/main/src/app-pages/DashboardPage/ContractContent.test.tsx +++ b/frontends/main/src/app-pages/DashboardPage/ContractContent.test.tsx @@ -1933,6 +1933,7 @@ describe("ContractContent", () => { , ) - await screen.findByRole("heading", { name: orgX.name }) + await screen.findByText(programA.title) + await screen.findByText(programB.title) }) }) From 40aefa16a2b6b3996771368a01c069628652c217 Mon Sep 17 00:00:00 2001 From: zamanafzal Date: Thu, 21 May 2026 12:10:56 +0500 Subject: [PATCH 3/5] chore: updated mitxonline-api-axios --- frontends/api/package.json | 2 +- frontends/main/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontends/api/package.json b/frontends/api/package.json index 91eba12d45..35194420e2 100644 --- a/frontends/api/package.json +++ b/frontends/api/package.json @@ -29,7 +29,7 @@ "ol-test-utilities": "0.0.0" }, "dependencies": { - "@mitodl/mitxonline-api-axios": "https://github.com/mitodl/mitxonline-api-clients/raw/96cb88a7aeac5ec38aeaf688b0784a0abeb7af47/src/typescript/mitxonline-api-axios/package.tgz", + "@mitodl/mitxonline-api-axios": "2026.5.20.1", "@tanstack/react-query": "^5.66.0", "axios": "^1.12.2", "tiny-invariant": "^1.3.3" diff --git a/frontends/main/package.json b/frontends/main/package.json index 76ce610b23..65fb7bbfec 100644 --- a/frontends/main/package.json +++ b/frontends/main/package.json @@ -14,7 +14,7 @@ "@emotion/styled": "^11.11.0", "@floating-ui/react": "^0.27.16", "@mitodl/course-search-utils": "^3.5.2", - "@mitodl/mitxonline-api-axios": "https://github.com/mitodl/mitxonline-api-clients/raw/96cb88a7aeac5ec38aeaf688b0784a0abeb7af47/src/typescript/mitxonline-api-axios/package.tgz", + "@mitodl/mitxonline-api-axios": "2026.5.20.1", "@mitodl/smoot-design": "^6.27.0", "@mui/material": "^6.4.5", "@mui/material-nextjs": "^6.4.3", From f5cb26cece88cb363fe3374188122c912c6510b4 Mon Sep 17 00:00:00 2001 From: zamanafzal Date: Thu, 21 May 2026 13:18:14 +0500 Subject: [PATCH 4/5] chore: current latest mitxonline-api-client version in yarn file --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0ae9729f16..d2ac5fe0cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3332,13 +3332,13 @@ __metadata: languageName: node linkType: hard -"@mitodl/mitxonline-api-axios@https://github.com/mitodl/mitxonline-api-clients/raw/96cb88a7aeac5ec38aeaf688b0784a0abeb7af47/src/typescript/mitxonline-api-axios/package.tgz": - version: 2026.5.6 - resolution: "@mitodl/mitxonline-api-axios@https://github.com/mitodl/mitxonline-api-clients/raw/96cb88a7aeac5ec38aeaf688b0784a0abeb7af47/src/typescript/mitxonline-api-axios/package.tgz" +"@mitodl/mitxonline-api-axios@npm:2026.5.1": + version: 2026.5.1 + resolution: "@mitodl/mitxonline-api-axios@npm:2026.5.1" dependencies: "@types/node": "npm:^20.11.19" axios: "npm:^1.6.5" - checksum: 10/1d42c26f29b2694656877b5b3e70a97fc5075130b777d4aa83a7f91f0c4862df416aadebd88f30b2ecf9eee3a7981c27433842171f28a3cc78c416444804b9f3 + checksum: 10/6eb179298221fc2801ce4e3c0589de0378c68b7a32503101de675a85f6e3569c78ec6cb4b1bc5aa85094637532a34d10c110a4a9cdfa96c525dc4362e0236d5c languageName: node linkType: hard @@ -8939,7 +8939,7 @@ __metadata: resolution: "api@workspace:frontends/api" dependencies: "@faker-js/faker": "npm:^10.0.0" - "@mitodl/mitxonline-api-axios": "https://github.com/mitodl/mitxonline-api-clients/raw/96cb88a7aeac5ec38aeaf688b0784a0abeb7af47/src/typescript/mitxonline-api-axios/package.tgz" + "@mitodl/mitxonline-api-axios": "npm:2026.5.1" "@tanstack/react-query": "npm:^5.66.0" "@testing-library/react": "npm:^16.3.0" axios: "npm:^1.12.2" @@ -16184,7 +16184,7 @@ __metadata: "@floating-ui/react": "npm:^0.27.16" "@happy-dom/jest-environment": "npm:^20.1.0" "@mitodl/course-search-utils": "npm:^3.5.2" - "@mitodl/mitxonline-api-axios": "https://github.com/mitodl/mitxonline-api-clients/raw/96cb88a7aeac5ec38aeaf688b0784a0abeb7af47/src/typescript/mitxonline-api-axios/package.tgz" + "@mitodl/mitxonline-api-axios": "npm:2026.5.1" "@mitodl/smoot-design": "npm:^6.27.0" "@mui/material": "npm:^6.4.5" "@mui/material-nextjs": "npm:^6.4.3" From 563b800649d02622b4506834140ef40f06e927ca Mon Sep 17 00:00:00 2001 From: zamanafzal Date: Fri, 22 May 2026 00:18:15 +0500 Subject: [PATCH 5/5] chore: current latest mitxonline-api-client version --- frontends/api/package.json | 2 +- frontends/main/package.json | 2 +- yarn.lock | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontends/api/package.json b/frontends/api/package.json index 35194420e2..3247087dd4 100644 --- a/frontends/api/package.json +++ b/frontends/api/package.json @@ -29,7 +29,7 @@ "ol-test-utilities": "0.0.0" }, "dependencies": { - "@mitodl/mitxonline-api-axios": "2026.5.20.1", + "@mitodl/mitxonline-api-axios": "2026.5.21", "@tanstack/react-query": "^5.66.0", "axios": "^1.12.2", "tiny-invariant": "^1.3.3" diff --git a/frontends/main/package.json b/frontends/main/package.json index 65fb7bbfec..2ec5327f52 100644 --- a/frontends/main/package.json +++ b/frontends/main/package.json @@ -14,7 +14,7 @@ "@emotion/styled": "^11.11.0", "@floating-ui/react": "^0.27.16", "@mitodl/course-search-utils": "^3.5.2", - "@mitodl/mitxonline-api-axios": "2026.5.20.1", + "@mitodl/mitxonline-api-axios": "2026.5.21", "@mitodl/smoot-design": "^6.27.0", "@mui/material": "^6.4.5", "@mui/material-nextjs": "^6.4.3", diff --git a/yarn.lock b/yarn.lock index d2ac5fe0cb..3177d89e84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3332,13 +3332,13 @@ __metadata: languageName: node linkType: hard -"@mitodl/mitxonline-api-axios@npm:2026.5.1": - version: 2026.5.1 - resolution: "@mitodl/mitxonline-api-axios@npm:2026.5.1" +"@mitodl/mitxonline-api-axios@npm:2026.5.21": + version: 2026.5.21 + resolution: "@mitodl/mitxonline-api-axios@npm:2026.5.21" dependencies: "@types/node": "npm:^20.11.19" axios: "npm:^1.6.5" - checksum: 10/6eb179298221fc2801ce4e3c0589de0378c68b7a32503101de675a85f6e3569c78ec6cb4b1bc5aa85094637532a34d10c110a4a9cdfa96c525dc4362e0236d5c + checksum: 10/01f91379031fff433fe511ad7fd49f7b0f77d4d45c22c693ab07e191586c98f0e90ba15bca966a3a5cba0039e1a1b85fa776128742a84b626fb6c736270df7f3 languageName: node linkType: hard @@ -8939,7 +8939,7 @@ __metadata: resolution: "api@workspace:frontends/api" dependencies: "@faker-js/faker": "npm:^10.0.0" - "@mitodl/mitxonline-api-axios": "npm:2026.5.1" + "@mitodl/mitxonline-api-axios": "npm:2026.5.21" "@tanstack/react-query": "npm:^5.66.0" "@testing-library/react": "npm:^16.3.0" axios: "npm:^1.12.2" @@ -16184,7 +16184,7 @@ __metadata: "@floating-ui/react": "npm:^0.27.16" "@happy-dom/jest-environment": "npm:^20.1.0" "@mitodl/course-search-utils": "npm:^3.5.2" - "@mitodl/mitxonline-api-axios": "npm:2026.5.1" + "@mitodl/mitxonline-api-axios": "npm:2026.5.21" "@mitodl/smoot-design": "npm:^6.27.0" "@mui/material": "npm:^6.4.5" "@mui/material-nextjs": "npm:^6.4.3"