Skip to content

Commit

Permalink
fix: ensure useSubscriptions and useEnterpriseCourseEnrollments uses …
Browse files Browse the repository at this point in the history
…queryOptions.select from args
  • Loading branch information
adamstankiewicz committed Dec 12, 2024
1 parent cb3d2bf commit f371801
Show file tree
Hide file tree
Showing 7 changed files with 277 additions and 61 deletions.
1 change: 1 addition & 0 deletions src/components/app/data/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export const ASSIGNMENT_TYPES = {
EXPIRED: 'expired',
ERRORED: 'errored',
EXPIRING: 'expiring',
REVERSED: 'reversed',
};

// When the start date is before this number of days before today, display the alternate start date (fixed to today).
Expand Down
98 changes: 77 additions & 21 deletions src/components/app/data/hooks/useEnterpriseCourseEnrollments.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,41 +30,90 @@ export const transformAllEnrollmentsByStatus = ({
/**
* Retrieves the relevant enterprise course enrollments, subsidy requests (e.g., license
* requests), and content assignments for the active enterprise customer user.
* @param {Types.UseQueryOptions} queryOptions The query options.
* @returns {Types.UseQueryResult} The query results.
*/
export default function useEnterpriseCourseEnrollments(queryOptions = {}) {
const isEnabled = queryOptions.enabled;
const {
enrollmentQueryOptions = {},
licenseRequestQueryOptions = {},
couponCodeRequestQueryOptions = {},
contentAssignmentQueryOptions = {},
} = queryOptions;
const { select: selectEnrollment, ...enrollmentQueryOptionsRest } = enrollmentQueryOptions;
const { select: selectLicenseRequest, ...licenseRequestQueryOptionsRest } = licenseRequestQueryOptions;
const { select: selectCouponCodeRequest, ...couponCodeRequestQueryOptionsRest } = couponCodeRequestQueryOptions;
const { select: selectContentAssignment, ...contentAssignmentQueryOptionsRest } = contentAssignmentQueryOptions;

const { data: enterpriseCustomer } = useEnterpriseCustomer();
const bffQueryFallback = {
...queryEnterpriseCourseEnrollments(enterpriseCustomer.uuid),
...queryOptions,
select: (data) => data.map(transformCourseEnrollment),
enabled: isEnabled,
};

const { data: enterpriseCourseEnrollments } = useBFF({
bffQueryOptions: {
...queryOptions,
select: (data) => data.enterpriseCourseEnrollments.map(transformCourseEnrollment),
enabled: isEnabled,
select: (data) => {
const transformedData = data.enterpriseCourseEnrollments.map(transformCourseEnrollment);
if (selectEnrollment) {
return selectEnrollment({
original: data,
transformed: transformedData,
});
}
return transformedData;
},
...enrollmentQueryOptionsRest,
},
fallbackQueryConfig: {
...queryEnterpriseCourseEnrollments(enterpriseCustomer.uuid),
...queryOptions,
select: (data) => {
const transformedData = data.map(transformCourseEnrollment);
if (selectEnrollment) {
return selectEnrollment({
original: data,
transformed: transformedData,
});
}
return transformedData;
},
...enrollmentQueryOptionsRest,
},
fallbackQueryConfig: bffQueryFallback,
});

const { data: { requests } } = useBrowseAndRequest({
subscriptionLicensesQueryOptions: {
select: (data) => data.map((subsidyRequest) => transformSubsidyRequest({
subsidyRequest,
slug: enterpriseCustomer.slug,
})),
enabled: isEnabled,
select: (data) => {
const transformedData = data.map((subsidyRequest) => transformSubsidyRequest({
subsidyRequest,
slug: enterpriseCustomer.slug,
}));
if (selectLicenseRequest) {
return selectLicenseRequest({
original: data,
transformed: transformedData,
});
}
return transformedData;
},
...licenseRequestQueryOptionsRest,
},
couponCodesQueryOptions: {
select: (data) => data.map((subsidyRequest) => transformSubsidyRequest({
subsidyRequest,
slug: enterpriseCustomer.slug,
})),
enabled: isEnabled,
select: (data) => {
const transformedData = data.map((subsidyRequest) => transformSubsidyRequest({
subsidyRequest,
slug: enterpriseCustomer.slug,
}));
if (selectCouponCodeRequest) {
return selectCouponCodeRequest({
original: data,
transformed: transformedData,
});
}
return transformedData;
},
...couponCodeRequestQueryOptionsRest,
},
});

const { data: contentAssignments } = useRedeemablePolicies({
select: (data) => {
const { learnerContentAssignments } = data;
Expand All @@ -78,10 +127,17 @@ export default function useEnterpriseCourseEnrollments(queryOptions = {}) {
enterpriseCustomer.slug,
));
});
if (selectContentAssignment) {
return selectContentAssignment({
original: data,
transformed: transformedAssignments,
});
}
return transformedAssignments;
},
enabled: isEnabled,
...contentAssignmentQueryOptionsRest,
});

// TODO: Talk about how we don't have access to weeksToComplete on the dashboard page.
const allEnrollmentsByStatus = useMemo(() => transformAllEnrollmentsByStatus({
enterpriseCourseEnrollments,
Expand Down
110 changes: 81 additions & 29 deletions src/components/app/data/hooks/useEnterpriseCourseEnrollments.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jest.mock('./useBFF');

const mockEnterpriseCustomer = enterpriseCustomerFactory();
const mockAuthenticatedUser = authenticatedUserFactory();
const mockCourseEnrollments = {
const mockCourseEnrollment = {
displayName: 'Education',
micromastersTitle: 'Demo in higher education',
courseRunUrl: 'test-course-url',
Expand Down Expand Up @@ -114,6 +114,16 @@ const mockRedeemablePolicies = {
},
};

const expectedTransformedRequests = (request) => ({
courseRunId: request.courseId,
title: request.courseTitle,
orgName: request.coursePartners?.map(partner => partner.name).join(', '),
courseRunStatus: COURSE_STATUSES.requested,
linkToCourse: `${mockEnterpriseCustomer.slug}/course/${request.courseId}`,
created: request.created,
notifications: [],
});

describe('useEnterpriseCourseEnrollments', () => {
const Wrapper = ({ children }) => (
<QueryClientProvider client={queryClient()}>
Expand All @@ -125,35 +135,28 @@ describe('useEnterpriseCourseEnrollments', () => {
beforeEach(() => {
jest.clearAllMocks();
useEnterpriseCustomer.mockReturnValue({ data: mockEnterpriseCustomer });
fetchEnterpriseCourseEnrollments.mockResolvedValue([mockCourseEnrollments]);
fetchEnterpriseCourseEnrollments.mockResolvedValue([mockCourseEnrollment]);
fetchBrowseAndRequestConfiguration.mockResolvedValue(mockBrowseAndRequestConfiguration);
fetchLicenseRequests.mockResolvedValue([mockLicenseRequests]);
fetchCouponCodeRequests.mockResolvedValue([mockCouponCodeRequests]);
fetchRedeemablePolicies.mockResolvedValue(mockRedeemablePolicies);
useBFF.mockReturnValue({ data: [mockCourseEnrollments].map(transformCourseEnrollment) });
useBFF.mockReturnValue({ data: [mockCourseEnrollment].map(transformCourseEnrollment) });
});
it('should return transformed return values from course enrollments API', async () => {
const { result, waitForNextUpdate } = renderHook(() => useEnterpriseCourseEnrollments(), { wrapper: Wrapper });
await waitForNextUpdate();
const expectedEnterpriseCourseEnrollments = {
title: mockCourseEnrollments.displayName,
microMastersTitle: mockCourseEnrollments.micromastersTitle,
linkToCourse: mockCourseEnrollments.courseRunUrl,
linkToCertificate: mockCourseEnrollments.certificateDownloadUrl,
hasEmailsEnabled: mockCourseEnrollments.emailsEnabled,
notifications: mockCourseEnrollments.dueDates,
canUnenroll: canUnenrollCourseEnrollment(mockCourseEnrollments),

it.each([
{ hasQueryOptions: false },
{ hasQueryOptions: true },
])('should return transformed enrollments data (%s)', async ({ hasQueryOptions }) => {
const expectedEnterpriseCourseEnrollments = [{
title: mockCourseEnrollment.displayName,
microMastersTitle: mockCourseEnrollment.micromastersTitle,
linkToCourse: mockCourseEnrollment.courseRunUrl,
linkToCertificate: mockCourseEnrollment.certificateDownloadUrl,
hasEmailsEnabled: mockCourseEnrollment.emailsEnabled,
notifications: mockCourseEnrollment.dueDates,
canUnenroll: canUnenrollCourseEnrollment(mockCourseEnrollment),
isCourseAssigned: false,
};
const expectedTransformedRequests = (request) => ({
courseRunId: request.courseId,
title: request.courseTitle,
orgName: request.coursePartners?.map(partner => partner.name).join(', '),
courseRunStatus: COURSE_STATUSES.requested,
linkToCourse: `${mockEnterpriseCustomer.slug}/course/${request.courseId}`,
created: request.created,
notifications: [],
});
}];
const expectedRequests = {
couponCodes: [expectedTransformedRequests(mockCouponCodeRequests)],
subscriptionLicenses: [expectedTransformedRequests(mockLicenseRequests)],
Expand All @@ -177,7 +180,7 @@ describe('useEnterpriseCourseEnrollments', () => {
uuid: mockContentAssignment.uuid,
learnerAcknowledged: mockContentAssignment.learnerAcknowledged,
};
const expectedContentAssignment = {
const expectedContentAssignmentData = {
acceptedAssignments: [],
allocatedAssignments: [expectedTransformedLearnerContentAssignment],
assignmentsForDisplay: [expectedTransformedLearnerContentAssignment],
Expand All @@ -187,15 +190,64 @@ describe('useEnterpriseCourseEnrollments', () => {
expiredAssignments: [],
};

const mockSelectEnrollment = jest.fn().mockReturnValue(expectedEnterpriseCourseEnrollments);
const mockSelectLicenseRequest = jest.fn().mockReturnValue(expectedRequests.subscriptionLicenses);
const mockSelectCouponCodeRequest = jest.fn().mockReturnValue(expectedRequests.couponCodes);
const mockSelectContentAssignment = jest.fn().mockReturnValue(expectedContentAssignmentData);
const mockQueryOptions = {
enrollmentQueryOptions: { select: mockSelectEnrollment },
licenseRequestQueryOptions: { select: mockSelectLicenseRequest },
couponCodeRequestQueryOptions: { select: mockSelectCouponCodeRequest },
contentAssignmentQueryOptions: { select: mockSelectContentAssignment },
};
const queryOptions = hasQueryOptions ? mockQueryOptions : undefined;
const { result, waitForNextUpdate } = renderHook(
() => {
if (hasQueryOptions) {
return useEnterpriseCourseEnrollments(queryOptions);
}
return useEnterpriseCourseEnrollments()

Check failure on line 209 in src/components/app/data/hooks/useEnterpriseCourseEnrollments.test.jsx

View workflow job for this annotation

GitHub Actions / tests

Missing semicolon
},
{ wrapper: Wrapper }

Check failure on line 211 in src/components/app/data/hooks/useEnterpriseCourseEnrollments.test.jsx

View workflow job for this annotation

GitHub Actions / tests

Missing trailing comma
);
await waitForNextUpdate();

// Call the mocked useBFF's input select functions
const useBFFArgs = useBFF.mock.calls[0][0];
const { select: selectBFFQuery } = useBFFArgs.bffQueryOptions;
const { select: selectFallbackBFFQuery } = useBFFArgs.fallbackQueryConfig;
selectBFFQuery({ enterpriseCourseEnrollments: [mockCourseEnrollment] });
selectFallbackBFFQuery([mockCourseEnrollment]);

// Assert that passed select fn query options were called with the correct arguments
if (hasQueryOptions) {
expect(mockSelectLicenseRequest).toHaveBeenCalledWith({
original: [mockLicenseRequests],
transformed: expectedRequests.subscriptionLicenses,
});
expect(mockSelectCouponCodeRequest).toHaveBeenCalledWith({
original: [mockCouponCodeRequests],
transformed: expectedRequests.couponCodes,
});
expect(mockSelectContentAssignment).toHaveBeenCalledWith({
original: mockRedeemablePolicies,
transformed: expectedContentAssignmentData,
});
expect(mockSelectEnrollment).toHaveBeenCalledWith({
original: [mockCourseEnrollment],
transformed: expectedEnterpriseCourseEnrollments,
});
}

const expectedTransformedAllEnrollmentsByStatus = transformAllEnrollmentsByStatus({
enterpriseCourseEnrollments: [expectedEnterpriseCourseEnrollments],
enterpriseCourseEnrollments: expectedEnterpriseCourseEnrollments,
requests: expectedRequests,
contentAssignments: expectedContentAssignment,
contentAssignments: expectedContentAssignmentData,
});

expect(result.current.data.allEnrollmentsByStatus).toEqual(expectedTransformedAllEnrollmentsByStatus);
expect(result.current.data.enterpriseCourseEnrollments).toEqual([expectedEnterpriseCourseEnrollments]);
expect(result.current.data.contentAssignments).toEqual(expectedContentAssignment);
expect(result.current.data.enterpriseCourseEnrollments).toEqual(expectedEnterpriseCourseEnrollments);
expect(result.current.data.contentAssignments).toEqual(expectedContentAssignmentData);
expect(result.current.data.requests).toEqual(expectedRequests);
});
});
24 changes: 20 additions & 4 deletions src/components/app/data/hooks/useSubscriptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,28 @@ import { transformSubscriptionsData } from '../services';
*/
export default function useSubscriptions(queryOptions = {}) {
const { data: enterpriseCustomer } = useEnterpriseCustomer();
const { select, ...queryOptionsRest } = queryOptions;

return useBFF({
bffQueryOptions: {
select: (data) => transformSubscriptionsData(
data?.enterpriseCustomerUserSubsidies?.subscriptions,
{ isBFFData: true },
),
...queryOptionsRest,
select: (data) => {
const transformedData = transformSubscriptionsData(
data?.enterpriseCustomerUserSubsidies?.subscriptions,
{ isBFFData: true },
);

// When custom `select` function is provided in `queryOptions`, call it with original and transformed data.
if (select) {
return select({
original: data,
transformed: transformedData,
});
}

// Otherwise, return the transformed data.
return transformedData;
},
},
fallbackQueryConfig: {
...querySubscriptions(enterpriseCustomer.uuid),
Expand Down
Loading

0 comments on commit f371801

Please sign in to comment.