import { h, render, Component } from '../../src/preact'; /** @jsx h */ describe('context', () => { let scratch; before( () => { scratch = document.createElement('div'); (document.body || document.documentElement).appendChild(scratch); }); beforeEach( () => { scratch.innerHTML = ''; }); after( () => { scratch.parentNode.removeChild(scratch); scratch = null; }); it('should pass context to grandchildren', () => { const CONTEXT = { a:'a' }; const PROPS = { b:'b' }; // let inner; class Outer extends Component { getChildContext() { return CONTEXT; } render(props) { return
; } } sinon.spy(Outer.prototype, 'getChildContext'); class Inner extends Component { // constructor() { // super(); // inner = this; // } shouldComponentUpdate() { return true; } componentWillReceiveProps() {} componentWillUpdate() {} componentDidUpdate() {} render(props, state, context) { return
{ context && context.a }
; } } sinon.spy(Inner.prototype, 'shouldComponentUpdate'); sinon.spy(Inner.prototype, 'componentWillReceiveProps'); sinon.spy(Inner.prototype, 'componentWillUpdate'); sinon.spy(Inner.prototype, 'componentDidUpdate'); sinon.spy(Inner.prototype, 'render'); render(, scratch, scratch.lastChild); 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); CONTEXT.foo = 'bar'; render(, 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); /* Future: * Newly created context objects are *not* currently cloned. * This test checks that they *are* cloned. */ // Inner.prototype.render.reset(); // CONTEXT.foo = 'baz'; // inner.forceUpdate(); // expect(Inner.prototype.render).to.have.been.calledWith(PROPS, {}, { a:'a', foo:'bar' }); }); it('should pass context to direct children', () => { const CONTEXT = { a:'a' }; const PROPS = { b:'b' }; class Outer extends Component { getChildContext() { return CONTEXT; } render(props) { return ; } } sinon.spy(Outer.prototype, 'getChildContext'); class Inner extends Component { shouldComponentUpdate() { return true; } componentWillReceiveProps() {} componentWillUpdate() {} componentDidUpdate() {} render(props, state, context) { return
{ context && context.a }
; } } sinon.spy(Inner.prototype, 'shouldComponentUpdate'); sinon.spy(Inner.prototype, 'componentWillReceiveProps'); sinon.spy(Inner.prototype, 'componentWillUpdate'); sinon.spy(Inner.prototype, 'componentDidUpdate'); sinon.spy(Inner.prototype, 'render'); render(, scratch, scratch.lastChild); 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); CONTEXT.foo = 'bar'; render(, 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); // make sure render() could make use of context.a expect(Inner.prototype.render).to.have.returned(sinon.match({ children:['a'] })); }); it('should preserve existing context properties when creating child contexts', () => { let outerContext = { outer:true }, innerContext = { inner:true }; class Outer extends Component { getChildContext() { return { outerContext }; } render() { return
; } } class Inner extends Component { getChildContext() { return { innerContext }; } render() { return ; } } class InnerMost extends Component { render() { return test; } } sinon.spy(Inner.prototype, 'render'); sinon.spy(InnerMost.prototype, 'render'); render(, scratch); expect(Inner.prototype.render).to.have.been.calledWith({}, {}, { outerContext }); expect(InnerMost.prototype.render).to.have.been.calledWith({}, {}, { outerContext, innerContext }); }); });