From 25468959e77d3295c02f75cf596ef9180175d4eb Mon Sep 17 00:00:00 2001 From: Troy Sankey Date: Wed, 24 Jul 2024 15:11:00 -0700 Subject: [PATCH] refactor: more consistency and clarity around late enrollment variables (#1131) Renames the following everywhere in the codebase: * getLateRedemptionBufferDays -> getLateEnrollmentBufferDays * useLateRedemptionBufferDays -> useLateEnrollmentBufferDays * isEnrollableBufferDays -> lateEnrollmentBufferDays ENT-9259 --- src/components/app/data/hooks/index.js | 2 +- .../app/data/hooks/useCourseMetadata.js | 6 +++--- .../app/data/hooks/useCourseMetadata.test.jsx | 6 +++--- .../hooks/useCourseRedemptionEligibility.js | 6 +++--- .../useCourseRedemptionEligibility.test.jsx | 6 +++--- .../data/hooks/useLateEnrollmentBufferDays.js | 8 ++++++++ ...jsx => useLateEnrollmentBufferDays.test.jsx} | 8 ++++---- .../data/hooks/useLateRedemptionBufferDays.js | 8 -------- src/components/app/data/queries/queries.js | 4 ++-- src/components/app/data/utils.js | 17 ++++++++--------- src/components/app/data/utils.test.js | 5 +++-- src/components/course/data/courseLoader.js | 8 +++++--- .../routes/externalCourseEnrollmentLoader.js | 6 +++--- 13 files changed, 46 insertions(+), 44 deletions(-) create mode 100644 src/components/app/data/hooks/useLateEnrollmentBufferDays.js rename src/components/app/data/hooks/{useLateRedemptionBufferDays.test.jsx => useLateEnrollmentBufferDays.test.jsx} (95%) delete mode 100644 src/components/app/data/hooks/useLateRedemptionBufferDays.js diff --git a/src/components/app/data/hooks/index.js b/src/components/app/data/hooks/index.js index 08174ff54b..a1b9f9ee33 100644 --- a/src/components/app/data/hooks/index.js +++ b/src/components/app/data/hooks/index.js @@ -33,7 +33,7 @@ export { default as useIsAssignmentsOnlyLearner } from './useIsAssignmentsOnlyLe export { default as useNProgressLoader } from './useNProgressLoader'; export { default as useNotices } from './useNotices'; export { default as useLearnerSkillLevels } from './useLearnerSkillLevels'; -export { default as useLateRedemptionBufferDays } from './useLateRedemptionBufferDays'; +export { default as useLateEnrollmentBufferDays } from './useLateEnrollmentBufferDays'; export { default as useProgramDetails } from './useProgramDetails'; export { default as useLearnerProgramProgressData } from './useLearnerProgramProgressData'; export { default as useLearnerPathwayProgressData } from './useLearnerPathwayProgressData'; diff --git a/src/components/app/data/hooks/useCourseMetadata.js b/src/components/app/data/hooks/useCourseMetadata.js index 8794db5fe6..42c925b66a 100644 --- a/src/components/app/data/hooks/useCourseMetadata.js +++ b/src/components/app/data/hooks/useCourseMetadata.js @@ -3,7 +3,7 @@ import { useParams, useSearchParams } from 'react-router-dom'; import { queryCourseMetadata } from '../queries'; import { getAvailableCourseRuns } from '../utils'; -import useLateRedemptionBufferDays from './useLateRedemptionBufferDays'; +import useLateEnrollmentBufferDays from './useLateEnrollmentBufferDays'; /** * Retrieves the course metadata for the given enterprise customer and course key. @@ -16,7 +16,7 @@ export default function useCourseMetadata(queryOptions = {}) { // `requestUrl.searchParams` uses `URLSearchParams`, which decodes `+` as a space, so we // need to replace it with `+` again to be a valid course run key. const courseRunKey = searchParams.get('course_run_key')?.replaceAll(' ', '+'); - const isEnrollableBufferDays = useLateRedemptionBufferDays({ + const lateEnrollmentBufferDays = useLateEnrollmentBufferDays({ enabled: !!courseKey, }); return useQuery({ @@ -27,7 +27,7 @@ export default function useCourseMetadata(queryOptions = {}) { if (!data) { return data; } - const availableCourseRuns = getAvailableCourseRuns({ course: data, isEnrollableBufferDays }); + const availableCourseRuns = getAvailableCourseRuns({ course: data, lateEnrollmentBufferDays }); const transformedData = { ...data, availableCourseRuns, diff --git a/src/components/app/data/hooks/useCourseMetadata.test.jsx b/src/components/app/data/hooks/useCourseMetadata.test.jsx index af2183b188..aeabb49b64 100644 --- a/src/components/app/data/hooks/useCourseMetadata.test.jsx +++ b/src/components/app/data/hooks/useCourseMetadata.test.jsx @@ -4,11 +4,11 @@ import dayjs from 'dayjs'; import { useParams, useSearchParams } from 'react-router-dom'; import { queryClient } from '../../../../utils/tests'; import { fetchCourseMetadata } from '../services'; -import useLateRedemptionBufferDays from './useLateRedemptionBufferDays'; +import useLateEnrollmentBufferDays from './useLateEnrollmentBufferDays'; import useCourseMetadata from './useCourseMetadata'; jest.mock('./useEnterpriseCustomer'); -jest.mock('./useLateRedemptionBufferDays'); +jest.mock('./useLateEnrollmentBufferDays'); jest.mock('../services', () => ({ ...jest.requireActual('../services'), @@ -41,7 +41,7 @@ describe('useCourseMetadata', () => { jest.clearAllMocks(); fetchCourseMetadata.mockResolvedValue(mockCourseMetadata); useParams.mockReturnValue({ courseKey: 'edX+DemoX' }); - useLateRedemptionBufferDays.mockReturnValue(undefined); + useLateEnrollmentBufferDays.mockReturnValue(undefined); useSearchParams.mockReturnValue([new URLSearchParams({ course_run_key: 'course-v1:edX+DemoX+T2024' })]); }); it('should handle resolved value correctly with no select function passed', async () => { diff --git a/src/components/app/data/hooks/useCourseRedemptionEligibility.js b/src/components/app/data/hooks/useCourseRedemptionEligibility.js index 6bb117ab00..89e6ab42d5 100644 --- a/src/components/app/data/hooks/useCourseRedemptionEligibility.js +++ b/src/components/app/data/hooks/useCourseRedemptionEligibility.js @@ -4,7 +4,7 @@ import { useQuery } from '@tanstack/react-query'; import useCourseMetadata from './useCourseMetadata'; import { queryCanRedeem } from '../queries'; import useEnterpriseCustomer from './useEnterpriseCustomer'; -import useLateRedemptionBufferDays from './useLateRedemptionBufferDays'; +import useLateEnrollmentBufferDays from './useLateEnrollmentBufferDays'; export function transformCourseRedemptionEligibility({ courseMetadata, @@ -45,10 +45,10 @@ export default function useCourseRedemptionEligibility(queryOptions = {}) { const { select, ...queryOptionsRest } = queryOptions; const { data: enterpriseCustomer } = useEnterpriseCustomer(); const { data: courseMetadata } = useCourseMetadata(); - const isEnrollableBufferDays = useLateRedemptionBufferDays(); + const lateEnrollmentBufferDays = useLateEnrollmentBufferDays(); return useQuery({ - ...queryCanRedeem(enterpriseCustomer.uuid, courseMetadata, isEnrollableBufferDays), + ...queryCanRedeem(enterpriseCustomer.uuid, courseMetadata, lateEnrollmentBufferDays), enabled: !!courseMetadata, select: (data) => { const transformedData = transformCourseRedemptionEligibility({ diff --git a/src/components/app/data/hooks/useCourseRedemptionEligibility.test.jsx b/src/components/app/data/hooks/useCourseRedemptionEligibility.test.jsx index 407af47ebe..1b5d7c5daa 100644 --- a/src/components/app/data/hooks/useCourseRedemptionEligibility.test.jsx +++ b/src/components/app/data/hooks/useCourseRedemptionEligibility.test.jsx @@ -7,12 +7,12 @@ import useEnterpriseCustomer from './useEnterpriseCustomer'; import { queryClient } from '../../../../utils/tests'; import { fetchCanRedeem } from '../services'; import useCourseMetadata from './useCourseMetadata'; -import { useCourseRedemptionEligibility, useLateRedemptionBufferDays } from './index'; +import { useCourseRedemptionEligibility, useLateEnrollmentBufferDays } from './index'; import { transformCourseRedemptionEligibility } from './useCourseRedemptionEligibility'; jest.mock('./useEnterpriseCustomer'); jest.mock('./useCourseMetadata'); -jest.mock('./useLateRedemptionBufferDays'); +jest.mock('./useLateEnrollmentBufferDays'); jest.mock('../services', () => ({ ...jest.requireActual('../services'), fetchCanRedeem: jest.fn().mockResolvedValue(null), @@ -86,7 +86,7 @@ describe('useCourseRedemptionEligibility', () => { fetchCanRedeem.mockResolvedValue(mockCanRedeemData); useParams.mockReturnValue({ courseRunKey: mockCourseRunKey }); useCourseMetadata.mockReturnValue({ data: mockCourseMetadata }); - useLateRedemptionBufferDays.mockReturnValue(undefined); + useLateEnrollmentBufferDays.mockReturnValue(undefined); }); it('should handle resolved value correctly', async () => { const { result, waitForNextUpdate } = renderHook(() => useCourseRedemptionEligibility(), { wrapper: Wrapper }); diff --git a/src/components/app/data/hooks/useLateEnrollmentBufferDays.js b/src/components/app/data/hooks/useLateEnrollmentBufferDays.js new file mode 100644 index 0000000000..f3d7b73b68 --- /dev/null +++ b/src/components/app/data/hooks/useLateEnrollmentBufferDays.js @@ -0,0 +1,8 @@ +import { getLateEnrollmentBufferDays } from '../utils'; +import useRedeemablePolicies from './useRedeemablePolicies'; + +export default function useLateEnrollmentBufferDays(queryOptions = {}) { + const { data } = useRedeemablePolicies(queryOptions); + const { redeemablePolicies } = data || {}; + return getLateEnrollmentBufferDays(redeemablePolicies); +} diff --git a/src/components/app/data/hooks/useLateRedemptionBufferDays.test.jsx b/src/components/app/data/hooks/useLateEnrollmentBufferDays.test.jsx similarity index 95% rename from src/components/app/data/hooks/useLateRedemptionBufferDays.test.jsx rename to src/components/app/data/hooks/useLateEnrollmentBufferDays.test.jsx index 869eb524e8..bb793d00e2 100644 --- a/src/components/app/data/hooks/useLateRedemptionBufferDays.test.jsx +++ b/src/components/app/data/hooks/useLateEnrollmentBufferDays.test.jsx @@ -4,7 +4,7 @@ import dayjs from 'dayjs'; import { AppContext } from '@edx/frontend-platform/react'; import { queryClient } from '../../../../utils/tests'; import { fetchRedeemablePolicies } from '../services'; -import useLateRedemptionBufferDays from './useLateRedemptionBufferDays'; +import useLateEnrollmentBufferDays from './useLateEnrollmentBufferDays'; import { authenticatedUserFactory, enterpriseCustomerFactory } from '../services/data/__factories__'; import useEnterpriseCustomer from './useEnterpriseCustomer'; import { LATE_ENROLLMENTS_BUFFER_DAYS } from '../../../../config/constants'; @@ -63,7 +63,7 @@ const mockRedeemablePolicies = { }; const mockEnterpriseCustomer = enterpriseCustomerFactory(); const mockAuthenticatedUser = authenticatedUserFactory(); -describe('useLateRedemptionBufferDays', () => { +describe('useLateEnrollmentBufferDays', () => { const Wrapper = ({ children }) => ( @@ -77,7 +77,7 @@ describe('useLateRedemptionBufferDays', () => { fetchRedeemablePolicies.mockResolvedValue(mockRedeemablePolicies); }); it('should handle resolved value correctly', async () => { - const { result, waitForNextUpdate } = renderHook(() => useLateRedemptionBufferDays(), { wrapper: Wrapper }); + const { result, waitForNextUpdate } = renderHook(() => useLateEnrollmentBufferDays(), { wrapper: Wrapper }); await waitForNextUpdate(); expect(result.current).toEqual(LATE_ENROLLMENTS_BUFFER_DAYS); }); @@ -106,7 +106,7 @@ describe('useLateRedemptionBufferDays', () => { }; fetchRedeemablePolicies.mockResolvedValue(updatedMockRedeemablePolicies); - const { result, waitForNextUpdate } = renderHook(() => useLateRedemptionBufferDays(), { wrapper: Wrapper }); + const { result, waitForNextUpdate } = renderHook(() => useLateEnrollmentBufferDays(), { wrapper: Wrapper }); await waitForNextUpdate(); expect(result.current).toEqual(undefined); }); diff --git a/src/components/app/data/hooks/useLateRedemptionBufferDays.js b/src/components/app/data/hooks/useLateRedemptionBufferDays.js deleted file mode 100644 index d9a9819aec..0000000000 --- a/src/components/app/data/hooks/useLateRedemptionBufferDays.js +++ /dev/null @@ -1,8 +0,0 @@ -import { getLateRedemptionBufferDays } from '../utils'; -import useRedeemablePolicies from './useRedeemablePolicies'; - -export default function useLateRedemptionBufferDays(queryOptions = {}) { - const { data } = useRedeemablePolicies(queryOptions); - const { redeemablePolicies } = data || {}; - return getLateRedemptionBufferDays(redeemablePolicies); -} diff --git a/src/components/app/data/queries/queries.js b/src/components/app/data/queries/queries.js index 7335d96cd8..eb82a1a4ba 100644 --- a/src/components/app/data/queries/queries.js +++ b/src/components/app/data/queries/queries.js @@ -259,10 +259,10 @@ export function queryCanRedeemContextQueryKey(enterpriseUuid, courseKey) { * ._ctx.canRedeem(availableCourseRunKeys) * @returns {Types.QueryOptions} */ -export function queryCanRedeem(enterpriseUuid, courseMetadata, isEnrollableBufferDays) { +export function queryCanRedeem(enterpriseUuid, courseMetadata, lateEnrollmentBufferDays) { const availableCourseRuns = getAvailableCourseRuns({ course: courseMetadata, - isEnrollableBufferDays, + lateEnrollmentBufferDays, }); const availableCourseRunKeys = availableCourseRuns.map(({ key }) => key); return queries diff --git a/src/components/app/data/utils.js b/src/components/app/data/utils.js index 97e6847a30..96bf8f8f15 100644 --- a/src/components/app/data/utils.js +++ b/src/components/app/data/utils.js @@ -433,7 +433,7 @@ export function retrieveErrorMessage(error) { * @returns {number|undefined} - Returns the number of late redemption buffer days * if any policy has late redemption enabled. */ -export function getLateRedemptionBufferDays(redeemablePolicies) { +export function getLateEnrollmentBufferDays(redeemablePolicies) { if (!redeemablePolicies) { return undefined; } @@ -443,8 +443,7 @@ export function getLateRedemptionBufferDays(redeemablePolicies) { // itself back to False after a finite period of time. policy.isLateRedemptionEnabled )); - const isEnrollableBufferDays = anyPolicyHasLateRedemptionEnabled ? LATE_ENROLLMENTS_BUFFER_DAYS : undefined; - return isEnrollableBufferDays; + return anyPolicyHasLateRedemptionEnabled ? LATE_ENROLLMENTS_BUFFER_DAYS : undefined; } // See https://2u-internal.atlassian.net/wiki/spaces/WS/pages/8749811/Enroll+button+and+Course+Run+Selector+Logic @@ -466,10 +465,10 @@ export function isArchived(courseRun) { * This function is used by logic that determines which runs should be visible on the course about page. * * @param {object} course - The course containing runs which will be a superset of the returned runs. - * @param {number} isEnrollableBufferDays - number of days to buffer the enrollment end date, or undefined. + * @param {number} lateEnrollmentBufferDays - number of days to buffer the enrollment end date, or undefined. * @returns List of course runs. */ -export function getAvailableCourseRuns({ course, isEnrollableBufferDays }) { +export function getAvailableCourseRuns({ course, lateEnrollmentBufferDays }) { if (!course?.courseRuns) { return []; } @@ -483,7 +482,7 @@ export function getAvailableCourseRuns({ course, isEnrollableBufferDays }) { // but the rules around the following fields are relaxed: // // * courseRun.isEnrollable: This field represents the enrollment window actually stored in the database. However, - // during late enrollment we expand the end date of the enrollment window by isEnrollableBufferDays. + // during late enrollment we expand the end date of the enrollment window by lateEnrollmentBufferDays. // * courseRun.isMarketable: This field is True when the run is published, has seats, and has a marketing URL. Since // late enrollment potentially means enrolling into an unpublished run, we must ignore the run state. const lateEnrollmentAvailableCourseRunsFilter = (courseRun) => { @@ -501,13 +500,13 @@ export function getAvailableCourseRuns({ course, isEnrollableBufferDays }) { // In cases where we don't expect the buffer to change behavior, fallback to the backend-provided value. return standardAvailableCourseRunsFilter(courseRun); } - const bufferedEnrollDeadline = dayjs(courseRun.enrollmentEnd).add(isEnrollableBufferDays, 'day'); + const bufferedEnrollDeadline = dayjs(courseRun.enrollmentEnd).add(lateEnrollmentBufferDays, 'day'); return today.isBefore(bufferedEnrollDeadline); }; - // isEnrollableBufferDays is used as a heuristic to determine if the late enrollment feature is enabled. + // lateEnrollmentBufferDays being set is used as a heuristic to determine if the late enrollment feature is enabled. return course.courseRuns.filter( - isDefinedAndNotNull(isEnrollableBufferDays) + isDefinedAndNotNull(lateEnrollmentBufferDays) ? lateEnrollmentAvailableCourseRunsFilter : standardAvailableCourseRunsFilter, ); diff --git a/src/components/app/data/utils.test.js b/src/components/app/data/utils.test.js index 39538ae743..deb936c86d 100644 --- a/src/components/app/data/utils.test.js +++ b/src/components/app/data/utils.test.js @@ -619,8 +619,9 @@ describe('getAvailableCourseRuns', () => { MockDate.set('2023-07-05T00:00:00Z'); expect(getAvailableCourseRuns({ course: sampleCourseRunDataWithRecentRuns.courseData })) .toEqual(sampleCourseRunDataWithRecentRuns.courseData.courseRuns.slice(0, 1)); - expect(getAvailableCourseRuns({ course: sampleCourseRunDataWithRecentRuns.courseData, isEnrollableBufferDays: 60 })) - .toEqual(sampleCourseRunDataWithRecentRuns.courseData.courseRuns.slice(0, 3)); + expect(getAvailableCourseRuns( + { course: sampleCourseRunDataWithRecentRuns.courseData, lateEnrollmentBufferDays: 60 }, + )).toEqual(sampleCourseRunDataWithRecentRuns.courseData.courseRuns.slice(0, 3)); }); it('returns empty array if course runs are not available', () => { sampleCourseRunData.courseData.courseRuns = []; diff --git a/src/components/course/data/courseLoader.js b/src/components/course/data/courseLoader.js index 40a2a120fe..7cba4a99c8 100644 --- a/src/components/course/data/courseLoader.js +++ b/src/components/course/data/courseLoader.js @@ -7,7 +7,7 @@ import { queryEnterpriseCourseEnrollments, extractEnterpriseCustomer, queryRedeemablePolicies, - getLateRedemptionBufferDays, + getLateEnrollmentBufferDays, querySubscriptions, queryLicenseRequests, queryCouponCodeRequests, @@ -77,9 +77,11 @@ export default function makeCourseLoader(queryClient) { enterpriseUuid: enterpriseCustomer.uuid, lmsUserId: authenticatedUser.userId, })); - const isEnrollableBufferDays = getLateRedemptionBufferDays(redeemableLearnerCreditPolicies.redeemablePolicies); + const lateEnrollmentBufferDays = getLateEnrollmentBufferDays( + redeemableLearnerCreditPolicies.redeemablePolicies, + ); return queryClient.ensureQueryData( - queryCanRedeem(enterpriseCustomer.uuid, courseMetadata, isEnrollableBufferDays), + queryCanRedeem(enterpriseCustomer.uuid, courseMetadata, lateEnrollmentBufferDays), ); }), queryClient.ensureQueryData(queryEnterpriseCourseEnrollments(enterpriseCustomer.uuid)), diff --git a/src/components/course/routes/externalCourseEnrollmentLoader.js b/src/components/course/routes/externalCourseEnrollmentLoader.js index 5242a77f14..4b93d36fa4 100644 --- a/src/components/course/routes/externalCourseEnrollmentLoader.js +++ b/src/components/course/routes/externalCourseEnrollmentLoader.js @@ -2,7 +2,7 @@ import { generatePath, redirect } from 'react-router-dom'; import { extractEnterpriseCustomer, - getLateRedemptionBufferDays, + getLateEnrollmentBufferDays, queryCanRedeem, queryCourseMetadata, queryRedeemablePolicies, @@ -41,9 +41,9 @@ export default function makeExternalCourseEnrollmentLoader(queryClient) { enterpriseUuid: enterpriseCustomer.uuid, lmsUserId: authenticatedUser.userId, })); - const isEnrollableBufferDays = getLateRedemptionBufferDays(redeemableLearnerCreditPolicies.redeemablePolicies); + const lateEnrollmentBufferDays = getLateEnrollmentBufferDays(redeemableLearnerCreditPolicies.redeemablePolicies); const canRedeem = await queryClient.ensureQueryData( - queryCanRedeem(enterpriseCustomer.uuid, courseMetadata, isEnrollableBufferDays), + queryCanRedeem(enterpriseCustomer.uuid, courseMetadata, lateEnrollmentBufferDays), ); const hasSuccessfulRedemption = !!canRedeem.find(r => r.contentKey === courseRunKey)?.hasSuccessfulRedemption; if (hasSuccessfulRedemption) {