diff options
author | Florian Dold <florian.dold@gmail.com> | 2016-11-08 15:07:07 +0100 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2016-11-08 15:19:39 +0100 |
commit | afb9fba64be1f15a3ce3ed31214a704e73e5e8bb (patch) | |
tree | 6f69712a8c976178c05144483ff0c8e9b09445c8 /thirdparty/preact/test/browser | |
parent | b37e7762bb5492cbd6788863232e7d2634ab5e5c (diff) | |
parent | 6e5fb04d3f3f9a6cd43ac20896d73321dd079f96 (diff) | |
download | wallet-core-afb9fba64be1f15a3ce3ed31214a704e73e5e8bb.tar.xz |
Update preact version
Diffstat (limited to 'thirdparty/preact/test/browser')
-rw-r--r-- | thirdparty/preact/test/browser/components.js | 19 | ||||
-rw-r--r-- | thirdparty/preact/test/browser/context.js | 32 | ||||
-rw-r--r-- | thirdparty/preact/test/browser/devtools.js | 234 | ||||
-rw-r--r-- | thirdparty/preact/test/browser/lifecycle.js | 4 | ||||
-rw-r--r-- | thirdparty/preact/test/browser/linked-state.js | 20 | ||||
-rw-r--r-- | thirdparty/preact/test/browser/refs.js | 22 | ||||
-rw-r--r-- | thirdparty/preact/test/browser/spec.js | 11 |
7 files changed, 307 insertions, 35 deletions
diff --git a/thirdparty/preact/test/browser/components.js b/thirdparty/preact/test/browser/components.js index b4649a719..9ef43cb1c 100644 --- a/thirdparty/preact/test/browser/components.js +++ b/thirdparty/preact/test/browser/components.js @@ -70,7 +70,7 @@ describe('Components', () => { expect(C3) .to.have.been.calledOnce - .and.to.have.been.calledWith(PROPS) + .and.to.have.been.calledWithMatch(PROPS) .and.to.have.returned(sinon.match({ nodeName: 'div', attributes: PROPS @@ -197,7 +197,7 @@ describe('Components', () => { expect(Outer) .to.have.been.calledOnce - .and.to.have.been.calledWith(PROPS) + .and.to.have.been.calledWithMatch(PROPS) .and.to.have.returned(sinon.match({ nodeName: Inner, attributes: PROPS @@ -205,7 +205,7 @@ describe('Components', () => { expect(Inner) .to.have.been.calledOnce - .and.to.have.been.calledWith(PROPS) + .and.to.have.been.calledWithMatch(PROPS) .and.to.have.returned(sinon.match({ nodeName: 'div', attributes: PROPS, @@ -247,7 +247,7 @@ describe('Components', () => { expect(Inner).to.have.been.calledTwice; expect(Inner.secondCall) - .to.have.been.calledWith({ foo:'bar', i:2 }) + .to.have.been.calledWithMatch({ foo:'bar', i:2 }) .and.to.have.returned(sinon.match({ attributes: { j: 2, @@ -269,7 +269,7 @@ describe('Components', () => { expect(Inner).to.have.been.calledThrice; expect(Inner.thirdCall) - .to.have.been.calledWith({ foo:'bar', i:3 }) + .to.have.been.calledWithMatch({ foo:'bar', i:3 }) .and.to.have.returned(sinon.match({ attributes: { j: 3, @@ -344,7 +344,7 @@ describe('Components', () => { expect(Inner.prototype.render).to.have.been.calledTwice; expect(Inner.prototype.render.secondCall) - .to.have.been.calledWith({ foo:'bar', i:2 }) + .to.have.been.calledWithMatch({ foo:'bar', i:2 }) .and.to.have.returned(sinon.match({ attributes: { j: 2, @@ -372,7 +372,7 @@ describe('Components', () => { expect(Inner.prototype.render).to.have.been.calledThrice; expect(Inner.prototype.render.thirdCall) - .to.have.been.calledWith({ foo:'bar', i:3 }) + .to.have.been.calledWithMatch({ foo:'bar', i:3 }) .and.to.have.returned(sinon.match({ attributes: { j: 3, @@ -435,7 +435,7 @@ describe('Components', () => { expect(Inner.prototype.componentDidMount).to.have.been.calledOnce; expect(Inner.prototype.componentWillMount).to.have.been.calledBefore(Inner.prototype.componentDidMount); - root = render(<asdf />, scratch, root); + render(<asdf />, scratch, root); expect(Inner.prototype.componentWillUnmount).to.have.been.calledOnce; expect(Inner.prototype.componentDidUnmount).to.have.been.calledOnce; @@ -689,8 +689,7 @@ describe('Components', () => { expect(C1.prototype.componentWillMount, 'unmount innermost w/ intermediary div, C1').not.to.have.been.called; expect(C2.prototype.componentDidUnmount, 'unmount innermost w/ intermediary div, C2 ummount').not.to.have.been.called; - // @TODO this was just incorrect? - // expect(C2.prototype.componentWillMount, 'unmount innermost w/ intermediary div, C2').not.to.have.been.called; + expect(C2.prototype.componentWillMount, 'unmount innermost w/ intermediary div, C2').not.to.have.been.called; expect(C3.prototype.componentDidUnmount, 'unmount innermost w/ intermediary div, C3').to.have.been.calledOnce; reset(); diff --git a/thirdparty/preact/test/browser/context.js b/thirdparty/preact/test/browser/context.js index e62a948a4..ed5f81471 100644 --- a/thirdparty/preact/test/browser/context.js +++ b/thirdparty/preact/test/browser/context.js @@ -1,6 +1,8 @@ import { h, render, Component } from '../../src/preact'; /** @jsx h */ +const CHILDREN_MATCHER = sinon.match( v => v==null || Array.isArray(v) && !v.length , '[empty children]'); + describe('context', () => { let scratch; @@ -57,18 +59,19 @@ describe('context', () => { expect(Outer.prototype.getChildContext).to.have.been.calledOnce; // initial render does not invoke anything but render(): - expect(Inner.prototype.render).to.have.been.calledWith({}, {}, CONTEXT); + expect(Inner.prototype.render).to.have.been.calledWith({ children:CHILDREN_MATCHER }, {}, CONTEXT); CONTEXT.foo = 'bar'; render(<Outer {...PROPS} />, scratch, scratch.lastChild); expect(Outer.prototype.getChildContext).to.have.been.calledTwice; - expect(Inner.prototype.shouldComponentUpdate).to.have.been.calledOnce.and.calledWith(PROPS, {}, CONTEXT); - expect(Inner.prototype.componentWillReceiveProps).to.have.been.calledWith(PROPS, CONTEXT); - expect(Inner.prototype.componentWillUpdate).to.have.been.calledWith(PROPS, {}); - expect(Inner.prototype.componentDidUpdate).to.have.been.calledWith({}, {}); - expect(Inner.prototype.render).to.have.been.calledWith(PROPS, {}, CONTEXT); + let props = { children: CHILDREN_MATCHER, ...PROPS }; + expect(Inner.prototype.shouldComponentUpdate).to.have.been.calledOnce.and.calledWith(props, {}, CONTEXT); + expect(Inner.prototype.componentWillReceiveProps).to.have.been.calledWith(props, CONTEXT); + expect(Inner.prototype.componentWillUpdate).to.have.been.calledWith(props, {}); + expect(Inner.prototype.componentDidUpdate).to.have.been.calledWith({ children:CHILDREN_MATCHER }, {}); + expect(Inner.prototype.render).to.have.been.calledWith(props, {}, CONTEXT); /* Future: @@ -115,18 +118,19 @@ describe('context', () => { expect(Outer.prototype.getChildContext).to.have.been.calledOnce; // initial render does not invoke anything but render(): - expect(Inner.prototype.render).to.have.been.calledWith({}, {}, CONTEXT); + expect(Inner.prototype.render).to.have.been.calledWith({ children: CHILDREN_MATCHER }, {}, CONTEXT); CONTEXT.foo = 'bar'; render(<Outer {...PROPS} />, scratch, scratch.lastChild); expect(Outer.prototype.getChildContext).to.have.been.calledTwice; - expect(Inner.prototype.shouldComponentUpdate).to.have.been.calledOnce.and.calledWith(PROPS, {}, CONTEXT); - expect(Inner.prototype.componentWillReceiveProps).to.have.been.calledWith(PROPS, CONTEXT); - expect(Inner.prototype.componentWillUpdate).to.have.been.calledWith(PROPS, {}); - expect(Inner.prototype.componentDidUpdate).to.have.been.calledWith({}, {}); - expect(Inner.prototype.render).to.have.been.calledWith(PROPS, {}, CONTEXT); + let props = { children: CHILDREN_MATCHER, ...PROPS }; + expect(Inner.prototype.shouldComponentUpdate).to.have.been.calledOnce.and.calledWith(props, {}, CONTEXT); + expect(Inner.prototype.componentWillReceiveProps).to.have.been.calledWith(props, CONTEXT); + expect(Inner.prototype.componentWillUpdate).to.have.been.calledWith(props, {}); + expect(Inner.prototype.componentDidUpdate).to.have.been.calledWith({ children: CHILDREN_MATCHER }, {}); + expect(Inner.prototype.render).to.have.been.calledWith(props, {}, CONTEXT); // make sure render() could make use of context.a expect(Inner.prototype.render).to.have.returned(sinon.match({ children:['a'] })); @@ -164,7 +168,7 @@ describe('context', () => { render(<Outer />, scratch); - expect(Inner.prototype.render).to.have.been.calledWith({}, {}, { outerContext }); - expect(InnerMost.prototype.render).to.have.been.calledWith({}, {}, { outerContext, innerContext }); + expect(Inner.prototype.render).to.have.been.calledWith({ children: CHILDREN_MATCHER }, {}, { outerContext }); + expect(InnerMost.prototype.render).to.have.been.calledWith({ children: CHILDREN_MATCHER }, {}, { outerContext, innerContext }); }); }); diff --git a/thirdparty/preact/test/browser/devtools.js b/thirdparty/preact/test/browser/devtools.js new file mode 100644 index 000000000..12c0e3369 --- /dev/null +++ b/thirdparty/preact/test/browser/devtools.js @@ -0,0 +1,234 @@ +import { h, Component, render } from '../../src/preact'; +import { initDevTools } from '../../devtools/devtools'; +import { unmountComponent } from '../../src/vdom/component'; + +class StatefulComponent extends Component { + constructor(props) { + super(props); + + this.state = {count: 0}; + } + + render() { + return h('span', {}, String(this.state.count)); + } +} + +function FunctionalComponent() { + return h('span', {class: 'functional'}, 'Functional'); +} + +function Label({label}) { + return label; +} + +class MultiChild extends Component { + constructor(props) { + super(props); + this.state = {count: props.initialCount}; + } + + render() { + return h('div', {}, Array(this.state.count).fill('child')); + } +} + +let describe_ = describe; +if (!('name' in Function.prototype)) { + // Skip these tests under Internet Explorer + describe_ = describe.skip; +} + +describe_('React Developer Tools integration', () => { + let cleanup; + let container; + let renderer; + + // Maps of DOM node to React*Component-like objects. + // For composite components, there will be two instances for each node, one + // for the composite component (instanceMap) and one for the root child DOM + // component rendered by that component (domInstanceMap) + let instanceMap = new Map(); + let domInstanceMap = new Map(); + + beforeEach(() => { + container = document.createElement('div'); + document.body.appendChild(container); + + const onMount = instance => { + if (instance._renderedChildren) { + domInstanceMap.set(instance.node, instance); + } else { + instanceMap.set(instance.node, instance); + } + }; + + const onUnmount = instance => { + instanceMap.delete(instance.node); + domInstanceMap.delete(instance.node); + }; + + global.__REACT_DEVTOOLS_GLOBAL_HOOK__ = { + inject: sinon.spy(_renderer => { + renderer = _renderer; + renderer.Mount._renderNewRootComponent = sinon.stub(); + renderer.Reconciler.mountComponent = sinon.spy(onMount); + renderer.Reconciler.unmountComponent = sinon.spy(onUnmount); + renderer.Reconciler.receiveComponent = sinon.stub(); + }) + }; + cleanup = initDevTools(); + }); + + afterEach(() => { + container.remove(); + cleanup(); + }); + + it('registers preact as a renderer with the React DevTools hook', () => { + expect(global.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject).to.be.called; + }); + + // Basic component addition/update/removal tests + it('notifies dev tools about new components', () => { + render(h(StatefulComponent), container); + expect(renderer.Reconciler.mountComponent).to.be.called; + }); + + it('notifies dev tools about component updates', () => { + const node = render(h(StatefulComponent), container); + node._component.forceUpdate(); + expect(renderer.Reconciler.receiveComponent).to.be.called; + }); + + it('notifies dev tools when components are removed', () => { + const node = render(h(StatefulComponent), container); + unmountComponent(node._component, true); + expect(renderer.Reconciler.unmountComponent).to.be.called; + }); + + // Test properties of DOM components exposed to devtools via + // ReactDOMComponent-like instances + it('exposes the tag name of DOM components', () => { + const node = render(h(StatefulComponent), container); + const domInstance = domInstanceMap.get(node); + expect(domInstance._currentElement.type).to.equal('span'); + }); + + it('exposes DOM component props', () => { + const node = render(h(FunctionalComponent), container); + const domInstance = domInstanceMap.get(node); + expect(domInstance._currentElement.props.class).to.equal('functional'); + }); + + it('exposes text component contents', () => { + const node = render(h(Label, {label: 'Text content'}), container); + const textInstance = domInstanceMap.get(node); + expect(textInstance._stringText).to.equal('Text content'); + }); + + // Test properties of composite components exposed to devtools via + // ReactCompositeComponent-like instances + it('exposes the name of composite component classes', () => { + const node = render(h(StatefulComponent), container); + expect(instanceMap.get(node).getName()).to.equal('StatefulComponent'); + }); + + it('exposes composite component props', () => { + const node = render(h(Label, {label: 'Text content'}), container); + const instance = instanceMap.get(node); + expect(instance._currentElement.props.label).to.equal('Text content'); + }); + + it('exposes composite component state', () => { + const node = render(h(StatefulComponent), container); + + node._component.setState({count: 42}); + node._component.forceUpdate(); + + expect(instanceMap.get(node).state).to.deep.equal({count: 42}); + }); + + // Test setting state via devtools + it('updates component when setting state from devtools', () => { + const node = render(h(StatefulComponent), container); + + instanceMap.get(node).setState({count: 10}); + instanceMap.get(node).forceUpdate(); + + expect(node.textContent).to.equal('10'); + }); + + // Test that the original instance is exposed via `_instance` so it can + // be accessed conveniently via `$r` in devtools + + // Functional component handling tests + it('wraps functional components with stateful ones', () => { + const vnode = h(FunctionalComponent); + expect(vnode.nodeName.prototype).to.have.property('render'); + }); + + it('exposes the name of functional components', () => { + const node = render(h(FunctionalComponent), container); + const instance = instanceMap.get(node); + expect(instance.getName()).to.equal('FunctionalComponent'); + }); + + it('exposes a fallback name if the component has no useful name', () => { + const node = render(h(() => h('div')), container); + const instance = instanceMap.get(node); + expect(instance.getName()).to.equal('(Function.name missing)'); + }); + + // Test handling of DOM children + it('notifies dev tools about DOM children', () => { + const node = render(h(StatefulComponent), container); + const domInstance = domInstanceMap.get(node); + expect(renderer.Reconciler.mountComponent).to.have.been.calledWith(domInstance); + }); + + it('notifies dev tools when a component update adds DOM children', () => { + const node = render(h(MultiChild, {initialCount: 2}), container); + + node._component.setState({count: 4}); + node._component.forceUpdate(); + + expect(renderer.Reconciler.mountComponent).to.have.been.called.twice; + }); + + it('notifies dev tools when a component update modifies DOM children', () => { + const node = render(h(StatefulComponent), container); + + instanceMap.get(node).setState({count: 10}); + instanceMap.get(node).forceUpdate(); + + const textInstance = domInstanceMap.get(node.childNodes[0]); + expect(textInstance._stringText).to.equal('10'); + }); + + it('notifies dev tools when a component update removes DOM children', () => { + const node = render(h(MultiChild, {initialCount: 1}), container); + + node._component.setState({count: 0}); + node._component.forceUpdate(); + + expect(renderer.Reconciler.unmountComponent).to.be.called; + }); + + // Root component info + it('exposes root components on the _instancesByReactRootID map', () => { + render(h(StatefulComponent), container); + expect(Object.keys(renderer.Mount._instancesByReactRootID).length).to.equal(1); + }); + + it('notifies dev tools when new root components are mounted', () => { + render(h(StatefulComponent), container); + expect(renderer.Mount._renderNewRootComponent).to.be.called; + }); + + it('removes root components when they are unmounted', () => { + const node = render(h(StatefulComponent), container); + unmountComponent(node._component, true); + expect(Object.keys(renderer.Mount._instancesByReactRootID).length).to.equal(0); + }); +}); diff --git a/thirdparty/preact/test/browser/lifecycle.js b/thirdparty/preact/test/browser/lifecycle.js index d6204ca8f..4deb92163 100644 --- a/thirdparty/preact/test/browser/lifecycle.js +++ b/thirdparty/preact/test/browser/lifecycle.js @@ -3,6 +3,8 @@ import { h, render, rerender, Component } from '../../src/preact'; let spyAll = obj => Object.keys(obj).forEach( key => sinon.spy(obj,key) ); +const EMPTY_CHILDREN = []; + describe('Lifecycle methods', () => { let scratch; @@ -50,7 +52,7 @@ describe('Lifecycle methods', () => { } class Inner extends Component { componentWillUpdate(nextProps, nextState) { - expect(nextProps).to.be.deep.equal({i: 1}); + expect(nextProps).to.be.deep.equal({ children:EMPTY_CHILDREN, i: 1 }); expect(nextState).to.be.deep.equal({}); } render() { diff --git a/thirdparty/preact/test/browser/linked-state.js b/thirdparty/preact/test/browser/linked-state.js index 1ca84cdc6..03db2a7b8 100644 --- a/thirdparty/preact/test/browser/linked-state.js +++ b/thirdparty/preact/test/browser/linked-state.js @@ -26,7 +26,10 @@ describe('linked-state', () => { element.type= 'text'; element.value = 'newValue'; - linkFunction({ currentTarget: element }); + linkFunction({ + currentTarget: element, + target: element + }); expect(TestComponent.prototype.setState).to.have.been.calledOnce; expect(TestComponent.prototype.setState).to.have.been.calledWith({'testStateKey': 'newValue'}); @@ -42,7 +45,10 @@ describe('linked-state', () => { checkboxElement.type= 'checkbox'; checkboxElement.checked = true; - linkFunction({ currentTarget: checkboxElement }); + linkFunction({ + currentTarget: checkboxElement, + target: checkboxElement + }); expect(TestComponent.prototype.setState).to.have.been.calledOnce; expect(TestComponent.prototype.setState).to.have.been.calledWith({'testStateKey': true}); @@ -53,7 +59,10 @@ describe('linked-state', () => { radioElement.type= 'radio'; radioElement.checked = true; - linkFunction({ currentTarget: radioElement }); + linkFunction({ + currentTarget: radioElement, + target: radioElement + }); expect(TestComponent.prototype.setState).to.have.been.calledOnce; expect(TestComponent.prototype.setState).to.have.been.calledWith({'testStateKey': true}); @@ -66,7 +75,10 @@ describe('linked-state', () => { element.type= 'text'; element.value = 'newValue'; - linkFunction({ currentTarget: element }); + linkFunction({ + currentTarget: element, + target: element + }); expect(TestComponent.prototype.setState).to.have.been.calledOnce; expect(TestComponent.prototype.setState).to.have.been.calledWith({nested: {state: {key: 'newValue'}}}); diff --git a/thirdparty/preact/test/browser/refs.js b/thirdparty/preact/test/browser/refs.js index 89678b76e..337a9717b 100644 --- a/thirdparty/preact/test/browser/refs.js +++ b/thirdparty/preact/test/browser/refs.js @@ -200,8 +200,8 @@ describe('refs', () => { </div> ), scratch); - expect(Foo.prototype.render).to.have.been.calledWithExactly({ a:'a' }, { }, { }); - expect(Bar).to.have.been.calledWithExactly({ b:'b', ref:bar }, { }); + expect(Foo.prototype.render).to.have.been.calledWithMatch({ ref:sinon.match.falsy, a:'a' }, { }, { }); + expect(Bar).to.have.been.calledWithMatch({ b:'b', ref:bar }, { }); }); // Test for #232 @@ -284,4 +284,22 @@ describe('refs', () => { expect(inst.handleMount.firstCall).to.have.been.calledWith(null); expect(inst.handleMount.secondCall).to.have.been.calledWith(scratch.querySelector('#div')); }); + + + it('should add refs to components representing DOM nodes with no attributes if they have been pre-rendered', () => { + // Simulate pre-render + let parent = document.createElement('div'); + let child = document.createElement('div'); + parent.appendChild(child); + scratch.appendChild(parent); // scratch contains: <div><div></div></div> + + let ref = spy('ref'); + + function Wrapper() { + return <div></div>; + } + + render(<div><Wrapper ref={ref} /></div>, scratch, scratch.firstChild); + expect(ref).to.have.been.calledOnce.and.calledWith(scratch.firstChild.firstChild); + }); }); diff --git a/thirdparty/preact/test/browser/spec.js b/thirdparty/preact/test/browser/spec.js index eb48151f0..d33cdb93f 100644 --- a/thirdparty/preact/test/browser/spec.js +++ b/thirdparty/preact/test/browser/spec.js @@ -1,6 +1,8 @@ import { h, render, rerender, Component } from '../../src/preact'; /** @jsx h */ +const EMPTY_CHILDREN = []; + describe('Component spec', () => { let scratch; @@ -24,6 +26,7 @@ describe('Component spec', () => { constructor(props, context) { super(props, context); expect(props).to.be.deep.equal({ + children: EMPTY_CHILDREN, fieldA: 1, fieldB: 2, fieldC: 1, fieldD: 2 }); @@ -81,14 +84,14 @@ describe('Component spec', () => { fieldC: 1, fieldD: 2 }; - expect(proto.ctor).to.have.been.calledWith(PROPS1); - expect(proto.render).to.have.been.calledWith(PROPS1); + expect(proto.ctor).to.have.been.calledWithMatch(PROPS1); + expect(proto.render).to.have.been.calledWithMatch(PROPS1); rerender(); // expect(proto.ctor).to.have.been.calledWith(PROPS2); - expect(proto.componentWillReceiveProps).to.have.been.calledWith(PROPS2); - expect(proto.render).to.have.been.calledWith(PROPS2); + expect(proto.componentWillReceiveProps).to.have.been.calledWithMatch(PROPS2); + expect(proto.render).to.have.been.calledWithMatch(PROPS2); }); // @TODO: migrate this to preact-compat |