import { createElement, hydrate, Fragment } from 'preact'; import { setupScratch, teardown, sortAttributes, serializeHtml, spyOnElementAttributes, createEvent } from '../_util/helpers'; import { ul, li, div } from '../_util/dom'; import { logCall, clearLog, getLog } from '../_util/logCall'; /** @jsx createElement */ describe('hydrate()', () => { /** @type {HTMLElement} */ let scratch; let attributesSpy; const List = ({ children }) => ; const ListItem = ({ children, onClick = null }) => (
  • {children}
  • ); let resetAppendChild; let resetInsertBefore; let resetRemoveChild; let resetRemove; let resetSetAttribute; let resetRemoveAttribute; before(() => { resetAppendChild = logCall(Element.prototype, 'appendChild'); resetInsertBefore = logCall(Element.prototype, 'insertBefore'); resetRemoveChild = logCall(Element.prototype, 'removeChild'); resetRemove = logCall(Element.prototype, 'remove'); resetSetAttribute = logCall(Element.prototype, 'setAttribute'); resetRemoveAttribute = logCall(Element.prototype, 'removeAttribute'); }); after(() => { resetAppendChild(); resetInsertBefore(); resetRemoveChild(); resetRemove(); resetSetAttribute(); resetRemoveAttribute(); }); beforeEach(() => { scratch = setupScratch(); attributesSpy = spyOnElementAttributes(); }); afterEach(() => { teardown(scratch); clearLog(); }); it('should reuse existing DOM', () => { const onClickSpy = sinon.spy(); const html = ul([li('1'), li('2'), li('3')]); scratch.innerHTML = html; clearLog(); hydrate( , scratch ); expect(scratch.innerHTML).to.equal(html); expect(getLog()).to.deep.equal([]); expect(onClickSpy).not.to.have.been.called; scratch.querySelector('li:last-child').dispatchEvent(createEvent('click')); expect(onClickSpy).to.have.been.called.calledOnce; }); it('should reuse existing DOM when given components', () => { const onClickSpy = sinon.spy(); const html = ul([li('1'), li('2'), li('3')]); scratch.innerHTML = html; clearLog(); hydrate( 1 2 3 , scratch ); expect(scratch.innerHTML).to.equal(html); expect(getLog()).to.deep.equal([]); expect(onClickSpy).not.to.have.been.called; scratch.querySelector('li:last-child').dispatchEvent(createEvent('click')); expect(onClickSpy).to.have.been.called.calledOnce; }); it('should properly set event handlers to existing DOM when given components', () => { const proto = Element.prototype; sinon.spy(proto, 'addEventListener'); const clickHandlers = [sinon.spy(), sinon.spy(), sinon.spy()]; const html = ul([li('1'), li('2'), li('3')]); scratch.innerHTML = html; clearLog(); hydrate( 1 2 3 , scratch ); expect(scratch.innerHTML).to.equal(html); expect(getLog()).to.deep.equal([]); expect(proto.addEventListener).to.have.been.calledThrice; expect(clickHandlers[2]).not.to.have.been.called; scratch.querySelector('li:last-child').dispatchEvent(createEvent('click')); expect(clickHandlers[2]).to.have.been.calledOnce; }); it('should add missing nodes to existing DOM when hydrating', () => { const html = ul([li('1')]); scratch.innerHTML = html; clearLog(); hydrate( 1 2 3 , scratch ); expect(scratch.innerHTML).to.equal(ul([li('1'), li('2'), li('3')])); expect(getLog()).to.deep.equal([ '
  • .appendChild(#text)', '