react-native-navigation
Version:
React Native Navigation - truly native navigation for iOS and Android
333 lines (331 loc) • 11.3 kB
JavaScript
;
import * as React from 'react';
import { View, Text } from 'react-native';
import { render, act } from '@testing-library/react-native';
import { ComponentWrapper } from "./ComponentWrapper.js";
import { Store } from "./Store.js";
import { mock, verify, instance } from 'ts-mockito';
import { ComponentEventsObserver } from "../events/ComponentEventsObserver.js";
import { jsx as _jsx } from "react/jsx-runtime";
describe('ComponentWrapper', () => {
const componentName = 'example.MyComponent';
let store;
let myComponentProps;
let mockedComponentEventsObserver;
let componentEventsObserver;
let uut;
class MyComponent extends React.Component {
static options = {
title: 'MyComponentTitle'
};
render() {
myComponentProps = this.props;
if (this.props.renderCount) {
this.props.renderCount();
}
return /*#__PURE__*/_jsx(Text, {
children: this.props.text || 'Hello, World!'
});
}
}
class TestParent extends React.Component {
constructor(props) {
super(props);
this.ChildClass = props.ChildClass;
this.state = {
propsFromState: {}
};
}
setStateFromTest = newState => {
this.setState({
propsFromState: newState
});
};
render() {
const {
ChildClass
} = this;
return /*#__PURE__*/_jsx(ChildClass, {
componentId: "component1",
...this.state.propsFromState
});
}
}
beforeEach(() => {
store = new Store();
mockedComponentEventsObserver = mock(ComponentEventsObserver);
componentEventsObserver = instance(mockedComponentEventsObserver);
uut = new ComponentWrapper();
});
it('must have componentId as prop', () => {
const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);
const orig = console.error;
console.error = a => a;
expect(() => {
render(/*#__PURE__*/_jsx(NavigationComponent, {}));
}).toThrowError('Component example.MyComponent does not have a componentId!');
console.error = orig;
});
it('wraps the component', () => {
const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);
expect(NavigationComponent).not.toBeInstanceOf(MyComponent);
const {
getByText
} = render(/*#__PURE__*/_jsx(NavigationComponent, {
componentId: 'component1'
}));
expect(getByText('Hello, World!')).toBeTruthy();
});
it('injects props from wrapper into original component', () => {
const renderCount = jest.fn();
const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);
const {
getByText
} = render(/*#__PURE__*/_jsx(NavigationComponent, {
componentId: 'component1',
text: 'yo',
renderCount: renderCount
}));
expect(getByText('yo')).toBeTruthy();
expect(renderCount).toHaveBeenCalledTimes(1);
});
it('updates props from wrapper into original component on state change', () => {
const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);
let parentRef = null;
render(/*#__PURE__*/_jsx(TestParent, {
ref: ref => {
parentRef = ref;
},
ChildClass: NavigationComponent
}));
expect(myComponentProps.foo).toEqual(undefined);
// Use the ref to call setState wrapped in act
act(() => {
if (parentRef) {
parentRef.setStateFromTest({
foo: 'yo'
});
}
});
expect(myComponentProps.foo).toEqual('yo');
});
it('pulls props from the store and injects them into the inner component', () => {
store.updateProps('component123', {
numberProp: 1,
stringProp: 'hello',
objectProp: {
a: 2
}
});
const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);
render(/*#__PURE__*/_jsx(NavigationComponent, {
componentId: 'component123'
}));
expect(myComponentProps).toEqual({
componentId: 'component123',
componentName,
numberProp: 1,
stringProp: 'hello',
objectProp: {
a: 2
}
});
});
it('update props with callback', done => {
const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);
render(/*#__PURE__*/_jsx(NavigationComponent, {
componentId: 'component123'
}));
function callback() {
try {
expect(true).toBe(true);
done();
} catch (error) {
done(error);
}
}
store.updateProps('component123', {
someProp: 'someValue'
}, callback);
});
it('updates props from store into inner component', () => {
const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);
render(/*#__PURE__*/_jsx(TestParent, {
ChildClass: NavigationComponent
}));
expect(myComponentProps.myProp).toEqual(undefined);
act(() => {
store.updateProps('component1', {
myProp: 'hello'
});
});
expect(myComponentProps.myProp).toEqual('hello');
});
it('updates props from state into inner component', () => {
const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);
let parentRef = null;
render(/*#__PURE__*/_jsx(TestParent, {
ref: ref => {
parentRef = ref;
},
ChildClass: NavigationComponent
}));
expect(myComponentProps.foo).toEqual(undefined);
act(() => {
if (parentRef) {
parentRef.setStateFromTest({
foo: 'yo'
});
}
});
expect(myComponentProps.foo).toEqual('yo');
});
it('protects id from change', () => {
const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);
let parentRef = null;
render(/*#__PURE__*/_jsx(TestParent, {
ref: ref => {
parentRef = ref;
},
ChildClass: NavigationComponent
}));
expect(myComponentProps.componentId).toEqual('component1');
if (parentRef) {
parentRef.setStateFromTest({
id: 'ERROR'
});
}
expect(myComponentProps.componentId).toEqual('component1');
});
it('cleans props from store on unMount', () => {
store.updateProps('component123', {
foo: 'bar'
});
const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);
const {
unmount
} = render(/*#__PURE__*/_jsx(NavigationComponent, {
componentId: 'component123'
}));
expect(store.getPropsForId('component123')).toEqual({
foo: 'bar'
});
unmount();
expect(store.getPropsForId('component123')).toEqual({});
});
it('merges static members from wrapped component when generated', () => {
const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);
expect(NavigationComponent.options).toEqual({
title: 'MyComponentTitle'
});
});
it('calls unmounted on componentEventsObserver', () => {
const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);
const {
unmount
} = render(/*#__PURE__*/_jsx(NavigationComponent, {
componentId: 'component123'
}));
verify(mockedComponentEventsObserver.unmounted('component123')).never();
unmount();
verify(mockedComponentEventsObserver.unmounted('component123')).once();
});
it('renders HOC components correctly', () => {
const generator = () => props => /*#__PURE__*/_jsx(View, {
children: /*#__PURE__*/_jsx(MyComponent, {
...props
})
});
uut = new ComponentWrapper();
const NavigationComponent = uut.wrap(componentName, generator, store, componentEventsObserver);
const {
getByText
} = render(/*#__PURE__*/_jsx(NavigationComponent, {
componentId: 'component123'
}));
expect(getByText('Hello, World!')).toBeTruthy();
});
it('sets component instance in store when constructed', () => {
const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);
render(/*#__PURE__*/_jsx(NavigationComponent, {
componentId: 'component1'
}));
expect(store.getComponentInstance('component1')).toBeTruthy();
});
it('Component generator is invoked only once', () => {
const componentGenerator = jest.fn(() => MyComponent);
uut.wrap(componentName, componentGenerator, store, componentEventsObserver);
expect(componentGenerator.mock.calls.length).toBe(1);
});
describe(`register with redux store`, () => {
class MyReduxComp extends React.Component {
static options() {
return {
foo: 123
};
}
render() {
return /*#__PURE__*/_jsx(Text, {
children: this.props.txt
});
}
}
function mapStateToProps(state) {
return {
txt: state.txt
};
}
const ConnectedComp = require('react-redux').connect(mapStateToProps)(MyReduxComp);
const ReduxProvider = require('react-redux').Provider;
const initialState = {
txt: 'it just works'
};
const reduxStore = {
getState: () => initialState,
dispatch: jest.fn(),
subscribe: jest.fn(),
replaceReducer: jest.fn()
};
it(`wraps the component with a react-redux provider with passed store`, () => {
const NavigationComponent = uut.wrap(componentName, () => ConnectedComp, store, componentEventsObserver, undefined, ReduxProvider, reduxStore);
const {
getByText
} = render(/*#__PURE__*/_jsx(ReduxProvider, {
store: reduxStore,
children: /*#__PURE__*/_jsx(NavigationComponent, {
componentId: 'theCompId'
})
}));
expect(getByText('it just works')).toBeTruthy();
expect(NavigationComponent.options()).toEqual({
foo: 123
});
});
});
it('initialize isMounted with false value', () => {
const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);
new NavigationComponent({
componentId: 'component123'
});
expect(store.getComponentInstance('component123')?.isMounted).toEqual(false);
});
it('updates isMounted on componentDidMount', () => {
const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);
render(/*#__PURE__*/_jsx(NavigationComponent, {
componentId: 'component123'
}));
expect(store.getComponentInstance('component123')?.isMounted).toEqual(true);
});
it('clears isMounted on componentDidUnmount', () => {
const NavigationComponent = uut.wrap(componentName, () => MyComponent, store, componentEventsObserver);
const {
unmount
} = render(/*#__PURE__*/_jsx(NavigationComponent, {
componentId: 'component123'
}));
expect(store.getComponentInstance('component123')?.isMounted).toEqual(true);
unmount();
expect(store.getComponentInstance('component123')?.isMounted).toBeUndefined();
});
});
//# sourceMappingURL=ComponentWrapper.test.js.map