import { createElement, render, Component } from 'preact';
import {
setupScratch,
teardown,
serializeHtml,
sortAttributes
} from '../_util/helpers';
/** @jsx createElement */
describe('replaceNode parameter in render()', () => {
let scratch;
/**
* @param {HTMLDivElement} container
* @returns {HTMLDivElement[]}
*/
function setupABCDom(container) {
return ['a', 'b', 'c'].map(id => {
const child = document.createElement('div');
child.id = id;
container.appendChild(child);
return child;
});
}
beforeEach(() => {
scratch = setupScratch();
});
afterEach(() => {
teardown(scratch);
});
it('should use replaceNode as render root and not inject into it', () => {
setupABCDom(scratch);
const childA = scratch.querySelector('#a');
render(
contents
, scratch, childA);
expect(scratch.querySelector('#a')).to.equalNode(childA);
expect(childA.innerHTML).to.equal('contents');
});
it('should not remove siblings of replaceNode', () => {
setupABCDom(scratch);
const childA = scratch.querySelector('#a');
render(, scratch, childA);
expect(scratch.innerHTML).to.equal(
''
);
});
it('should notice prop changes on replaceNode', () => {
setupABCDom(scratch);
const childA = scratch.querySelector('#a');
render(, scratch, childA);
expect(sortAttributes(String(scratch.innerHTML))).to.equal(
sortAttributes(
''
)
);
});
it('should unmount existing components', () => {
const unmount = sinon.spy();
const mount = sinon.spy();
class App extends Component {
componentDidMount() {
mount();
}
componentWillUnmount() {
unmount();
}
render() {
return App
;
}
}
render(
,
scratch
);
expect(scratch.innerHTML).to.equal('');
expect(mount).to.be.calledOnce;
render(new
, scratch, scratch.querySelector('#a'));
expect(scratch.innerHTML).to.equal('new
');
expect(unmount).to.be.calledOnce;
});
it('should unmount existing components in prerendered HTML', () => {
const unmount = sinon.spy();
const mount = sinon.spy();
class App extends Component {
componentDidMount() {
mount();
}
componentWillUnmount() {
unmount();
}
render() {
return App;
}
}
scratch.innerHTML = ``;
const childContainer = scratch.querySelector('#child');
render(, childContainer);
expect(serializeHtml(childContainer)).to.equal('App');
expect(mount).to.be.calledOnce;
render(, scratch, scratch.firstElementChild);
expect(serializeHtml(scratch)).to.equal('');
expect(unmount).to.be.calledOnce;
});
it('should render multiple render roots in one parentDom', () => {
setupABCDom(scratch);
const childA = scratch.querySelector('#a');
const childB = scratch.querySelector('#b');
const childC = scratch.querySelector('#c');
const expectedA = 'childA
';
const expectedB = 'childB
';
const expectedC = 'childC
';
render(childA
, scratch, childA);
render(childB
, scratch, childB);
render(childC
, scratch, childC);
expect(scratch.innerHTML).to.equal(`${expectedA}${expectedB}${expectedC}`);
});
it('should call unmount when working with replaceNode', () => {
const mountSpy = sinon.spy();
const unmountSpy = sinon.spy();
class MyComponent extends Component {
componentDidMount() {
mountSpy();
}
componentWillUnmount() {
unmountSpy();
}
render() {
return My Component
;
}
}
const container = document.createElement('div');
scratch.appendChild(container);
render(, scratch, container);
expect(mountSpy).to.be.calledOnce;
render(Not my component
, document.body, container);
expect(unmountSpy).to.be.calledOnce;
});
it('should double replace', () => {
const container = document.createElement('div');
scratch.appendChild(container);
render(Hello
, scratch, scratch.firstElementChild);
expect(scratch.innerHTML).to.equal('Hello
');
render(Hello
, scratch, scratch.firstElementChild);
expect(scratch.innerHTML).to.equal('Hello
');
});
it('should replaceNode after rendering', () => {
function App({ i }) {
return {i}
;
}
render(, scratch);
expect(scratch.innerHTML).to.equal('2
');
render(, scratch, scratch.firstChild);
expect(scratch.innerHTML).to.equal('3
');
});
it("shouldn't remove elements on subsequent renders with replaceNode", () => {
const placeholder = document.createElement('div');
scratch.appendChild(placeholder);
const App = () => (
New content
);
render(, scratch, placeholder);
expect(scratch.innerHTML).to.equal(
'New content
'
);
render(, scratch, placeholder);
expect(scratch.innerHTML).to.equal(
'New content
'
);
});
it('should remove redundant elements on subsequent renders with replaceNode', () => {
const placeholder = document.createElement('div');
scratch.appendChild(placeholder);
const App = () => (
New content
);
render(, scratch, placeholder);
expect(scratch.innerHTML).to.equal(
'New content
'
);
placeholder.appendChild(document.createElement('span'));
expect(scratch.innerHTML).to.equal(
'New content
'
);
render(, scratch, placeholder);
expect(scratch.innerHTML).to.equal(
'New content
'
);
});
});