import * as React from 'react'; import { Text } from 'react-native'; import * as renderer from 'react-test-renderer'; import { ComponentWrapper } from './ComponentWrapper'; import { Store } from './Store'; import { mock, verify, instance } from 'ts-mockito'; import { ComponentEventsObserver } from '../events/ComponentEventsObserver'; describe('ComponentWrapper', () => { const componentName = 'example.MyComponent'; let store; let myComponentProps; let mockedComponentEventsObserver: ComponentEventsObserver; let componentEventsObserver: ComponentEventsObserver; class MyComponent extends React.Component { static options = { title: 'MyComponentTitle' }; render() { myComponentProps = this.props; if (this.props.renderCount) { this.props.renderCount(); } return {this.props.text || 'Hello, World!'}; } } class TestParent extends React.Component { private ChildClass; constructor(props) { super(props); this.ChildClass = props.ChildClass; this.state = { propsFromState: {} }; } render() { return ( ); } } beforeEach(() => { store = new Store(); mockedComponentEventsObserver = mock(ComponentEventsObserver); componentEventsObserver = instance(mockedComponentEventsObserver); }); it('must have componentId as prop', () => { const NavigationComponent = ComponentWrapper.wrap(componentName, () => MyComponent, store, componentEventsObserver); const orig = console.error; console.error = (a) => a; expect(() => { renderer.create(); }).toThrowError('Component example.MyComponent does not have a componentId!'); console.error = orig; }); it('wraps the component', () => { const NavigationComponent = ComponentWrapper.wrap(componentName, () => MyComponent, store, componentEventsObserver); expect(NavigationComponent).not.toBeInstanceOf(MyComponent); const tree = renderer.create(); expect(tree.toJSON()!.children).toEqual(['Hello, World!']); }); it('injects props from wrapper into original component', () => { const renderCount = jest.fn(); const NavigationComponent = ComponentWrapper.wrap(componentName, () => MyComponent, store, componentEventsObserver); const tree = renderer.create(); expect(tree.toJSON()!.children).toEqual(['yo']); expect(renderCount).toHaveBeenCalledTimes(1); }); it('updates props from wrapper into original component on state change', () => { const NavigationComponent = ComponentWrapper.wrap(componentName, () => MyComponent, store, componentEventsObserver); const tree = renderer.create(); expect(myComponentProps.foo).toEqual(undefined); (tree.getInstance() as any).setState({ propsFromState: { foo: 'yo' } }); expect(myComponentProps.foo).toEqual('yo'); }); it('pulls props from the store and injects them into the inner component', () => { store.setPropsForId('component123', { numberProp: 1, stringProp: 'hello', objectProp: { a: 2 } }); const NavigationComponent = ComponentWrapper.wrap(componentName, () => MyComponent, store, componentEventsObserver); renderer.create(); expect(myComponentProps).toEqual({ componentId: 'component123', numberProp: 1, stringProp: 'hello', objectProp: { a: 2 } }); }); it('updates props from store into inner component', () => { const NavigationComponent = ComponentWrapper.wrap(componentName, () => MyComponent, store, componentEventsObserver); const tree = renderer.create(); store.setPropsForId('component1', { myProp: 'hello' }); expect(myComponentProps.foo).toEqual(undefined); expect(myComponentProps.myProp).toEqual(undefined); (tree.getInstance() as any).setState({ propsFromState: { foo: 'yo' } }); expect(myComponentProps.foo).toEqual('yo'); expect(myComponentProps.myProp).toEqual('hello'); }); it('protects id from change', () => { const NavigationComponent = ComponentWrapper.wrap(componentName, () => MyComponent, store, componentEventsObserver); const tree = renderer.create(); expect(myComponentProps.componentId).toEqual('component1'); (tree.getInstance() as any).setState({ propsFromState: { id: 'ERROR' } }); expect(myComponentProps.componentId).toEqual('component1'); }); it('assignes key by id', () => { const NavigationComponent = ComponentWrapper.wrap(componentName, () => MyComponent, store, componentEventsObserver); const tree = renderer.create(); expect(myComponentProps.componentId).toEqual('component1'); expect((tree.getInstance() as any)._reactInternalInstance.child.key).toEqual('component1'); }); it('cleans props from store on unMount', () => { store.setPropsForId('component123', { foo: 'bar' }); const NavigationComponent = ComponentWrapper.wrap(componentName, () => MyComponent, store, componentEventsObserver); const tree = renderer.create(); expect(store.getPropsForId('component123')).toEqual({ foo: 'bar' }); tree.unmount(); expect(store.getPropsForId('component123')).toEqual({}); }); it(`merges static members from wrapped component when generated`, () => { const NavigationComponent = ComponentWrapper.wrap(componentName, () => MyComponent, store, componentEventsObserver) as any; expect(NavigationComponent.options).toEqual({ title: 'MyComponentTitle' }); }); it(`calls unmounted on componentEventsObserver`, () => { const NavigationComponent = ComponentWrapper.wrap(componentName, () => MyComponent, store, componentEventsObserver); const tree = renderer.create(); verify(mockedComponentEventsObserver.unmounted('component123')).never(); tree.unmount(); verify(mockedComponentEventsObserver.unmounted('component123')).once(); }); describe(`register with redux store`, () => { class MyReduxComp extends React.Component { static get options() { return { foo: 123 }; } render() { return ( {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 = require('redux').createStore((state = initialState) => state); it(`wraps the component with a react-redux provider with passed store`, () => { const NavigationComponent = ComponentWrapper.wrap(componentName, () => ConnectedComp, store, componentEventsObserver, ReduxProvider, reduxStore); const tree = renderer.create(); expect(tree.toJSON()!.children).toEqual(['it just works']); expect((NavigationComponent as any).options).toEqual({ foo: 123 }); }); }); });