import React from 'react'; import { AppContext } from '@edx/frontend-platform/react'; import { keyStore } from 'utils'; import { RequestKeys } from 'data/constants/requests'; import { post } from 'data/services/lms/utils'; import api from 'data/services/lms/api'; import * as reduxHooks from 'data/redux/hooks'; import * as apiHooks from './api'; const reduxKeys = keyStore(reduxHooks); jest.mock('data/services/lms/utils', () => ({ post: jest.fn((...args) => ({ post: args })), })); jest.mock('data/services/lms/api', () => ({ initializeList: jest.fn(), updateEntitlementEnrollment: jest.fn(), unenrollFromCourse: jest.fn(), deleteEntitlementEnrollment: jest.fn(), updateEmailSettings: jest.fn(), createCreditRequest: jest.fn(), })); jest.mock('data/redux/hooks', () => ({ useCardCourseRunData: jest.fn(), useCardCreditData: jest.fn(), useCardEntitlementData: jest.fn(), useLoadData: jest.fn(), useMakeNetworkRequest: jest.fn(), useClearRequest: jest.fn(), useEmailConfirmationData: jest.fn(), })); jest.mock('react', () => ({ ...jest.requireActual('react'), useContext: jest.fn(context => context), })); const moduleKeys = keyStore(apiHooks); const testString = 'TEST-string'; const uuid = 'test-UUID'; const cardId = 'test-card-id'; const selection = 'test-selection'; const courseId = 'test-COURSE-id'; const isRefundable = 'test-is-refundable'; const user = 'test-user'; const loadData = jest.fn(); reduxHooks.useLoadData.mockReturnValue(loadData); const clearRequest = jest.fn(); reduxHooks.useClearRequest.mockReturnValue(clearRequest); reduxHooks.useCardCourseRunData.mockReturnValue({ courseId }); reduxHooks.useCardEntitlementData.mockReturnValue({ uuid, isRefundable }); let hook; let out; const testInitCardHook = (hookKey) => { test(`initializes ${hookKey} with cardId`, () => { expect(reduxHooks[hookKey]).toHaveBeenCalledWith(cardId); }); }; const initializeApp = jest.fn(); describe('api hooks', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('useNetworkRequest', () => { const makeNetworkRequest = jest.fn(args => ({ networkRequest: args })); const action = jest.fn((...actionArgs) => ({ action: actionArgs })); const args = { some: 'test', args: 'for you' }; it('returns network request based on incoming action', () => { reduxHooks.useMakeNetworkRequest.mockReturnValue(makeNetworkRequest); hook = apiHooks.useNetworkRequest(action, args); expect(hook()).toEqual(makeNetworkRequest({ promise: action(), ...args })); }); it('forwards action arguments', () => { reduxHooks.useMakeNetworkRequest.mockReturnValue(makeNetworkRequest); hook = apiHooks.useNetworkRequest(action, args); const actionArgs = [1, 2, 3]; expect(hook(...actionArgs)).toEqual( makeNetworkRequest({ promise: action(...actionArgs), ...args }), ); }); }); describe('network requests', () => { const mockUseNetworkRequest = jest.fn((action, args) => ({ action, args })); const testRequestKey = (requestKey) => { test('requestKey', () => { expect(hook.args.requestKey).toEqual(requestKey); }); }; beforeEach(() => { jest.spyOn(apiHooks, moduleKeys.useNetworkRequest).mockImplementation(mockUseNetworkRequest); }); describe('useInitializeApp', () => { beforeEach(() => { hook = apiHooks.useInitializeApp(); }); it('calls initialize api method', () => { expect(hook.action).toEqual(api.initializeList); }); testRequestKey(RequestKeys.initialize); it('initializes load data hook', () => { expect(reduxHooks.useLoadData).toHaveBeenCalledWith(); }); it('calls loadData with data on success', () => { hook.args.onSuccess({ data: testString }); expect(loadData).toHaveBeenCalledWith(testString); }); }); describe('entitlement enrollment hooks', () => { beforeEach(() => { jest.spyOn(apiHooks, moduleKeys.useInitializeApp).mockReturnValue(initializeApp); }); const testInitialization = () => { it('initializes useInitializeApp', () => { expect(apiHooks.useInitializeApp).toHaveBeenCalledWith(); }); testInitCardHook(reduxKeys.useCardEntitlementData); }; const testArgs = (requestKey) => { testRequestKey(requestKey); it('initializes app on success', () => { expect(hook.args.onSuccess).toEqual(initializeApp); }); }; describe('useNewEntitlementEnrollment', () => { beforeEach(() => { hook = apiHooks.useNewEntitlementEnrollment(cardId); }); testInitialization(); testArgs(RequestKeys.newEntitlementEnrollment); it('calls updateEntitlementEnrollment api method', () => { hook.action(selection); expect(api.updateEntitlementEnrollment) .toHaveBeenCalledWith({ uuid, courseId: selection }); }); }); describe('useSwitchEntitlementEnrollment', () => { beforeEach(() => { hook = apiHooks.useSwitchEntitlementEnrollment(cardId); }); testInitialization(); testArgs(RequestKeys.switchEntitlementSession); it('calls updateEntitlementEnrollment api method', () => { hook.action(selection); expect(api.updateEntitlementEnrollment) .toHaveBeenCalledWith({ uuid, courseId: selection }); }); }); describe('useLeaveEntitlementSession', () => { beforeEach(() => { hook = apiHooks.useLeaveEntitlementSession(cardId); }); testInitialization(); testArgs(RequestKeys.leaveEntitlementSession); it('calls updateEntitlementEnrollment api method', () => { hook.action(); expect(api.deleteEntitlementEnrollment) .toHaveBeenCalledWith({ uuid, isRefundable }); }); }); }); describe('useUnenrollFromCourse', () => { beforeEach(() => { hook = apiHooks.useUnenrollFromCourse(cardId); }); testInitCardHook(reduxKeys.useCardCourseRunData); testRequestKey(RequestKeys.unenrollFromCourse); it('calls unenrollFromCourse api method with courseId', () => { hook.action(); expect(api.unenrollFromCourse).toHaveBeenCalledWith({ courseId }); }); }); describe('useMasqueradeAs', () => { beforeEach(() => { hook = apiHooks.useMasqueradeAs(cardId); }); it('initializes load data hook', () => { expect(reduxHooks.useLoadData).toHaveBeenCalledWith(); }); testRequestKey(RequestKeys.masquerade); it('calls initializeList api method', () => { hook.action(user); expect(api.initializeList).toHaveBeenCalledWith({ user }); }); it('loads data on success', () => { hook.args.onSuccess({ data: testString }); expect(loadData).toHaveBeenCalledWith(testString); }); }); describe('useClearMasquerade', () => { beforeEach(() => { jest.spyOn(apiHooks, moduleKeys.useInitializeApp).mockReturnValue(initializeApp); hook = apiHooks.useClearMasquerade(cardId); }); it('initializes clear request redux hook', () => { expect(reduxHooks.useClearRequest).toHaveBeenCalledWith(); }); it('initializes useInitializeApp hook', () => { expect(apiHooks.useInitializeApp).toHaveBeenCalledWith(); }); it('clears masquerade state and initializes app on call', () => { hook(); expect(clearRequest).toHaveBeenCalledWith(RequestKeys.masquerade); expect(initializeApp).toHaveBeenCalledWith(); }); }); describe('useUpdateEmailSettings', () => { const enable = 'test-enable'; beforeEach(() => { hook = apiHooks.useUpdateEmailSettings(cardId); }); testInitCardHook(reduxKeys.useCardCourseRunData); testRequestKey(RequestKeys.updateEmailSettings); it('calls updateEmailSettings api method on call', () => { hook.action(enable); expect(api.updateEmailSettings).toHaveBeenCalledWith({ courseId, enable }); }); }); describe('useSendConfirmEmail', () => { const sendEmailUrl = 'test-send-email-url'; beforeEach(() => { reduxHooks.useEmailConfirmationData.mockReturnValue({ sendEmailUrl }); hook = apiHooks.useSendConfirmEmail(cardId); out = hook(); }); it('initializes useEmailConfirmationData hook', () => { expect(reduxHooks.useEmailConfirmationData).toHaveBeenCalledWith(); }); it('posts to email url on call', () => { expect(out).toEqual(post(sendEmailUrl)); }); }); describe('useCreateCreditRequest', () => { const username = 'test-username'; const providerId = 'test-provider-id'; beforeEach(() => { React.useContext.mockReturnValue({ authenticatedUser: { username } }); reduxHooks.useCardCreditData.mockReturnValue({ providerId }); hook = apiHooks.useCreateCreditRequest(cardId); }); testInitCardHook(reduxKeys.useCardCreditData); testInitCardHook(reduxKeys.useCardCourseRunData); it('initializes username from app context', () => { expect(React.useContext).toHaveBeenCalledWith(AppContext); }); it('calls createCreditRequest api method on call', () => { out = hook(); expect(api.createCreditRequest).toHaveBeenCalledWith({ providerId, courseId, username, }); }); }); }); });