summaryrefslogtreecommitdiff
path: root/preact/test/browser/replaceNode.test.js
diff options
context:
space:
mode:
Diffstat (limited to 'preact/test/browser/replaceNode.test.js')
-rw-r--r--preact/test/browser/replaceNode.test.js239
1 files changed, 239 insertions, 0 deletions
diff --git a/preact/test/browser/replaceNode.test.js b/preact/test/browser/replaceNode.test.js
new file mode 100644
index 0000000..63cdd2d
--- /dev/null
+++ b/preact/test/browser/replaceNode.test.js
@@ -0,0 +1,239 @@
+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(<div id="a">contents</div>, 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(<div id="a" />, scratch, childA);
+ expect(scratch.innerHTML).to.equal(
+ '<div id="a"></div><div id="b"></div><div id="c"></div>'
+ );
+ });
+
+ it('should notice prop changes on replaceNode', () => {
+ setupABCDom(scratch);
+ const childA = scratch.querySelector('#a');
+
+ render(<div id="a" className="b" />, scratch, childA);
+ expect(sortAttributes(String(scratch.innerHTML))).to.equal(
+ sortAttributes(
+ '<div id="a" class="b"></div><div id="b"></div><div id="c"></div>'
+ )
+ );
+ });
+
+ it('should unmount existing components', () => {
+ const unmount = sinon.spy();
+ const mount = sinon.spy();
+ class App extends Component {
+ componentDidMount() {
+ mount();
+ }
+
+ componentWillUnmount() {
+ unmount();
+ }
+
+ render() {
+ return <div>App</div>;
+ }
+ }
+
+ render(
+ <div id="a">
+ <App />
+ </div>,
+ scratch
+ );
+ expect(scratch.innerHTML).to.equal('<div id="a"><div>App</div></div>');
+ expect(mount).to.be.calledOnce;
+
+ render(<div id="a">new</div>, scratch, scratch.querySelector('#a'));
+ expect(scratch.innerHTML).to.equal('<div id="a">new</div>');
+ 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 <span>App</span>;
+ }
+ }
+
+ scratch.innerHTML = `<div id="child"></div>`;
+
+ const childContainer = scratch.querySelector('#child');
+
+ render(<App />, childContainer);
+ expect(serializeHtml(childContainer)).to.equal('<span>App</span>');
+ expect(mount).to.be.calledOnce;
+
+ render(<div />, scratch, scratch.firstElementChild);
+ expect(serializeHtml(scratch)).to.equal('<div id=""></div>');
+ 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 = '<div id="a">childA</div>';
+ const expectedB = '<div id="b">childB</div>';
+ const expectedC = '<div id="c">childC</div>';
+
+ render(<div id="a">childA</div>, scratch, childA);
+ render(<div id="b">childB</div>, scratch, childB);
+ render(<div id="c">childC</div>, 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 <div>My Component</div>;
+ }
+ }
+
+ const container = document.createElement('div');
+ scratch.appendChild(container);
+
+ render(<MyComponent />, scratch, container);
+ expect(mountSpy).to.be.calledOnce;
+
+ render(<div>Not my component</div>, document.body, container);
+ expect(unmountSpy).to.be.calledOnce;
+ });
+
+ it('should double replace', () => {
+ const container = document.createElement('div');
+ scratch.appendChild(container);
+
+ render(<div>Hello</div>, scratch, scratch.firstElementChild);
+ expect(scratch.innerHTML).to.equal('<div>Hello</div>');
+
+ render(<div>Hello</div>, scratch, scratch.firstElementChild);
+ expect(scratch.innerHTML).to.equal('<div>Hello</div>');
+ });
+
+ it('should replaceNode after rendering', () => {
+ function App({ i }) {
+ return <p>{i}</p>;
+ }
+
+ render(<App i={2} />, scratch);
+ expect(scratch.innerHTML).to.equal('<p>2</p>');
+
+ render(<App i={3} />, scratch, scratch.firstChild);
+ expect(scratch.innerHTML).to.equal('<p>3</p>');
+ });
+
+ it("shouldn't remove elements on subsequent renders with replaceNode", () => {
+ const placeholder = document.createElement('div');
+ scratch.appendChild(placeholder);
+ const App = () => (
+ <div>
+ New content
+ <button>Update</button>
+ </div>
+ );
+
+ render(<App />, scratch, placeholder);
+ expect(scratch.innerHTML).to.equal(
+ '<div>New content<button>Update</button></div>'
+ );
+
+ render(<App />, scratch, placeholder);
+ expect(scratch.innerHTML).to.equal(
+ '<div>New content<button>Update</button></div>'
+ );
+ });
+
+ it('should remove redundant elements on subsequent renders with replaceNode', () => {
+ const placeholder = document.createElement('div');
+ scratch.appendChild(placeholder);
+ const App = () => (
+ <div>
+ New content
+ <button>Update</button>
+ </div>
+ );
+
+ render(<App />, scratch, placeholder);
+ expect(scratch.innerHTML).to.equal(
+ '<div>New content<button>Update</button></div>'
+ );
+
+ placeholder.appendChild(document.createElement('span'));
+ expect(scratch.innerHTML).to.equal(
+ '<div>New content<button>Update</button><span></span></div>'
+ );
+
+ render(<App />, scratch, placeholder);
+ expect(scratch.innerHTML).to.equal(
+ '<div>New content<button>Update</button></div>'
+ );
+ });
+});