Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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 frontends/api/src/mitxonline/clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ProgramsApi,
ProgramCertificatesApi,
UsersApi,
ProgramEnrollmentsApi,
} from "@mitodl/mitxonline-api-axios/v2"
import axios from "axios"

Expand Down Expand Up @@ -51,10 +52,17 @@ const courseRunEnrollmentsApi = new EnrollmentsApi(
axiosInstance,
)

const programEnrollmentsApi = new ProgramEnrollmentsApi(
undefined,
BASE_PATH,
axiosInstance,
)

export {
usersApi,
b2bApi,
courseRunEnrollmentsApi,
programEnrollmentsApi,
programsApi,
programCollectionsApi,
coursesApi,
Expand Down
20 changes: 17 additions & 3 deletions frontends/api/src/mitxonline/hooks/enrollment/queries.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { queryOptions } from "@tanstack/react-query"
import type { CourseRunEnrollment } from "@mitodl/mitxonline-api-axios/v2"
import type {
CourseRunEnrollment,
UserProgramEnrollmentDetail,
} from "@mitodl/mitxonline-api-axios/v2"

import { courseRunEnrollmentsApi } from "../../clients"
import { courseRunEnrollmentsApi, programEnrollmentsApi } from "../../clients"
import { RawAxiosRequestConfig } from "axios"

const enrollmentKeys = {
root: ["mitxonline", "enrollments"],
Expand All @@ -10,10 +14,11 @@ const enrollmentKeys = {
"courseRunEnrollments",
"list",
],
programEnrollmentsList: () => [
programEnrollmentsList: (opts?: RawAxiosRequestConfig) => [
...enrollmentKeys.root,
"programEnrollments",
"list",
opts,
],
}

Expand All @@ -25,6 +30,15 @@ const enrollmentQueries = {
return courseRunEnrollmentsApi.enrollmentsList().then((res) => res.data)
},
}),
programEnrollmentsList: (opts?: RawAxiosRequestConfig) =>
queryOptions({
queryKey: enrollmentKeys.programEnrollmentsList(opts),
queryFn: async (): Promise<UserProgramEnrollmentDetail[]> => {
return programEnrollmentsApi
.programEnrollmentsList(opts)
.then((res) => res.data)
},
}),
}

export { enrollmentQueries, enrollmentKeys }
73 changes: 71 additions & 2 deletions frontends/api/src/mitxonline/test-utils/factories/courses.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,80 @@
import { mergeOverrides, makePaginatedFactory } from "ol-test-utilities"
import type { PartialFactory } from "ol-test-utilities"
import type { CourseWithCourseRunsSerializerV2 } from "@mitodl/mitxonline-api-axios/v2"
import type {
CourseWithCourseRunsSerializerV2,
V1CourseWithCourseRuns,
} from "@mitodl/mitxonline-api-axios/v2"
import { faker } from "@faker-js/faker/locale/en"
import { UniqueEnforcer } from "enforce-unique"

const uniqueCourseId = new UniqueEnforcer()
const uniqueCourseRunId = new UniqueEnforcer()

const v1Course: PartialFactory<V1CourseWithCourseRuns> = (overrides = {}) => {
const defaults: V1CourseWithCourseRuns = {
id: uniqueCourseId.enforce(() => faker.number.int()),
title: faker.lorem.words(3),
readable_id: faker.lorem.slug(),
next_run_id: faker.number.int(),
departments: [
{
name: faker.company.name(),
},
],
page: {
feature_image_src: faker.image.url(),
page_url: faker.internet.url(),
description: faker.lorem.paragraph(),
live: faker.datatype.boolean(),
length: `${faker.number.int({ min: 1, max: 12 })} weeks`,
effort: `${faker.number.int({ min: 1, max: 10 })} hours/week`,
financial_assistance_form_url: faker.internet.url(),
current_price: faker.number.int({ min: 0, max: 1000 }),
instructors: [
{
name: faker.person.fullName(),
bio: faker.lorem.paragraph(),
},
],
},
programs: null,
courseruns: [
{
id: uniqueCourseRunId.enforce(() => faker.number.int()),
title: faker.lorem.words(3),
start_date: faker.date.future().toISOString(),
end_date: faker.date.future().toISOString(),
enrollment_start: faker.date.past().toISOString(),
enrollment_end: faker.date.future().toISOString(),
expiration_date: faker.date.future().toISOString(),
courseware_url: faker.internet.url(),
courseware_id: faker.string.uuid(),
certificate_available_date: faker.date.future().toISOString(),
upgrade_deadline: faker.date.future().toISOString(),
is_upgradable: faker.datatype.boolean(),
is_enrollable: faker.datatype.boolean(),
is_archived: faker.datatype.boolean(),
is_self_paced: faker.datatype.boolean(),
run_tag: faker.lorem.word(),
live: faker.datatype.boolean(),
course_number: faker.lorem.word(),
products: [
{
id: faker.number.int(),
price: faker.commerce.price(),
description: faker.lorem.sentence(),
is_active: faker.datatype.boolean(),
product_flexible_price: null,
},
],
approved_flexible_price_exists: faker.datatype.boolean(),
},
],
}

return mergeOverrides<V1CourseWithCourseRuns>(defaults, overrides)
}

const course: PartialFactory<CourseWithCourseRunsSerializerV2> = (
overrides = {},
) => {
Expand Down Expand Up @@ -91,6 +159,7 @@ const course: PartialFactory<CourseWithCourseRunsSerializerV2> = (
return mergeOverrides<CourseWithCourseRunsSerializerV2>(defaults, overrides)
}

const v1Courses = makePaginatedFactory(v1Course)
const courses = makePaginatedFactory(course)

export { course, courses }
export { v1Course, v1Courses, course, courses }
51 changes: 50 additions & 1 deletion frontends/api/src/mitxonline/test-utils/factories/enrollment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import type { PartialFactory } from "ol-test-utilities"
import type {
CourseRunEnrollment,
CourseRunGrade,
UserProgramEnrollmentDetail,
} from "@mitodl/mitxonline-api-axios/v2"
import { UniqueEnforcer } from "enforce-unique"
import { factories } from ".."

const uniqueEnrollmentId = new UniqueEnforcer()
const uniqueRunId = new UniqueEnforcer()
Expand Down Expand Up @@ -99,9 +101,56 @@ const courseEnrollment: PartialFactory<CourseRunEnrollment> = (
return mergeOverrides<CourseRunEnrollment>(defaults, overrides)
}

const programEnrollment: PartialFactory<UserProgramEnrollmentDetail> = (
overrides = {},
): UserProgramEnrollmentDetail => {
const defaults: UserProgramEnrollmentDetail = {
certificate: faker.datatype.boolean()
? {
uuid: faker.string.uuid(),
link: faker.internet.url(),
}
: null,
program: {
id: faker.number.int(),
title: faker.lorem.words(3),
readable_id: faker.lorem.slug(),
courses: factories.courses.v1Course(),
requirements: {
required: [faker.number.int()],
electives: [faker.number.int()],
},
req_tree: [],
page: {
feature_image_src: faker.image.url(),
page_url: faker.internet.url(),
financial_assistance_form_url: faker.internet.url(),
description: faker.lorem.paragraph(),
live: faker.datatype.boolean(),
length: `${faker.number.int({ min: 1, max: 12 })} weeks`,
effort: `${faker.number.int({ min: 1, max: 10 })} hours/week`,
price: faker.commerce.price(),
},
program_type: faker.helpers.arrayElement([
"certificate",
"degree",
"diploma",
]),
departments: [
{
name: faker.company.name(),
},
],
live: faker.datatype.boolean(),
},
enrollments: [courseEnrollment()],
}
return mergeOverrides<UserProgramEnrollmentDetail>(defaults, overrides)
}

// Not paginated
const courseEnrollments = (count: number): CourseRunEnrollment[] => {
return new Array(count).fill(null).map(() => courseEnrollment())
}

export { courseEnrollment, courseEnrollments, grade }
export { courseEnrollment, courseEnrollments, grade, programEnrollment }
5 changes: 5 additions & 0 deletions frontends/api/src/mitxonline/test-utils/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ const enrollment = {
`${API_BASE_URL}/api/v1/enrollments/${id ? `${id}/` : ""}`,
}

const programEnrollments = {
enrollmentsList: () => `${API_BASE_URL}/api/v1/program_enrollments/`,
}

const b2b = {
courseEnrollment: (readableId?: string) =>
`${API_BASE_URL}/api/v0/b2b/enroll/${readableId}/`,
Expand Down Expand Up @@ -59,4 +63,5 @@ export {
programCollections,
courses,
organization,
programEnrollments,
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import moment from "moment"
const makeCourses = factories.courses.courses
const makeProgram = factories.programs.program
const makeProgramCollection = factories.programs.programCollection
const makeEnrollment = factories.enrollment.courseEnrollment
const makeCourseEnrollment = factories.enrollment.courseEnrollment
const makeGrade = factories.enrollment.grade

const dashboardCourse: PartialFactory<DashboardCourse> = (...overrides) => {
Expand Down Expand Up @@ -45,20 +45,20 @@ const dashboardCourse: PartialFactory<DashboardCourse> = (...overrides) => {

const setupEnrollments = (includeExpired: boolean) => {
const completed = [
makeEnrollment({
makeCourseEnrollment({
run: { title: "C Course Ended" },
grades: [makeGrade({ passed: true })],
}),
]
const expired = includeExpired
? [
makeEnrollment({
makeCourseEnrollment({
run: {
title: "A Course Ended",
end_date: faker.date.past().toISOString(),
},
}),
makeEnrollment({
makeCourseEnrollment({
run: {
title: "B Course Ended",
end_date: faker.date.past().toISOString(),
Expand All @@ -67,26 +67,26 @@ const setupEnrollments = (includeExpired: boolean) => {
]
: []
const started = [
makeEnrollment({
makeCourseEnrollment({
run: {
title: "A Course Started",
start_date: faker.date.past().toISOString(),
},
}),
makeEnrollment({
makeCourseEnrollment({
run: {
title: "B Course Started",
start_date: faker.date.past().toISOString(),
},
}),
]
const notStarted = [
makeEnrollment({
makeCourseEnrollment({
run: {
start_date: moment().add(1, "day").toISOString(), // Sooner first
},
}),
makeEnrollment({
makeCourseEnrollment({
run: {
start_date: moment().add(5, "day").toISOString(), // Later second
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ describe("OrganizationContent", () => {
mockedUseFeatureFlagEnabled.mockReturnValue(true)
// Set default empty enrollments for all tests
setMockResponse.get(urls.enrollment.enrollmentsList(), [])
// Add missing program enrollments mock
setMockResponse.get(urls.programEnrollments.enrollmentsList(), [])
})

it("displays a header for each program returned and cards for courses in program", async () => {
Expand Down Expand Up @@ -300,4 +302,41 @@ describe("OrganizationContent", () => {
)
})
})

test("Shows the program certificate link button if the program has a certificate", async () => {
const { orgX, programA } = setupProgramsAndCourses()

// Mock the program to have a certificate
const programWithCertificate = {
...programA,
program_type: "Program", // Set specific program type
certificate: {
uuid: "cert-123",
url: "/certificates/program/1",
},
}
const programEnrollment = factories.enrollment.programEnrollment({
program: { id: programWithCertificate.id },
certificate: {
link: programWithCertificate.certificate.url,
},
})
setMockResponse.get(urls.programs.programsList({ org_id: orgX.id }), {
results: [programWithCertificate],
})
setMockResponse.get(urls.programEnrollments.enrollmentsList(), [
programEnrollment,
])

renderWithProviders(<OrganizationContent orgSlug={orgX.slug} />)

const programRoot = await screen.findByTestId("org-program-root")
const certificateButton = within(programRoot).getByRole("link", {
name: "View Program Certificate",
})
expect(certificateButton).toHaveAttribute(
"href",
programWithCertificate.certificate.url,
)
})
})
Loading
Loading