Skip to content

Commit

Permalink
feat: account for learner credit audit upgrade modal UI (#1110)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamstankiewicz committed Jul 15, 2024
1 parent 261da17 commit 00a9728
Show file tree
Hide file tree
Showing 14 changed files with 802 additions and 403 deletions.
44 changes: 27 additions & 17 deletions src/components/app/data/hooks/useCanUpgradeWithLearnerCredit.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@ import { useQuery } from '@tanstack/react-query';
import { queryCanUpgradeWithLearnerCredit } from '../queries';
import useEnterpriseCustomer from './useEnterpriseCustomer';

export function isPolicyRedemptionEnabled({ canRedeemData }) {
const hasSuccessfulRedemption = canRedeemData?.hasSuccessfulRedemption;
const redeemableSubsidyAccessPolicy = canRedeemData?.redeemableSubsidyAccessPolicy;
return hasSuccessfulRedemption || !!redeemableSubsidyAccessPolicy;
}

/**
* Determines whether the given courseRunKey is redeemable with their learner credit policies
* Returns the first redeemableSubsidyAccessPolicy
Expand All @@ -21,18 +15,34 @@ export default function useCanUpgradeWithLearnerCredit(courseRunKey, queryOption
...queryCanUpgradeWithLearnerCredit(enterpriseCustomer.uuid, courseRunKey),
...queryOptionsRest,
select: (data) => {
if (data.length === 0) {
return {
applicableSubsidyAccessPolicy: null,
};
}
return {
applicableSubsidyAccessPolicy: data.flatMap((canRedeemData) => ({
...canRedeemData,
listPrice: canRedeemData?.listPrice?.usd,
isPolicyRedemptionEnabled: isPolicyRedemptionEnabled({ canRedeemData }),
}))[0],
// Base transformed data
const transformedData = {
applicableSubsidyAccessPolicy: null,
listPrice: null,
};

// Determine whether the course run key is redeemable. If so, update the transformed data with the
// applicable subsidy access policy and list price.
const redeemableCourseRun = data.filter((canRedeemData) => (
canRedeemData.canRedeem && canRedeemData.redeemableSubsidyAccessPolicy
))[0];
if (redeemableCourseRun) {
const applicableSubsidyAccessPolicy = redeemableCourseRun.redeemableSubsidyAccessPolicy;
applicableSubsidyAccessPolicy.isPolicyRedemptionEnabled = true;
transformedData.applicableSubsidyAccessPolicy = applicableSubsidyAccessPolicy;
transformedData.listPrice = redeemableCourseRun.listPrice.usd;
}

// 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;
},
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,35 +48,50 @@ const mockCanRedeemData = [{
reasons: [],
}];

const Wrapper = ({ children }) => (
<QueryClientProvider client={queryClient()}>
{children}
</QueryClientProvider>
);

describe('useCanUpgradeWithLearnerCredit', () => {
const Wrapper = ({ children }) => (
<QueryClientProvider client={queryClient()}>
{children}
</QueryClientProvider>
);
beforeEach(() => {
jest.clearAllMocks();
useEnterpriseCustomer.mockReturnValue({ data: mockEnterpriseCustomer });
fetchCanRedeem.mockResolvedValue(mockCanRedeemData);
});
it('should handle resolved value correctly', async () => {
const {
result,
waitForNextUpdate,
} = renderHook(() => useCanUpgradeWithLearnerCredit(mockCourseRunKey), { wrapper: Wrapper });
await waitForNextUpdate();

it.each([
{ hasCustomSelect: false },
{ hasCustomSelect: true },
])('should handle resolved value correctly (%s)', async ({ hasCustomSelect }) => {
const queryOptions = {};
if (hasCustomSelect) {
// mock the custom select transform function to simply return the same transformed data
queryOptions.select = jest.fn(data => data.transformed);
}
const { result, waitForNextUpdate } = renderHook(
() => useCanUpgradeWithLearnerCredit(mockCourseRunKey, queryOptions),
{ wrapper: Wrapper },
);
await waitForNextUpdate();
const expectedTransformedResult = {
applicableSubsidyAccessPolicy: {
...mockCanRedeemData[0].redeemableSubsidyAccessPolicy,
isPolicyRedemptionEnabled: true,
},
listPrice: mockCanRedeemData[0].listPrice.usd,
};
if (hasCustomSelect) {
expect(queryOptions.select).toHaveBeenCalledWith({
original: mockCanRedeemData,
transformed: expectedTransformedResult,
});
}
expect(result.current).toEqual(
expect.objectContaining({
data: {
applicableSubsidyAccessPolicy: {
...mockCanRedeemData[0],
listPrice: mockCanRedeemData[0].listPrice.usd,
isPolicyRedemptionEnabled: true,
},
},
data: expectedTransformedResult,
isLoading: false,
isFetching: false,
}),
);
});
Expand Down
4 changes: 2 additions & 2 deletions src/components/app/data/queries/queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,12 +282,12 @@ export function queryCanRedeem(enterpriseUuid, courseMetadata, isEnrollableBuffe
* ._ctx.canRedeem(availableCourseRunKeys)
* @returns {Types.QueryOptions}
*/
export function queryCanUpgradeWithLearnerCredit(enterpriseUuid, courseRunKeys) {
export function queryCanUpgradeWithLearnerCredit(enterpriseUuid, courseRunKey) {
return queries
.enterprise
.enterpriseCustomer(enterpriseUuid)
._ctx.course(null)
._ctx.canRedeem(courseRunKeys);
._ctx.canRedeem([courseRunKey]);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/components/app/data/services/course.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export async function fetchCourseMetadata(courseKey, courseRunKey) {
}

export async function fetchCourseRunMetadata(courseRunKey) {
const courseRunMetadataUrl = `${getConfig().DISCOVERY_API_BASE_URL}/api/v1/course_runs/${courseRunKey}`;
const courseRunMetadataUrl = `${getConfig().DISCOVERY_API_BASE_URL}/api/v1/course_runs/${courseRunKey}/`;
try {
const response = await getAuthenticatedHttpClient().get(courseRunMetadataUrl);
return camelCaseObject(response.data);
Expand Down
2 changes: 1 addition & 1 deletion src/components/app/data/services/course.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ describe('fetchCourseMetadata', () => {
});

describe('fetchCourseRunMetadata', () => {
const COURSE_RUN_METADATA = `${APP_CONFIG.DISCOVERY_API_BASE_URL}/api/v1/course_runs/${mockCourseRunKey}`;
const COURSE_RUN_METADATA = `${APP_CONFIG.DISCOVERY_API_BASE_URL}/api/v1/course_runs/${mockCourseRunKey}/`;
const courseRunMetadata = {
key: mockCourseRunKey,
title: 'edX Demonstration Course',
Expand Down
Loading

0 comments on commit 00a9728

Please sign in to comment.