-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: added missing test for subs redux sagas (#796)
* test: added unit test for Secure3DRedirectPage * test: added error messages missing tests * test: added the subscription status saga tests * test: fixed the broken details-redux.test.js * test: added the subscription detail saga test * test: fixed the multiple warning for missing config mocks
- Loading branch information
Showing
12 changed files
with
408 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import React from 'react'; | ||
import { render } from '../test-utils'; | ||
import { | ||
EmbargoErrorMessage, | ||
ProgramUnavailableMessage, | ||
IneligibleProgramErrorMessage, | ||
Unsuccessful3DSMessage, | ||
} from './ErrorMessages'; | ||
|
||
function getCustomTextContent(content, node) { | ||
// eslint-disable-next-line no-shadow | ||
// The textContent property sets or returns the text content of the specified node, and all its descendants. | ||
const hasText = (elem) => elem.textContent === this.searchFor; | ||
const nodeHasText = hasText(node); | ||
const childrenDontHaveText = Array.from(node.children).every( | ||
(child) => !hasText(child), | ||
); | ||
|
||
return nodeHasText && childrenDontHaveText; | ||
} | ||
|
||
describe('ErrorMessages', () => { | ||
it('should render an EmbargoErrorMessage', () => { | ||
const { getByText } = render(<EmbargoErrorMessage />); | ||
const errorMessage = getByText("We're sorry, this program is not available in your region."); | ||
expect(errorMessage).toBeInTheDocument(); | ||
}); | ||
|
||
it('should render a ProgramUnavailableMessage', () => { | ||
const { getByText, getByRole } = render(<ProgramUnavailableMessage />); | ||
const heading = getByText(getCustomTextContent.bind({ searchFor: 'Something went wrong, please reload the page. If the issue persists please contact support.' })); | ||
expect(heading).toBeInTheDocument(); | ||
|
||
const supportLink = getByRole('link', { name: /contact support/i }); | ||
expect(supportLink).toBeInTheDocument(); | ||
expect(supportLink.href).toBe('http://localhost:18000/support'); | ||
}); | ||
|
||
it('should render an IneligibleProgramErrorMessage', () => { | ||
const { getByText } = render(<IneligibleProgramErrorMessage />); | ||
const errorMessage = getByText("We're sorry, this program is no longer offering a subscription option. Please search our catalog for current availability."); | ||
expect(errorMessage).toBeInTheDocument(); | ||
}); | ||
|
||
it('should render an Unsuccessful3DSMessage', () => { | ||
const { getByText } = render(<Unsuccessful3DSMessage />); | ||
const errorMessage = getByText("We're sorry, the details you provided could not pass the 3D Secure check. Please try different payment details."); | ||
expect(errorMessage).toBeInTheDocument(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
import { runSaga } from 'redux-saga'; | ||
import { Factory } from 'rosie'; | ||
|
||
import { camelCaseObject } from '../../../payment/data/utils'; | ||
|
||
// Actions | ||
import { | ||
subscriptionDetailsReceived, | ||
subscriptionDetailsProcessing, | ||
fetchSubscriptionDetails, | ||
submitSubscription, | ||
} from './actions'; | ||
|
||
// Sagas | ||
import { clearMessages, addMessage, MESSAGE_TYPES } from '../../../feedback'; | ||
import { | ||
handleFetchSubscriptionDetails, | ||
handleSubmitSubscription, | ||
} from './sagas'; | ||
|
||
import { subscriptionDetailsInitialState } from './reducer'; | ||
import { subscriptionStatusReceived } from '../status/actions'; | ||
|
||
// Services | ||
import * as SubscriptionApiService from '../service'; | ||
import { subscriptionStripeCheckout } from '../../subscription-methods'; | ||
|
||
import { sendSubscriptionEvent } from '../utils'; | ||
|
||
import '../../__factories__/subscription.factory'; | ||
import '../../__factories__/subscriptionStatus.factory'; | ||
|
||
// Mocking service | ||
jest.mock('../service', () => ({ | ||
getDetails: jest.fn(), | ||
postDetails: jest.fn(), | ||
})); | ||
|
||
// Mocking stripe service | ||
jest.mock('../../subscription-methods', () => ({ | ||
subscriptionStripeCheckout: jest.fn(), | ||
})); | ||
|
||
// Mock the logError function | ||
jest.mock('@edx/frontend-platform/logging', () => ({ | ||
logError: jest.fn(), | ||
})); | ||
|
||
// // Mock the logError function | ||
jest.mock('@edx/frontend-platform/analytics', () => ({ | ||
sendTrackEvent: jest.fn(), | ||
})); | ||
|
||
// Mock the utils | ||
jest.mock('../utils', () => ({ | ||
sendSubscriptionEvent: jest.fn(), | ||
handleCustomErrors: jest.fn(), | ||
})); | ||
|
||
describe('details saga', () => { | ||
const details = camelCaseObject(Factory.build('subscription', { is_trial_eligible: true, status: 'succeeded' }, { numProducts: 2 })); | ||
const status = camelCaseObject(Factory.build('subscriptionStatus')); | ||
let dispatched; | ||
let fakeStore; | ||
beforeEach(() => { | ||
dispatched = []; | ||
SubscriptionApiService.getDetails.mockReset(); | ||
SubscriptionApiService.postDetails.mockReset(); | ||
sendSubscriptionEvent.mockReset(); | ||
// Used to reset the dispatch and onError handlers for runSaga. | ||
fakeStore = { | ||
getState: () => ({ | ||
subscription: { | ||
details, | ||
status, | ||
}, | ||
}), | ||
dispatch: action => dispatched.push(action), | ||
}; | ||
}); | ||
|
||
it('should successfully handleFetchSubscriptionDetails', async () => { | ||
// Mock the getDetails function | ||
SubscriptionApiService.getDetails.mockResolvedValue(details); | ||
|
||
await runSaga( | ||
{ | ||
...fakeStore, | ||
getState: () => ({ | ||
subscription: { | ||
details: subscriptionDetailsInitialState, | ||
status, | ||
}, | ||
}), | ||
}, | ||
handleFetchSubscriptionDetails, | ||
{}, | ||
).done; | ||
expect(dispatched).toEqual([ | ||
// clearMessages(), | ||
subscriptionDetailsProcessing(true), | ||
subscriptionDetailsReceived(details), | ||
subscriptionDetailsProcessing(false), | ||
fetchSubscriptionDetails.fulfill(), | ||
// subscriptionStatusReceived(details), | ||
]); | ||
expect(SubscriptionApiService.getDetails.mock.calls.length).toBe(1); | ||
}); | ||
|
||
it('should handle handleFetchSubscriptionDetails errors', async () => { | ||
// Mock the getDetails error state | ||
SubscriptionApiService.getDetails.mockRejectedValue(new Error('Api error')); | ||
|
||
await runSaga( | ||
{ | ||
...fakeStore, | ||
getState: () => ({ | ||
subscription: { | ||
details: subscriptionDetailsInitialState, | ||
status, | ||
}, | ||
}), | ||
}, | ||
handleFetchSubscriptionDetails, | ||
{}, | ||
).done; | ||
expect(dispatched).toEqual([ | ||
subscriptionDetailsProcessing(true), | ||
clearMessages(), | ||
addMessage('fallback-error', null, {}, MESSAGE_TYPES.ERROR), | ||
subscriptionDetailsProcessing(false), | ||
fetchSubscriptionDetails.fulfill(), | ||
]); | ||
expect(SubscriptionApiService.getDetails.mock.calls.length).toBe(1); | ||
}); | ||
|
||
it('should successfully post subscription details', async () => { | ||
const formData = { // dummy form data | ||
address: 'some dummy address', | ||
firstName: 'John', | ||
lastName: 'Smith', | ||
country: 'US', | ||
}; | ||
|
||
const postData = { // saga payload data | ||
payload: { | ||
method: 'stripe', | ||
...formData, | ||
}, | ||
}; | ||
|
||
const stripeServiceResult = { // stripe service result | ||
payment_method_id: status.paymentMethodId, | ||
program_uuid: details.programUuid, | ||
program_title: details.programTitle, | ||
billing_details: formData, | ||
}; | ||
|
||
const result = { // api result | ||
status: 'succeeded', | ||
subscriptionId: status.subscriptionId, | ||
}; | ||
|
||
// Mocking the resolve value | ||
subscriptionStripeCheckout.mockResolvedValue(stripeServiceResult); | ||
SubscriptionApiService.postDetails.mockResolvedValue(result); | ||
|
||
await runSaga( | ||
fakeStore, | ||
handleSubmitSubscription, | ||
postData, | ||
).toPromise(); | ||
expect(dispatched).toEqual([ | ||
subscriptionDetailsProcessing(true), | ||
clearMessages(), | ||
submitSubscription.request(), | ||
submitSubscription.success(result), | ||
subscriptionStatusReceived({ | ||
...result, | ||
paymentMethodId: stripeServiceResult.payment_method_id, | ||
}), | ||
subscriptionDetailsProcessing(false), | ||
]); | ||
expect(SubscriptionApiService.postDetails.mock.calls.length).toBe(1); | ||
expect(sendSubscriptionEvent.mock.calls.length).toBe(1); | ||
|
||
// send successful event | ||
expect(sendSubscriptionEvent.mock.calls[0][0]).toStrictEqual({ details, success: true }); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.