Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(enzyme-3): Fix tests using Enzyme lifecycles #2

Merged
merged 2 commits into from
Oct 31, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
131 changes: 64 additions & 67 deletions src/__tests__/reactNativeAbHoc.test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React from 'react';
import { shallow } from 'enzyme';
import { View, Text, AsyncStorage } from 'react-native';
import { shallow } from 'enzyme';
import ReactNativeAbHoc from './../reactNativeAbHoc';

const getWrapper = (Component, props = {}) => shallow(<Component {...props} />);

/** @test {ReactNativeAbHoc} */
describe('ReactNativeAbHoc', () => {
let A;
Expand All @@ -11,10 +13,23 @@ describe('ReactNativeAbHoc', () => {
let Component;

beforeEach(() => {
const setItem = () => Promise.resolve('setItem');
const getItem = () => Promise.resolve('A');
jest.setMock('AsyncStorage', { setItem: jest.fn(setItem), getItem: jest.fn(getItem) });
const asyncMap = {};
const getItem = key => Promise.resolve(asyncMap[key]);
const setItem = (key, variant) => {
asyncMap[key] = variant;
Promise.resolve(variant);
};

AsyncStorage.setItem = jest.fn(setItem);
AsyncStorage.getItem = jest.fn(getItem);
});

afterEach(() => {
AsyncStorage.setItem.mockReset();
AsyncStorage.getItem.mockReset();
});

beforeEach(() => {
A = () => (
<View>
<Text>A</Text>
Expand All @@ -39,87 +54,69 @@ describe('ReactNativeAbHoc', () => {
);
});

describe('componentWillMount', () => {
it('should have called the persistVariant with the A variant', () => {
const wrapper = shallow(<Component variant="A" />);
const instance = wrapper.instance();
instance.persistVariant = jest.fn();
instance.componentWillMount();
expect(instance.persistVariant).toHaveBeenCalledWith({ variant: 'A', component: A });
describe('ReactNativeAbHoc#findVariant', () => {
it('should have found the variant', () => {
expect(Component.findVariant('A')).toEqual({ variant: 'A', component: A });
});

it('should have thrown an error if the variant variant doesnt exist', () => {
expect(() => shallow(<Component variant="X" />)).toThrow(
'The variant named "X" doesn\'t exist in the current experient "Experiment',
it('should have throw an error if the variant is not found', () => {
expect(() => Component.findVariant('x')).toThrow(
'The variant named "x" doesn\'t exist in the current experiment "Experiment"',
);
});
});

it('should have returned the second component', () => {
Math.random = jest.fn().mockImplementation(() => 0.4);
const wrapper = shallow(<Component />);
const instance = wrapper.instance();
instance.shouldPersist = jest.fn();
instance.componentWillMount();
expect(instance.shouldPersist).toHaveBeenCalledWith({ variant: 'B', component: B });
describe('ReactNativeAbHoc#randomizeVariant', () => {
it('should return component A', () => {
Math.random = jest.fn().mockImplementation(() => 0.2);
expect(Component.randomizeVariant()).toEqual({ variant: 'A', component: A });
});

it('should have returned the first component', () => {
Math.random = jest.fn().mockImplementation(() => 0.1);
const wrapper = shallow(<Component />);
const instance = wrapper.instance();
instance.shouldPersist = jest.fn();
instance.componentWillMount();
expect(instance.shouldPersist).toHaveBeenCalledWith({ variant: 'A', component: A });
it('should return component B', () => {
Math.random = jest.fn().mockImplementation(() => 0.5);
expect(Component.randomizeVariant()).toEqual({ variant: 'B', component: B });
});

it('should have returned the last component', () => {
Math.random = jest.fn().mockImplementation(() => 0.9);
const wrapper = shallow(<Component />);
const instance = wrapper.instance();
instance.shouldPersist = jest.fn();
instance.componentWillMount();
expect(instance.shouldPersist).toHaveBeenCalledWith({ variant: 'C', component: C });
it('should return component C', () => {
Math.random = jest.fn().mockImplementation(() => 0.8);
expect(Component.randomizeVariant()).toEqual({ variant: 'C', component: C });
});
});

describe('persistVariant', () => {
it('should have called the AsyncStorage setItem if the variant is forced ', async () => {
const instance = shallow(<Component variant="B" />).instance();
// Reset the mock called in shallow
AsyncStorage.setItem.mockClear();
await instance.persistVariant({ variant: 'B', component: B });
describe('ReactNativeAbHoc#lifecycle', () => {
it('should render a forced component', async () => {
const wrapper = await getWrapper(Component, { variant: 'B' });
expect(AsyncStorage.setItem).toHaveBeenCalledWith('abhoc-variant-Experiment', 'B');
expect(wrapper.state()).toEqual({ variant: 'B', component: B });
expect(wrapper.find(View).prop('variant')).toEqual('B');
});

it('should have called the component setState with "variant" and "B" ', async () => {
const instance = shallow(<Component variant="B" />).instance();
// Reset the mock called in shallow
AsyncStorage.setItem.mockClear();
instance.setState = jest.fn();
await instance.persistVariant({ variant: 'B', component: B });
expect(instance.setState).toHaveBeenCalledWith({ variant: 'B', component: B });
it('should render a random component', async (done) => {
Math.random = jest.fn().mockImplementation(() => 0.8);
const wrapper = await getWrapper(Component, { simpleProps: 'test' });
expect(AsyncStorage.getItem).toHaveBeenCalledWith('abhoc-variant-Experiment');
expect(AsyncStorage.setItem).toHaveBeenCalledWith('abhoc-variant-Experiment', 'C');
expect(wrapper.find(View).prop('simpleProps')).toEqual('test');
setTimeout(() => {
expect(wrapper.state()).toEqual({ variant: 'C', component: C });
done();
}, 0);
});
});

describe('shouldPersist', () => {
it('shouldnt have called the persist method if something as been obtained from the storage ', async () => {
Math.random = jest.fn().mockImplementation(() => 0.1);
AsyncStorage.setItem.mockClear();
const instance = shallow(<Component />).instance();
instance.persistVariant = jest.fn();
await instance.shouldPersist({ variant: 'B', component: B });
expect(instance.persistVariant).not.toHaveBeenCalled();
});
it('should render a random (existing) component', async (done) => {
Math.random = jest.fn().mockImplementation(() => 0.8);

AsyncStorage.setItem('abhoc-variant-Experiment', 'C');
AsyncStorage.setItem.mockReset();

it('should have called the persist method if nothing in the storage ', async () => {
Math.random = jest.fn().mockImplementation(() => 0.1);
AsyncStorage.setItem.mockClear();
const getItem = () => Promise.resolve(null);
AsyncStorage.getItem = getItem;
const instance = shallow(<Component />).instance();
instance.persistVariant = jest.fn();
await instance.shouldPersist({ variant: 'B', component: B });
expect(instance.persistVariant).toHaveBeenCalledWith({ variant: 'B', component: B });
const wrapper = await getWrapper(Component, { simpleProps: 'test' });
expect(AsyncStorage.getItem).toHaveBeenCalledWith('abhoc-variant-Experiment');
expect(AsyncStorage.setItem).not.toHaveBeenCalled();
expect(wrapper.find(View).prop('simpleProps')).toEqual('test');
setTimeout(() => {
expect(wrapper.state()).toEqual({ variant: 'C', component: C });
done();
}, 0);
});
});
});
7 changes: 4 additions & 3 deletions src/reactNativeAbHoc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import React from 'react';
import PropTypes from 'prop-types';
import { View, AsyncStorage } from 'react-native';

const LOCAL_STORAGE_KEY = 'abhoc-variant';
export const LOCAL_STORAGE_KEY = 'abhoc-variant';

export default (experiment, ...variants) => {
class AbHoc extends React.Component {
static findVariant(variant) {
const chosenVariant = variants.find(v => v.variant === variant);
if (!chosenVariant) {
throw new Error(
`The variant named "${variant}" doesn't exist in the current experient "${experiment}"`,
`The variant named "${variant}" doesn't exist in the current experiment "${experiment}"`,
);
}
return chosenVariant;
Expand All @@ -27,7 +27,7 @@ export default (experiment, ...variants) => {
this.state = { variant: null, component: View };
}

componentWillMount() {
async componentDidMount() {
const { variant } = this.props;
if (variant) {
const chosenVariant = AbHoc.findVariant(variant);
Expand All @@ -44,6 +44,7 @@ export default (experiment, ...variants) => {

async shouldPersist({ variant, component }) {
const localVariantKey = await AsyncStorage.getItem(`${LOCAL_STORAGE_KEY}-${experiment}`);

if (!localVariantKey) {
return this.persistVariant({ variant, component });
}
Expand Down
48 changes: 43 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1352,7 +1352,7 @@ crc@3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/crc/-/crc-3.3.0.tgz#fa622e1bc388bf257309082d6b65200ce67090ba"

create-react-class@^15.5.2:
create-react-class@^15.5.2, create-react-class@^15.6.2:
version "15.6.2"
resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.2.tgz#cf1ed15f12aad7f14ef5f2dfe05e6c42f91ef02a"
dependencies:
Expand Down Expand Up @@ -1414,6 +1414,10 @@ csurf@~1.8.3:
csrf "~3.0.0"
http-errors "~1.3.1"

cubic-bezier@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/cubic-bezier/-/cubic-bezier-0.1.2.tgz#d4970942002e45372e0aa92db657e39eaf6824d7"

damerau-levenshtein@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz#03191c432cb6eea168bb77f3a55ffdccb8978514"
Expand Down Expand Up @@ -2503,7 +2507,7 @@ inquirer@^3.0.6:
strip-ansi "^4.0.0"
through "^2.3.6"

invariant@^2.2.0:
invariant@^2.2.0, invariant@^2.2.1:
version "2.2.2"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
dependencies:
Expand Down Expand Up @@ -3116,6 +3120,10 @@ jsx-ast-utils@^1.4.0, jsx-ast-utils@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1efb182c0df1"

keymirror@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/keymirror/-/keymirror-0.1.1.tgz#918889ea13f8d0a42e7c557250eee713adc95c35"

kind-of@^3.0.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
Expand Down Expand Up @@ -4082,7 +4090,7 @@ prop-types@^15.5.10:
fbjs "^0.8.9"
loose-envify "^1.3.1"

prop-types@^15.5.6, prop-types@^15.5.8:
prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.6.0:
version "15.6.0"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856"
dependencies:
Expand Down Expand Up @@ -4118,7 +4126,7 @@ quick-lru@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8"

raf@^3.3.2:
raf@^3.2.0, raf@^3.3.2:
version "3.4.0"
resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.0.tgz#a28876881b4bc2ca9117d4138163ddb80f781575"
dependencies:
Expand Down Expand Up @@ -4191,6 +4199,30 @@ react-dom@16.0.0-beta.5:
object-assign "^4.1.0"
prop-types "^15.5.6"

react-dom@^16.0.0:
version "16.0.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.0.0.tgz#9cc3079c3dcd70d4c6e01b84aab2a7e34c303f58"
dependencies:
fbjs "^0.8.16"
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.0"

react-native-mock-render@^0.0.13:
version "0.0.13"
resolved "https://registry.yarnpkg.com/react-native-mock-render/-/react-native-mock-render-0.0.13.tgz#fb9b69ba1df0f333139329459b423939b020a318"
dependencies:
create-react-class "^15.6.2"
cubic-bezier "^0.1.2"
invariant "^2.2.1"
keymirror "^0.1.1"
lodash "^4.15.0"
prop-types "^15.6.0"
raf "^3.2.0"
react-dom "^16.0.0"
react-timer-mixin "^0.13.3"
warning "^2.1.0"

react-native@0.49.3:
version "0.49.3"
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.49.3.tgz#0ca459ee49f9c59e8326b2ced9e34c59333a2f26"
Expand Down Expand Up @@ -4268,7 +4300,7 @@ react-test-renderer@^16.0.0-0:
fbjs "^0.8.16"
object-assign "^4.1.1"

react-timer-mixin@^0.13.2:
react-timer-mixin@^0.13.2, react-timer-mixin@^0.13.3:
version "0.13.3"
resolved "https://registry.yarnpkg.com/react-timer-mixin/-/react-timer-mixin-0.13.3.tgz#0da8b9f807ec07dc3e854d082c737c65605b3d22"

Expand Down Expand Up @@ -5150,6 +5182,12 @@ walker@~1.0.5:
dependencies:
makeerror "1.0.x"

warning@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/warning/-/warning-2.1.0.tgz#21220d9c63afc77a8c92111e011af705ce0c6901"
dependencies:
loose-envify "^1.0.0"

watch@~0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/watch/-/watch-0.18.0.tgz#28095476c6df7c90c963138990c0a5423eb4b986"
Expand Down