const React = require('react'); const { Component } = require('react'); const { Text } = require('react-native'); const renderer = require('react-test-renderer'); const ComponentWrapper = require('./ComponentWrapper'); const Store = require('./Store'); describe('ComponentWrapper', () => { let store; const componentName = 'example.MyComponent'; let childRef; class MyComponent extends Component { render() { return {'Hello, World!'}; } } class TestParent extends Component { constructor(props) { super(props); this.ChildClass = props.ChildClass; this.state = { propsFromState: {} }; } render() { const Child = this.ChildClass; return ( childRef = r} componentId="component1" {...this.state.propsFromState} /> ); } } beforeEach(() => { store = new Store(); }); it('must have componentId as prop', () => { const NavigationComponent = ComponentWrapper.wrap(componentName, MyComponent, store); const orig = console.error; console.error = (a) => a; expect(() => { renderer.create(); }).toThrow(new Error('Component example.MyComponent does not have a componentId!')); console.error = orig; }); it('wraps the component', () => { const NavigationComponent = ComponentWrapper.wrap(componentName, MyComponent, store); expect(NavigationComponent).not.toBeInstanceOf(MyComponent); const tree = renderer.create(); expect(tree.toJSON().children).toEqual(['Hello, World!']); expect(tree.getInstance().originalComponentRef).toBeInstanceOf(MyComponent); }); it('injects props from wrapper into original component', () => { const NavigationComponent = ComponentWrapper.wrap(componentName, MyComponent, store); const tree = renderer.create(); expect(tree.getInstance().originalComponentRef.props.myProp).toEqual('yo'); }); it('updates props from wrapper into original component', () => { const NavigationComponent = ComponentWrapper.wrap(componentName, MyComponent, store); const tree = renderer.create(); expect(childRef.props.foo).toEqual(undefined); tree.getInstance().setState({ propsFromState: { foo: 'yo' } }); expect(childRef.props.foo).toEqual('yo'); }); it('pulls props from the store and injects them into the inner component', () => { store.setPropsForComponentId('component123', { numberProp: 1, stringProp: 'hello', objectProp: { a: 2 } }); const NavigationComponent = ComponentWrapper.wrap(componentName, MyComponent, store); const tree = renderer.create(); const originalComponentProps = tree.getInstance().originalComponentRef.props; expect(originalComponentProps).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); const tree = renderer.create(); store.setPropsForComponentId('component1', { myProp: 'hello' }); expect(childRef.originalComponentRef.props.foo).toEqual(undefined); expect(childRef.originalComponentRef.props.myProp).toEqual(undefined); tree.getInstance().setState({ propsFromState: { foo: 'yo' } }); expect(childRef.originalComponentRef.props.foo).toEqual('yo'); expect(childRef.originalComponentRef.props.myProp).toEqual('hello'); }); it('protects id from change', () => { const NavigationComponent = ComponentWrapper.wrap(componentName, MyComponent, store); const tree = renderer.create(); expect(childRef.originalComponentRef.props.componentId).toEqual('component1'); tree.getInstance().setState({ propsFromState: { id: 'ERROR' } }); expect(childRef.originalComponentRef.props.componentId).toEqual('component1'); }); it('assignes key by id', () => { const NavigationComponent = ComponentWrapper.wrap(componentName, MyComponent, store); const tree = renderer.create(); expect(tree.getInstance().originalComponentRef.props.componentId).toEqual('component1'); expect(tree.getInstance().originalComponentRef._reactInternalInstance.key).toEqual('component1'); }); it('saves self ref into store', () => { const NavigationComponent = ComponentWrapper.wrap(componentName, MyComponent, store); const tree = renderer.create(); expect(store.getRefForComponentId('component1')).toBeDefined(); expect(store.getRefForComponentId('component1')).toBe(tree.getInstance()); }); it('cleans ref from store on unMount', () => { const NavigationComponent = ComponentWrapper.wrap(componentName, MyComponent, store); const tree = renderer.create(); expect(store.getRefForComponentId('component1')).toBeDefined(); tree.unmount(); expect(store.getRefForComponentId('component1')).toBeUndefined(); }); it('holds ref to OriginalComponent', () => { const NavigationComponent = ComponentWrapper.wrap(componentName, MyComponent, store); const tree = renderer.create(); expect(tree.getInstance().originalComponentRef).toBeDefined(); expect(tree.getInstance().originalComponentRef).toBeInstanceOf(MyComponent); }); it('cleans ref to internal component on unount', () => { const NavigationComponent = ComponentWrapper.wrap(componentName, MyComponent, store); const tree = renderer.create(); const instance = tree.getInstance(); expect(instance.originalComponentRef).toBeInstanceOf(Component); tree.unmount(); expect(instance.originalComponentRef).toBeFalsy(); }); describe('component lifecycle', () => { const didAppearCallback = jest.fn(); const didDisappearCallback = jest.fn(); const onNavigationButtonPressedCallback = jest.fn(); class MyLifecycleComponent extends MyComponent { didAppear() { didAppearCallback(); } didDisappear() { didDisappearCallback(); } onNavigationButtonPressed() { onNavigationButtonPressedCallback(); } } it('didAppear, didDisappear and onNavigationButtonPressed are optional', () => { const NavigationComponent = ComponentWrapper.wrap(componentName, MyComponent, store); const tree = renderer.create(); expect(() => tree.getInstance().didAppear()).not.toThrow(); expect(() => tree.getInstance().didDisappear()).not.toThrow(); expect(() => tree.getInstance().onNavigationButtonPressed()).not.toThrow(); }); it('calls didAppear on OriginalComponent', () => { const NavigationComponent = ComponentWrapper.wrap(componentName, MyLifecycleComponent, store); const tree = renderer.create(); expect(didAppearCallback).toHaveBeenCalledTimes(0); tree.getInstance().didAppear(); expect(didAppearCallback).toHaveBeenCalledTimes(1); }); it('calls didDisappear on OriginalComponent', () => { const NavigationComponent = ComponentWrapper.wrap(componentName, MyLifecycleComponent, store); const tree = renderer.create(); expect(didDisappearCallback).toHaveBeenCalledTimes(0); tree.getInstance().didDisappear(); expect(didDisappearCallback).toHaveBeenCalledTimes(1); }); it('calls onNavigationButtonPressed on OriginalComponent', () => { const NavigationComponent = ComponentWrapper.wrap(componentName, MyLifecycleComponent, store); const tree = renderer.create(); expect(onNavigationButtonPressedCallback).toHaveBeenCalledTimes(0); tree.getInstance().onNavigationButtonPressed(); expect(onNavigationButtonPressedCallback).toHaveBeenCalledTimes(1); }); }); });