react-stripe-elements
Version:
React components for Stripe.js and Stripe Elements
631 lines (526 loc) • 19 kB
JavaScript
// @noflow
import React from 'react';
import {mount, shallow} from 'enzyme';
import injectStripe from './inject';
describe('injectStripe()', () => {
let WrappedComponent;
let context;
let createSource;
let createToken;
let createPaymentMethod;
let handleCardPayment;
let handleCardSetup;
let elementMock;
let rawElementMock;
let elementsMock;
// Before ALL tests (sync or async)
beforeEach(() => {
createSource = jest.fn();
createToken = jest.fn();
createPaymentMethod = jest.fn();
handleCardPayment = jest.fn();
handleCardSetup = jest.fn();
elementsMock = {};
rawElementMock = {
_frame: {
id: 'id',
},
_componentName: 'name',
};
elementMock = {
element: {
on: jest.fn(),
},
impliedTokenType: 'card',
impliedSourceType: 'card',
impliedPaymentMethodType: 'card',
};
WrappedComponent = () => <div />;
WrappedComponent.displayName = 'WrappedComponent';
});
describe('[sync]', () => {
// Before ONLY sync tests
beforeEach(() => {
context = {
tag: 'sync',
stripe: {
elements: jest.fn(),
createSource,
createToken,
createPaymentMethod,
handleCardPayment,
handleCardSetup,
},
elements: elementsMock,
getRegisteredElements: () => [elementMock],
};
});
it('sets the correct displayName', () => {
expect(injectStripe(WrappedComponent).displayName).toBe(
'InjectStripe(WrappedComponent)'
);
});
it("includes the original component's displayName", () => {
WrappedComponent.displayName = 'foo';
expect(injectStripe(WrappedComponent).displayName).toBe(
'InjectStripe(foo)'
);
});
it("falls back to the original component's name if no displayName is set", () => {
WrappedComponent.displayName = undefined;
expect(injectStripe(WrappedComponent).displayName).toBe(
`InjectStripe(${WrappedComponent.name})`
);
});
it('throws when StripeProvider is missing from ancestry', () => {
// Prevent the expected console.error from react to keep the test output clean
const originalConsoleError = global.console.error;
global.console.error = (msg) => {
if (
!msg.startsWith(
'Warning: Failed context type: The context `tag` is marked as required'
) &&
!msg.startsWith(
'Warning: Failed context type: The context `getRegisteredElements` is marked as required'
)
) {
originalConsoleError(msg);
}
};
const Injected = injectStripe(WrappedComponent());
expect(() => shallow(<Injected />)).toThrow(
/It looks like you are trying to inject Stripe context outside of an Elements context/
);
global.console.error = originalConsoleError;
});
it('renders <WrappedComponent> with `stripe` prop', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
expect(props).toHaveProperty('stripe');
expect(props).toHaveProperty('stripe.createSource');
expect(props).toHaveProperty('stripe.createToken');
expect(props).toHaveProperty('stripe.createPaymentMethod');
expect(props).toHaveProperty('stripe.handleCardPayment');
});
it('renders <WrappedComponent> with `elements` prop', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
expect(props.elements).toBe(elementsMock);
});
it('props.stripe.createToken calls createToken with element and empty options when called with no arguments', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
props.stripe.createToken();
expect(createToken).toHaveBeenCalledWith(elementMock.element, {});
});
it('props.stripe.createToken calls createToken with element and options when called with options object', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
props.stripe.createToken({foo: 'bar'});
expect(createToken).toHaveBeenCalledWith(elementMock.element, {
foo: 'bar',
});
});
it('props.stripe.createToken calls createToken with string as first argument and options object as second', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
props.stripe.createToken('test', {foo: 'bar'});
expect(createToken).toHaveBeenCalledWith('test', {foo: 'bar'});
});
it('props.stripe.createToken throws when called with invalid options type', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
expect(() => props.stripe.createToken(1)).toThrow(
'Invalid options passed to createToken. Expected an object, got number.'
);
});
it('props.stripe.createToken throws when no element is in the tree', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context: {
...context,
getRegisteredElements: () => [],
},
});
const props = wrapper.props();
expect(props.stripe.createToken).toThrow(
/We could not infer which Element you want to use for this operation./
);
});
it('props.stripe.createSource errors when called without a type', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
expect(props.stripe.createSource).toThrow(/Invalid Source type/);
});
it('props.stripe.createSource calls createSource with element and type when only type is passed in', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
props.stripe.createSource({type: 'card'});
expect(createSource).toHaveBeenCalledWith(elementMock.element, {
type: 'card',
});
});
it('props.stripe.createSource calls createSource with options', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
props.stripe.createSource({type: 'card', foo: 'bar'});
expect(createSource).toHaveBeenCalledWith(elementMock.element, {
type: 'card',
foo: 'bar',
});
});
it('props.stripe.createSource calls createSource with options when called with unknown type', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
props.stripe.createSource({type: 'baz', foo: 'bar'});
expect(createSource).toHaveBeenCalledWith({type: 'baz', foo: 'bar'});
});
it('props.stripe.createSource throws when called with invalid options argument', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
expect(() => props.stripe.createSource(1)).toThrow(
'Invalid options passed to createSource. Expected an object, got number.'
);
});
it('props.stripe.createSource throws when called with source type that matches multiple elements', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context: {
...context,
getRegisteredElements: () => [
{
element: {
on: jest.fn(),
},
impliedTokenType: 'card',
impliedSourceType: 'card',
},
{
element: {
on: jest.fn(),
},
impliedTokenType: 'card',
impliedSourceType: 'card',
},
],
},
});
const props = wrapper.props();
expect(() => props.stripe.createSource({type: 'card'})).toThrow(
/We could not infer which Element you want to use for this operation/
);
});
it('props.stripe.createPaymentMethod calls createPaymentMethod with element and type when only type is passed in', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
props.stripe.createPaymentMethod('card');
expect(createPaymentMethod).toHaveBeenCalledWith(
'card',
elementMock.element
);
});
it('props.stripe.createPaymentMethod calls createPaymentMethod with data options', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
props.stripe.createPaymentMethod('card', {
billing_details: {
name: 'Jenny Rosen',
},
});
expect(createPaymentMethod).toHaveBeenCalledWith(
'card',
elementMock.element,
{
billing_details: {
name: 'Jenny Rosen',
},
}
);
});
it('props.stripe.createPaymentMethod calls createPaymentMethod with element from arguments when passed in', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
props.stripe.createPaymentMethod('card', rawElementMock);
expect(createPaymentMethod).toHaveBeenCalledWith('card', rawElementMock);
});
it('props.stripe.createPaymentMethod calls createPaymentMethod with element and options from arguments when passed in', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
props.stripe.createPaymentMethod('card', rawElementMock, {
billing_details: {
name: 'Jenny Rosen',
},
});
expect(createPaymentMethod).toHaveBeenCalledWith('card', rawElementMock, {
billing_details: {
name: 'Jenny Rosen',
},
});
});
it('props.stripe.handleCardPayment calls handleCardPayment with element and clientSecret when only clientSecret is passed in', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
props.stripe.handleCardPayment('clientSecret');
expect(handleCardPayment).toHaveBeenCalledWith(
'clientSecret',
elementMock.element
);
});
it('props.stripe.handleCardPayment calls handleCardPayment with data options', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
props.stripe.handleCardPayment('clientSecret', {
billing_details: {
name: 'Jenny Rosen',
},
});
expect(handleCardPayment).toHaveBeenCalledWith(
'clientSecret',
elementMock.element,
{
billing_details: {
name: 'Jenny Rosen',
},
}
);
});
it('props.stripe.handleCardPayment calls handleCardPayment with element from arguments when passed in', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
props.stripe.handleCardPayment('clientSecret', rawElementMock);
expect(handleCardPayment).toHaveBeenCalledWith(
'clientSecret',
rawElementMock
);
});
it('props.stripe.handleCardPayment calls handleCardPayment with element and data from arguments when passed in', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
props.stripe.handleCardPayment('clientSecret', rawElementMock, {
billing_details: {
name: 'Jenny Rosen',
},
});
expect(handleCardPayment).toHaveBeenCalledWith(
'clientSecret',
rawElementMock,
{
billing_details: {
name: 'Jenny Rosen',
},
}
);
});
it('props.stripe.handleCardPayment calls handleCardPayment with only the clientSecret when no element is present', () => {
context.getRegisteredElements = () => [];
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
props.stripe.handleCardPayment('clientSecret');
expect(handleCardPayment).toHaveBeenCalledWith('clientSecret');
});
it('props.stripe.handleCardSetup calls handleCardSetup with element and clientSecret when only clientSecret is passed in', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
props.stripe.handleCardSetup('clientSecret');
expect(handleCardSetup).toHaveBeenCalledWith(
'clientSecret',
elementMock.element
);
});
it('props.stripe.handleCardSetup calls handleCardSetup with data options', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
props.stripe.handleCardSetup('clientSecret', {
billing_details: {
name: 'Jenny Rosen',
},
});
expect(handleCardSetup).toHaveBeenCalledWith(
'clientSecret',
elementMock.element,
{
billing_details: {
name: 'Jenny Rosen',
},
}
);
});
it('props.stripe.handleCardSetup calls handleCardSetup with element from arguments when passed in', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
props.stripe.handleCardSetup('clientSecret', rawElementMock);
expect(handleCardSetup).toHaveBeenCalledWith(
'clientSecret',
rawElementMock
);
});
it('props.stripe.handleCardSetup calls handleCardSetup with element and data from arguments when passed in', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
props.stripe.handleCardSetup('clientSecret', rawElementMock, {
billing_details: {
name: 'Jenny Rosen',
},
});
expect(handleCardSetup).toHaveBeenCalledWith(
'clientSecret',
rawElementMock,
{
billing_details: {
name: 'Jenny Rosen',
},
}
);
});
it('props.stripe.handleCardSetup calls handleCardSetup with only the clientSecret when no element is present', () => {
context.getRegisteredElements = () => [];
const Injected = injectStripe(WrappedComponent);
const wrapper = shallow(<Injected />, {
context,
});
const props = wrapper.props();
props.stripe.handleCardSetup('clientSecret');
expect(handleCardSetup).toHaveBeenCalledWith('clientSecret');
});
it('throws when `getWrappedInstance` is called without `{withRef: true}` option.', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = mount(<Injected />, {
context,
});
expect(() => wrapper.instance().getWrappedInstance()).toThrow(
'To access the wrapped instance, the `{withRef: true}` option must be set when calling `injectStripe()`'
);
});
it('`getWrappedInstance` works whith `{withRef: true}` option.', () => {
// refs won't work with stateless functional components
class WrappedClassComponent extends React.Component {
static displayName = 'WrappedClassComponent';
foo: 'bar';
render() {
return <div>{this.foo}</div>;
}
}
const Injected = injectStripe(WrappedClassComponent, {withRef: true});
const wrapper = mount(<Injected />, {
context,
});
expect(
wrapper.instance().getWrappedInstance() instanceof WrappedClassComponent
).toBe(true);
});
});
describe('[async]', () => {
it('props.stripe is null if addStripeLoadListener never fires', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = mount(<Injected />, {
context: {
tag: 'async',
// simulate StripeProvider never giving us a StripeShape
addStripeLoadListener: () => {},
getRegisteredElements: () => [elementMock],
},
});
const props = wrapper.find(WrappedComponent).props();
expect(props).toHaveProperty('stripe', null);
});
it('props.stripe is set when addStripeLoadListener fires', () => {
const Injected = injectStripe(WrappedComponent);
const wrapper = mount(<Injected />, {
context: {
tag: 'async',
// simulate StripeProvider eventually giving us a StripeShape
addStripeLoadListener: (fn) => {
fn({
elements: jest.fn(),
createSource,
createToken,
createPaymentMethod,
handleCardPayment,
handleCardSetup,
});
},
getRegisteredElements: () => [elementMock],
},
});
const props = wrapper.find(WrappedComponent).props();
expect(props).toHaveProperty('stripe');
expect(props).toHaveProperty('stripe.createToken');
expect(props).toHaveProperty('stripe.createSource');
expect(props).toHaveProperty('stripe.createPaymentMethod');
expect(props).toHaveProperty('stripe.handleCardPayment');
expect(props).toHaveProperty('stripe.handleCardSetup');
});
});
});