import { setupRerender } from 'preact/test-utils';
import { createElement, render, Component, Fragment } from 'preact';
import {
	setupScratch,
	teardown,
	spyAll,
	resetAllSpies
} from '../../_util/helpers';

/** @jsx createElement */

describe('Lifecycle methods', () => {
	/* eslint-disable react/display-name */

	/** @type {HTMLDivElement} */
	let scratch;

	/** @type {() => void} */
	let rerender;

	beforeEach(() => {
		scratch = setupScratch();
		rerender = setupRerender();
	});

	afterEach(() => {
		teardown(scratch);
	});

	describe('#componentDidCatch', () => {
		/** @type {Error} */
		let expectedError;

		/** @type {typeof import('../../../').Component} */
		let ThrowErr;
		class Receiver extends Component {
			componentDidCatch(error) {
				this.setState({ error });
			}

			render() {
				return this.state.error
					? String(this.state.error)
					: this.props.children;
			}
		}

		let thrower;

		spyAll(Receiver.prototype);

		function throwExpectedError() {
			throw (expectedError = new Error('Error!'));
		}

		beforeEach(() => {
			ThrowErr = class ThrowErr extends Component {
				constructor(props) {
					super(props);
					thrower = this;
				}

				componentDidCatch() {
					expect.fail("Throwing component should not catch it's own error.");
				}
				render() {
					return
ThrowErr: componentDidCatch
;
				}
			};
			sinon.spy(ThrowErr.prototype, 'componentDidCatch');

			expectedError = undefined;
			resetAllSpies(Receiver.prototype);
		});

		afterEach(() => {
			expect(
				ThrowErr.prototype.componentDidCatch,
				"Throwing component should not catch it's own error."
			).to.not.be.called;
			thrower = undefined;
		});

		it('should be called when child fails in constructor', () => {
			class ThrowErr extends Component {
				constructor(props, context) {
					super(props, context);
					throwExpectedError();
				}
				componentDidCatch() {
					expect.fail("Throwing component should not catch it's own error");
				}
				render() {
					return
;
				}
			}

			render(
				<Receiver>
					<ThrowErr />
				</Receiver>,
				scratch
			);
			rerender();

			expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
		});

		// https://github.com/preactjs/preact/issues/1570
		it('should handle double child throws', () => {
			const Child = ({ i }) => {
				throw new Error(`error! ${i}`);
			};

			const fn = () =>
				render(
					<Receiver>
						{[1, 2].map(i => (
							<Child key={i} i={i} />
						))}
					</Receiver>,
					scratch
				);
			expect(fn).to.not.throw();

			rerender();
			expect(scratch.innerHTML).to.equal('Error: error! 2');
		});

		it('should be called when child fails in componentWillMount', () => {
			ThrowErr.prototype.componentWillMount = throwExpectedError;

			render(
				<Receiver>
					<ThrowErr />
				</Receiver>,
				scratch
			);
			expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
		});

		it('should be called when child fails in render', () => {
			ThrowErr.prototype.render = throwExpectedError;

			render(
				<Receiver>
					<ThrowErr />
				</Receiver>,
				scratch
			);
			expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
		});

		it('should be called when child fails in componentDidMount', () => {
			ThrowErr.prototype.componentDidMount = throwExpectedError;

			render(
				<Receiver>
					<ThrowErr />
				</Receiver>,
				scratch
			);
			expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
		});

		it('should be called when child fails in getDerivedStateFromProps', () => {
			ThrowErr.getDerivedStateFromProps = throwExpectedError;

			sinon.spy(ThrowErr.prototype, 'render');
			render(
				<Receiver>
					<ThrowErr />
				</Receiver>,
				scratch
			);

			expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
			expect(ThrowErr.prototype.render).not.to.have.been.called;
		});

		it('should be called when child fails in getSnapshotBeforeUpdate', () => {
			ThrowErr.prototype.getSnapshotBeforeUpdate = throwExpectedError;

			render(
				<Receiver>
					<ThrowErr />
				</Receiver>,
				scratch
			);
			thrower.forceUpdate();
			rerender();

			expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
		});

		it('should be called when child fails in componentDidUpdate', () => {
			ThrowErr.prototype.componentDidUpdate = throwExpectedError;

			render(
				<Receiver>
					<ThrowErr />
				</Receiver>,
				scratch
			);

			thrower.forceUpdate();
			rerender();
			expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
		});

		it('should be called when child fails in componentWillUpdate', () => {
			ThrowErr.prototype.componentWillUpdate = throwExpectedError;

			render(
				<Receiver>
					<ThrowErr />
				</Receiver>,
				scratch
			);

			thrower.forceUpdate();
			rerender();
			expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
		});

		it('should be called when child fails in componentWillReceiveProps', () => {
			ThrowErr.prototype.componentWillReceiveProps = throwExpectedError;

			let receiver;
			class Receiver extends Component {
				constructor() {
					super();
					this.state = { foo: 'bar' };
					receiver = this;
				}
				componentDidCatch(error) {
					this.setState({ error });
				}
				render() {
					return this.state.error ? (
						String(this.state.error)
					) : (
						<ThrowErr foo={this.state.foo} />
					);
				}
			}

			sinon.spy(Receiver.prototype, 'componentDidCatch');
			render(<Receiver />, scratch);

			receiver.setState({ foo: 'baz' });
			rerender();

			expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
		});

		it('should be called when child fails in shouldComponentUpdate', () => {
			ThrowErr.prototype.shouldComponentUpdate = throwExpectedError;

			let receiver;
			class Receiver extends Component {
				constructor() {
					super();
					this.state = { foo: 'bar' };
					receiver = this;
				}
				componentDidCatch(error) {
					this.setState({ error });
				}
				render() {
					return this.state.error ? (
						String(this.state.error)
					) : (
						<ThrowErr foo={this.state.foo} />
					);
				}
			}

			sinon.spy(Receiver.prototype, 'componentDidCatch');
			render(<Receiver />, scratch);

			receiver.setState({ foo: 'baz' });
			rerender();

			expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
		});

		it('should be called when child fails in componentWillUnmount', () => {
			ThrowErr.prototype.componentWillUnmount = throwExpectedError;

			render(
				<Receiver>
					<ThrowErr />
				</Receiver>,
				scratch
			);
			render(
				<Receiver>

				</Receiver>,
				scratch
			);
			expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
		});

		it('should be called when applying a Component ref', () => {
			const Foo = () =>
;

			const ref = value => {
				if (value) {
					throwExpectedError();
				}
			};

			// In React, an error boundary handles it's own refs:
			// https://codesandbox.io/s/react-throwing-refs-lk958
			class Receiver extends Component {
				componentDidCatch(error) {
					this.setState({ error });
				}
				render() {
					return this.state.error ? (
						String(this.state.error)
					) : (
						<Foo ref={ref} />
					);
				}
			}

			sinon.spy(Receiver.prototype, 'componentDidCatch');
			render(<Receiver />, scratch);
			expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
		});

		it('should be called when applying a DOM ref', () => {
			const ref = value => {
				if (value) {
					throwExpectedError();
				}
			};

			// In React, an error boundary handles it's own refs:
			// https://codesandbox.io/s/react-throwing-refs-lk958
			class Receiver extends Component {
				componentDidCatch(error) {
					this.setState({ error });
				}
				render() {
					return this.state.error ? (
						String(this.state.error)
					) : (

					);
				}
			}

			sinon.spy(Receiver.prototype, 'componentDidCatch');
			render(<Receiver />, scratch);
			expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
		});

		it('should be called when unmounting a ref', () => {
			const ref = value => {
				if (value == null) {
					throwExpectedError();
				}
			};

			ThrowErr.prototype.render = () =>
;

			render(
				<Receiver>
					<ThrowErr />
				</Receiver>,
				scratch
			);
			render(
				<Receiver>

				</Receiver>,
				scratch
			);
			expect(Receiver.prototype.componentDidCatch).to.have.been.calledOnceWith(
				expectedError
			);
		});

		it('should be called when functional child fails', () => {
			function ThrowErr() {
				throwExpectedError();
			}

			render(
				<Receiver>
					<ThrowErr />
				</Receiver>,
				scratch
			);
			expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
		});

		it('should be called when child inside a Fragment fails', () => {
			function ThrowErr() {
				throwExpectedError();
			}

			render(
				<Receiver>
					<Fragment>
						<ThrowErr />
					</Fragment>
				</Receiver>,
				scratch
			);
			expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
		});

		it('should re-render with new content', () => {
			class ThrowErr extends Component {
				componentWillMount() {
					throw new Error('Error contents');
				}
				render() {
					return 'No error!?!?';
				}
			}

			render(
				<Receiver>
					<ThrowErr />
				</Receiver>,
				scratch
			);
			rerender();
			expect(scratch).to.have.property('textContent', 'Error: Error contents');
		});

		it('should be able to adapt and rethrow errors', () => {
			let adaptedError;
			class Adapter extends Component {
				componentDidCatch(error) {
					throw (adaptedError = new Error(
						'Adapted ' +
							String(error && 'message' in error ? error.message : error)
					));
				}
				render() {
					return
{this.props.children}
;
				}
			}

			function ThrowErr() {
				throwExpectedError();
			}

			sinon.spy(Adapter.prototype, 'componentDidCatch');
			render(
				<Receiver>
					<Adapter>
						<ThrowErr />
					</Adapter>
				</Receiver>,
				scratch
			);

			expect(Adapter.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
			expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith(
				adaptedError
			);

			rerender();
			expect(scratch).to.have.property('textContent', 'Error: Adapted Error!');
		});

		it('should bubble on repeated errors', () => {
			class Adapter extends Component {
				componentDidCatch(error) {
					// Try to handle the error
					this.setState({ error });
				}
				render() {
					// But fail at doing so
					if (this.state.error) {
						throw this.state.error;
					}
					return
{this.props.children}
;
				}
			}

			function ThrowErr() {
				throwExpectedError();
			}

			sinon.spy(Adapter.prototype, 'componentDidCatch');

			render(
				<Receiver>
					<Adapter>
						<ThrowErr />
					</Adapter>
				</Receiver>,
				scratch
			);
			rerender();

			expect(Adapter.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
			expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
			expect(scratch).to.have.property('textContent', 'Error: Error!');
		});

		it('should bubble on ignored errors', () => {
			class Adapter extends Component {
				componentDidCatch() {
					// Ignore the error
				}
				render() {
					return
{this.props.children}
;
				}
			}

			function ThrowErr() {
				throw new Error('Error!');
			}

			sinon.spy(Adapter.prototype, 'componentDidCatch');

			render(
				<Receiver>
					<Adapter>
						<ThrowErr />
					</Adapter>
				</Receiver>,
				scratch
			);
			rerender();

			expect(Adapter.prototype.componentDidCatch).to.have.been.called;
			expect(Receiver.prototype.componentDidCatch).to.have.been.called;
			expect(scratch).to.have.property('textContent', 'Error: Error!');
		});

		it('should not bubble on caught errors', () => {
			class TopReceiver extends Component {
				componentDidCatch(error) {
					this.setState({ error });
				}
				render() {
					return (

							{this.state.error
								? String(this.state.error)
								: this.props.children}

					);
				}
			}

			function ThrowErr() {
				throwExpectedError();
			}

			sinon.spy(TopReceiver.prototype, 'componentDidCatch');

			render(
				<TopReceiver>
					<Receiver>
						<ThrowErr />
					</Receiver>
				</TopReceiver>,
				scratch
			);
			rerender();

			expect(TopReceiver.prototype.componentDidCatch).not.to.have.been.called;
			expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
			expect(scratch).to.have.property('textContent', 'Error: Error!');
		});

		it('should be called through non-component parent elements', () => {
			ThrowErr.prototype.render = throwExpectedError;
			render(
				<Receiver>

						<ThrowErr />

				</Receiver>,
				scratch
			);
			expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
		});

		it('should bubble up when ref throws on component that is not an error boundary', () => {
			const ref = value => {
				if (value) {
					throwExpectedError();
				}
			};

			function ThrowErr() {
				return
;
			}

			render(
				<Receiver>
					<ThrowErr />
				</Receiver>,
				scratch
			);
			expect(Receiver.prototype.componentDidCatch).to.have.been.calledWith(
				expectedError
			);
		});

		it.skip('should successfully unmount constantly throwing ref', () => {
			const buggyRef = throwExpectedError;

			function ThrowErr() {
				return
ThrowErr
;
			}

			render(
				<Receiver>
					<ThrowErr />
				</Receiver>,
				scratch
			);
			rerender();

			expect(scratch.innerHTML).to.equal('
Error: Error!
');
		});
	});
});


dom.value = newProps.value == null ? '' : newProps.value;

keep keep keep keep keep replace keep keep keep

newProps.value == null ? '' : newProps.value,

import { setupRerender } from 'preact/test-utils';
import { createElement, render, Component, options } from 'preact';
import {
	setupScratch,
	teardown,
	getMixedArray,
	mixedArrayHTML,
	sortCss,
	serializeHtml,
	supportsPassiveEvents,
	supportsDataList
} from '../_util/helpers';
import { clearLog, getLog, logCall } from '../_util/logCall';

/** @jsx createElement */

function getAttributes(node) {
	let attrs = {};
	for (let i = node.attributes.length; i--; ) {
		attrs[node.attributes[i].name] = node.attributes[i].value;
	}
	return attrs;
}

// hacky normalization of attribute order across browsers.
function sortAttributes(html) {
	return html.replace(
		/<([a-z0-9-]+)((?:\s[a-z0-9:_.-]+=".*?")+)((?:\s*\/)?>)/gi,
		(s, pre, attrs, after) => {
			let list = attrs
				.match(/\s[a-z0-9:_.-]+=".*?"/gi)
				.sort((a, b) => (a > b ? 1 : -1));
			if (~after.indexOf('/')) after = '></' + pre + '>';
			return '<' + pre + list.join('') + after;
		}
	);
}

describe('render()', () => {
	let scratch, rerender;

	beforeEach(() => {
		scratch = setupScratch();
		rerender = setupRerender();
	});

	afterEach(() => {
		teardown(scratch);
	});

	before(() => {
		logCall(Element.prototype, 'appendChild');
		logCall(Element.prototype, 'insertBefore');
		logCall(Element.prototype, 'removeChild');
		logCall(Element.prototype, 'remove');
	});

	it('should render an empty text node given an empty string', () => {
		render('', scratch);
		let c = scratch.childNodes;
		expect(c).to.have.length(1);
		expect(c[0].data).to.equal('');
		expect(c[0].nodeName).to.equal('#text');
	});

	it('should allow node type change with content', () => {
		render(Bad, scratch);
		render(
Good
, scratch);
		expect(scratch.innerHTML).to.eql(`<div>Good</div>`);
	});

	it('should not render when detecting JSON-injection', () => {
		const vnode = JSON.parse('{"type":"span","children":"Malicious"}');
		render(vnode, scratch);
		expect(scratch.firstChild).to.be.null;
	});

	it('should create empty nodes (<* />)', () => {
		render(
, scratch);
		expect(scratch.childNodes).to.have.length(1);
		expect(scratch.childNodes[0].nodeName).to.equal('DIV');

		scratch.parentNode.removeChild(scratch);
		scratch = document.createElement('div');
		(document.body || document.documentElement).appendChild(scratch);

		render(, scratch);
		expect(scratch.childNodes).to.have.length(1);
		expect(scratch.childNodes[0].nodeName).to.equal('SPAN');
	});

	it('should not throw error in IE11 with type date', () => {
		expect(() => render(<input type="date" />, scratch)).to.not.throw();
	});

	it('should support custom tag names', () => {
		render(<foo />, scratch);
		expect(scratch.childNodes).to.have.length(1);
		expect(scratch.firstChild).to.have.property('nodeName', 'FOO');

		scratch.parentNode.removeChild(scratch);
		scratch = document.createElement('div');
		(document.body || document.documentElement).appendChild(scratch);

		render(<x-bar />, scratch);
		expect(scratch.childNodes).to.have.length(1);
		expect(scratch.firstChild).to.have.property('nodeName', 'X-BAR');
	});

	it('should support the form attribute', () => {
		render(

				<form id="myform" />
				<button form="myform">test</button>
				<input form="myform" />

,
			scratch
		);
		const div = scratch.childNodes[0];
		const form = div.childNodes[0];
		const button = div.childNodes[1];
		const input = div.childNodes[2];

		// IE11 doesn't support the form attribute
		if (!/(Trident)/.test(navigator.userAgent)) {
			expect(button).to.have.property('form', form);
			expect(input).to.have.property('form', form);
		}
	});

	it('should allow VNode reuse', () => {
		let reused =
Hello World!
;
		render(

				{reused}

				{reused}

,
			scratch
		);
		expect(scratch.innerHTML).to.eql(
			`<div><div class="reuse">Hello World!</div><span></span><div class="reuse">Hello World!</div></div>`
		);

		render(

				{reused}

,
			scratch
		);
		expect(scratch.innerHTML).to.eql(
			`<div><span></span><div class="reuse">Hello World!</div></div>`
		);
	});

	it('should merge new elements when called multiple times', () => {
		render(
, scratch);
		expect(scratch.childNodes).to.have.length(1);
		expect(scratch.firstChild).to.have.property('nodeName', 'DIV');
		expect(scratch.innerHTML).to.equal('

');

		render(, scratch);
		expect(scratch.childNodes).to.have.length(1);
		expect(scratch.firstChild).to.have.property('nodeName', 'SPAN');
		expect(scratch.innerHTML).to.equal('');

		render(Hello!, scratch);
		expect(scratch.childNodes).to.have.length(1);
		expect(scratch.firstChild).to.have.property('nodeName', 'SPAN');
		expect(scratch.innerHTML).to.equal('Hello!');
	});

	it('should nest empty nodes', () => {
		render(

				<foo />
				<x-bar />

,
			scratch
		);

		expect(scratch.childNodes).to.have.length(1);
		expect(scratch.childNodes[0].nodeName).to.equal('DIV');

		let c = scratch.childNodes[0].childNodes;
		expect(c).to.have.length(3);
		expect(c[0].nodeName).to.equal('SPAN');
		expect(c[1].nodeName).to.equal('FOO');
		expect(c[2].nodeName).to.equal('X-BAR');
	});

	it('should not render falsy values', () => {
		render(

				{null},{undefined},{false},{0},{NaN}

,
			scratch
		);

		expect(scratch.firstChild).to.have.property('innerHTML', ',,,0,NaN');
	});

	it('should not render null', () => {
		render(null, scratch);
		expect(scratch.innerHTML).to.equal('');
		expect(scratch.childNodes).to.have.length(0);
	});

	it('should not render undefined', () => {
		render(undefined, scratch);
		expect(scratch.innerHTML).to.equal('');
		expect(scratch.childNodes).to.have.length(0);
	});

	it('should not render boolean true', () => {
		render(true, scratch);
		expect(scratch.innerHTML).to.equal('');
		expect(scratch.childNodes).to.have.length(0);
	});

	it('should not render boolean false', () => {
		render(false, scratch);
		expect(scratch.innerHTML).to.equal('');
		expect(scratch.childNodes).to.have.length(0);
	});

	it('should not render children when using function children', () => {
		render(
{() => {}}
, scratch);
		expect(scratch.innerHTML).to.equal('

');
	});

	it('should render NaN as text content', () => {
		render(NaN, scratch);
		expect(scratch.innerHTML).to.equal('NaN');
	});

	it('should render numbers (0) as text content', () => {
		render(0, scratch);
		expect(scratch.innerHTML).to.equal('0');
	});

	it('should render numbers (42) as text content', () => {
		render(42, scratch);
		expect(scratch.innerHTML).to.equal('42');
	});

	it('should render strings as text content', () => {
		render('Testing, huh! How is it going?', scratch);
		expect(scratch.innerHTML).to.equal('Testing, huh! How is it going?');
	});

	it('should render arrays of mixed elements', () => {
		render(getMixedArray(), scratch);
		expect(scratch.innerHTML).to.equal(mixedArrayHTML);
	});

	it('should clear falsy attributes', () => {
		render(

,
			scratch
		);

		render(

,
			scratch
		);

		expect(
			getAttributes(scratch.firstChild),
			'from previous truthy values'
		).to.eql({
			a0: '0',
			anan: 'NaN'
		});
	});

	it('should not render falsy attributes on hydrate', () => {
		render(

,
			scratch
		);

		expect(getAttributes(scratch.firstChild), 'initial render').to.eql({
			a0: '0',
			anan: 'NaN'
		});
	});

	it('should clear falsy input values', () => {
		// Note: this test just demonstrates the default browser behavior
		render(

				<input value={0} />
				<input value={false} />
				<input value={null} />
				<input value={undefined} />

,
			scratch
		);

		let root = scratch.firstChild;
		expect(root.children[0]).to.have.property('value', '0');
		expect(root.children[1]).to.have.property('value', 'false');
		expect(root.children[2]).to.have.property('value', '');
		expect(root.children[3]).to.have.property('value', '');
	});

	it('should set value inside the specified range', () => {
		render(
			<input type="range" value={0.5} min="0" max="1" step="0.05" />,
			scratch
		);
		expect(scratch.firstChild.value).to.equal('0.5');
	});

	// IE or IE Edge will throw when attribute values don't conform to the
	// spec. That's the correct behaviour, but bad for this test...
	if (!/(Edge|MSIE|Trident)/.test(navigator.userAgent)) {
		it('should not clear falsy DOM properties', () => {
			function test(val) {
				render(

						<input value={val} />

,
					scratch
				);
			}

			test('2');
			test(false);
			expect(scratch.innerHTML).to.equal(
				'
<input>

',
				'for false'
			);

			test('3');
			test(null);
			expect(scratch.innerHTML).to.equal(
				'
<input>

',
				'for null'
			);

			test('4');
			test(undefined);
			expect(scratch.innerHTML).to.equal(
				'
<input>

',
				'for undefined'
			);
		});
	}

	// Test for preactjs/preact#651
	it('should set enumerable boolean attribute', () => {
		render(<input spellcheck={false} />, scratch);
		expect(scratch.firstChild.spellcheck).to.equal(false);
	});

	it('should not set tagName', () => {
		expect(() => render(<input tagName="div" />, scratch)).not.to.throw();
	});

	it('should apply string attributes', () => {
		render(
, scratch);
		expect(serializeHtml(scratch)).to.equal(
			'

'
		);
	});

	it('should not serialize function props as attributes', () => {
		render(
, scratch);

		let div = scratch.childNodes[0];
		expect(div.attributes.length).to.equal(0);
	});

	it('should serialize object props as attributes', () => {
		render(

,
			scratch
		);

		let div = scratch.childNodes[0];
		expect(div.attributes.length).to.equal(2);

		// Normalize attribute order because it's different in various browsers
		let normalized = {};
		for (let i = 0; i
 {
		render(
, scratch);
		expect(scratch.childNodes[0]).to.have.property('className', 'foo');
	});

	it('should alias className to class', () => {
		render(
, scratch);
		expect(scratch.childNodes[0]).to.have.property('className', 'bar');
	});

	it('should support false aria-* attributes', () => {
		render(
, scratch);
		expect(scratch.firstChild.getAttribute('aria-checked')).to.equal('false');
	});

	it('should support checked on custom elements', () => {
		render(<o-checkbox checked />, scratch);
		expect(scratch.innerHTML).to.equal(
			'<o-checkbox checked="true"></o-checkbox>'
		);
	});

	it('should mask value on password input elements', () => {
		render(<input value="xyz" type="password" />, scratch);
		expect(scratch.innerHTML).to.equal('<input type="password">');
	});

	describe('style attribute', () => {
		it('should apply style as String', () => {
			render(
, scratch);
			expect(scratch.childNodes[0].style.cssText).to.equal(
				'top: 5px; position: relative;'
			);
		});

		it('should not call CSSStyleDeclaration.setProperty for style strings', () => {
			render(
, scratch);
			sinon.stub(scratch.firstChild.style, 'setProperty');
			render(
, scratch);
			expect(scratch.firstChild.style.setProperty).to.not.be.called;
		});

		it('should properly switch from string styles to object styles and back', () => {
			render(
test
, scratch);

			let style = scratch.firstChild.style;
			expect(style.cssText).to.equal('display: inline;');

			render(
, scratch);
			expect(style.cssText).to.equal('color: red;');

			render(
, scratch);
			expect(style.cssText).to.equal('color: blue;');

			render(
, scratch);
			expect(style.cssText).to.equal('color: yellow;');

			render(
, scratch);
			expect(style.cssText).to.equal('display: block;');
		});

		it('should serialize style objects', () => {
			const styleObj = {
				color: 'rgb(255, 255, 255)',
				background: 'rgb(255, 100, 0)',
				backgroundPosition: '10px 10px',
				'background-size': 'cover',
				gridRowStart: 1,
				padding: 5,
				top: 100,
				left: '100%'
			};

			render(
test
, scratch);

			let style = scratch.firstChild.style;
			expect(style.color).to.equal('rgb(255, 255, 255)');
			expect(style.background).to.contain('rgb(255, 100, 0)');
			expect(style.backgroundPosition).to.equal('10px 10px');
			expect(style.backgroundSize).to.equal('cover');
			expect(style.padding).to.equal('5px');
			expect(style.top).to.equal('100px');
			expect(style.left).to.equal('100%');

			// Only check for this in browsers that support css grids
			if (typeof scratch.style.grid == 'string') {
				expect(style.gridRowStart).to.equal('1');
			}
		});

		it('should support opacity 0', () => {
			render(
Test
, scratch);
			let style = scratch.firstChild.style;
			expect(style)
				.to.have.property('opacity')
				.that.equals('1');

			render(
Test
, scratch);
			style = scratch.firstChild.style;
			expect(style)
				.to.have.property('opacity')
				.that.equals('0');
		});

		it('should replace previous style objects', () => {
			render(
test
, scratch);

			let style = scratch.firstChild.style;
			expect(style.cssText).to.equal('display: inline;');
			expect(style)
				.to.have.property('display')
				.that.equals('inline');
			expect(style)
				.to.have.property('color')
				.that.equals('');
			expect(style.zIndex.toString()).to.equal('');

			render(

test
,
				scratch
			);

			style = scratch.firstChild.style;
			expect(style.cssText).to.equal('color: rgb(0, 255, 255); z-index: 3;');
			expect(style)
				.to.have.property('display')
				.that.equals('');
			expect(style)
				.to.have.property('color')
				.that.equals('rgb(0, 255, 255)');

			// IE stores numeric z-index values as a number
			expect(style.zIndex.toString()).to.equal('3');

			render(

					test

,
				scratch
			);

			style = scratch.firstChild.style;
			expect(style.cssText).to.equal(
				'color: rgb(0, 255, 255); display: inline;'
			);
			expect(style)
				.to.have.property('display')
				.that.equals('inline');
			expect(style)
				.to.have.property('color')
				.that.equals('rgb(0, 255, 255)');
			expect(style.zIndex.toString()).to.equal('');
		});

		it('should remove existing attributes', () => {
			const div = document.createElement('div');
			div.setAttribute('class', 'red');
			const span = document.createElement('span');
			const text = document.createTextNode('Hi');

			span.appendChild(text);
			div.appendChild(span);
			scratch.appendChild(div);

			const App = () => (

					Bye

			);

			render(<App />, scratch);
			expect(serializeHtml(scratch)).to.equal('
Bye
');
		});

		it('should remove class attributes', () => {
			const App = props => (

					Bye

			);

			render(<App class="hi" />, scratch);
			expect(scratch.innerHTML).to.equal(
				'
Bye
'
			);

			render(<App />, scratch);
			expect(serializeHtml(scratch)).to.equal('
Bye
');
		});

		it('should remove old styles', () => {
			render(
, scratch);
			render(
, scratch);
			expect(scratch.firstChild.style.color).to.equal('');
			expect(scratch.firstChild.style.backgroundColor).to.equal('blue');
		});

		// Issue #1850
		it('should remove empty styles', () => {
			render(
, scratch);
			expect(scratch.firstChild.style.visibility).to.equal('hidden');
			render(
, scratch);
			expect(scratch.firstChild.style.visibility).to.equal('');
		});

		// Skip test if the currently running browser doesn't support CSS Custom Properties
		if (window.CSS && CSS.supports('color', 'var(--fake-var)')) {
			it('should support css custom properties', () => {
				render(

test
,
					scratch
				);
				expect(sortCss(scratch.firstChild.style.cssText)).to.equal(
					'--foo: red; color: var(--foo);'
				);
				expect(window.getComputedStyle(scratch.firstChild).color).to.equal(
					'rgb(255, 0, 0)'
				);
			});

			it('should not add "px" suffix for custom properties', () => {
				render(

test
,
					scratch
				);
				expect(sortCss(scratch.firstChild.style.cssText)).to.equal(
					'--foo: 100px; width: var(--foo);'
				);
			});

			it('css vars should not be transformed into dash-separated', () => {
				render(

						test

,
					scratch
				);
				expect(sortCss(scratch.firstChild.style.cssText)).to.equal(
					'--foo-baz: 2; --fooBar: 1; opacity: var(--fooBar); z-index: var(--foo-baz);'
				);
			});

			it('should call CSSStyleDeclaration.setProperty for css vars', () => {
				render(
, scratch);
				sinon.stub(scratch.firstChild.style, 'setProperty');
				render(

,
					scratch
				);
				expect(scratch.firstChild.style.setProperty).to.be.calledWith(
					'--foo',
					'10px'
				);
			});
		}
	});

	describe('event handling', () => {
		let proto;

		function fireEvent(on, type) {
			let e = document.createEvent('Event');
			e.initEvent(type, true, true);
			on.dispatchEvent(e);
		}

		beforeEach(() => {
			proto = document.createElement('div').constructor.prototype;

			sinon.spy(proto, 'addEventListener');
			sinon.spy(proto, 'removeEventListener');
		});

		afterEach(() => {
			proto.addEventListener.restore();
			proto.removeEventListener.restore();
		});

		it('should only register on* functions as handlers', () => {
			let click = () => {},
				onclick = () => {};

			render(
, scratch);

			expect(scratch.childNodes[0].attributes.length).to.equal(0);

			expect(
				proto.addEventListener
			).to.have.been.calledOnce.and.to.have.been.calledWithExactly(
				'click',
				sinon.match.func,
				false
			);
		});

		it('should only register truthy values as handlers', () => {
			function fooHandler() {}
			const falsyHandler = false;

			render(
, scratch);

			expect(
				proto.addEventListener
			).to.have.been.calledOnce.and.to.have.been.calledWithExactly(
				'OtherClick',
				sinon.match.func,
				false
			);

			expect(proto.addEventListener).not.to.have.been.calledWith('Click');
			expect(proto.addEventListener).not.to.have.been.calledWith('click');
		});

		it('should support native event names', () => {
			let click = sinon.spy(),
				mousedown = sinon.spy();

			render(
 click(1)} onmousedown={mousedown} />, scratch);

			expect(proto.addEventListener)
				.to.have.been.calledTwice.and.to.have.been.calledWith('click')
				.and.calledWith('mousedown');

			fireEvent(scratch.childNodes[0], 'click');
			expect(click).to.have.been.calledOnce.and.calledWith(1);
		});

		it('should support camel-case event names', () => {
			let click = sinon.spy(),
				mousedown = sinon.spy();

			render(
 click(1)} onMouseDown={mousedown} />, scratch);

			expect(proto.addEventListener)
				.to.have.been.calledTwice.and.to.have.been.calledWith('click')
				.and.calledWith('mousedown');

			fireEvent(scratch.childNodes[0], 'click');
			expect(click).to.have.been.calledOnce.and.calledWith(1);
		});

		it('should update event handlers', () => {
			let click1 = sinon.spy();
			let click2 = sinon.spy();

			render(
, scratch);

			fireEvent(scratch.childNodes[0], 'click');
			expect(click1).to.have.been.calledOnce;
			expect(click2).to.not.have.been.called;

			click1.resetHistory();
			click2.resetHistory();

			render(
, scratch);

			fireEvent(scratch.childNodes[0], 'click');
			expect(click1).to.not.have.been.called;
			expect(click2).to.have.been.called;
		});

		it('should remove event handlers', () => {
			let click = sinon.spy(),
				mousedown = sinon.spy();

			render(
 click(1)} onMouseDown={mousedown} />, scratch);
			render(
 click(2)} />, scratch);

			expect(proto.removeEventListener).to.have.been.calledWith('mousedown');

			fireEvent(scratch.childNodes[0], 'mousedown');
			expect(mousedown).not.to.have.been.called;

			proto.removeEventListener.resetHistory();
			click.resetHistory();
			mousedown.resetHistory();

			render(
, scratch);

			expect(proto.removeEventListener).to.have.been.calledWith('click');

			fireEvent(scratch.childNodes[0], 'click');
			expect(click).not.to.have.been.called;
		});

		it('should register events not appearing on dom nodes', () => {
			let onAnimationEnd = () => {};

			render(
, scratch);
			expect(
				proto.addEventListener
			).to.have.been.calledOnce.and.to.have.been.calledWithExactly(
				'animationend',
				sinon.match.func,
				false
			);
		});

		// Skip test if browser doesn't support passive events
		if (supportsPassiveEvents()) {
			it('should use capturing for event props ending with *Capture', () => {
				let click = sinon.spy(),
					focus = sinon.spy();

				render(

						<button />

,
					scratch
				);

				let root = scratch.firstChild;
				root.firstElementChild.click();
				root.firstElementChild.focus();

				expect(click, 'click').to.have.been.calledOnce;

				// Focus delegation requires a 50b hack I'm not sure we want to incur
				expect(focus, 'focus').to.have.been.calledOnce;

				// IE doesn't set it
				if (!/Edge/.test(navigator.userAgent)) {
					expect(click).to.have.been.calledWithMatch({ eventPhase: 0 }); // capturing
					expect(focus).to.have.been.calledWithMatch({ eventPhase: 0 }); // capturing
				}
			});
		}
	});

	describe('dangerouslySetInnerHTML', () => {
		it('should support dangerouslySetInnerHTML', () => {
			let html = 'foo & bar';
			// eslint-disable-next-line react/no-danger
			render(
, scratch);

			expect(scratch.firstChild, 'set').to.have.property('innerHTML', html);
			expect(scratch.innerHTML).to.equal('
' + html + '
');

			render(

					ab

,
				scratch
			);

			expect(scratch, 'unset').to.have.property(
				'innerHTML',
				`<div>a<strong>b</strong></div>`
			);

			// eslint-disable-next-line react/no-danger
			render(
, scratch);
			expect(scratch.innerHTML, 're-set').to.equal('
' + html + '
');
		});

		it('should apply proper mutation for VNodes with dangerouslySetInnerHTML attr', () => {
			let thing;
			class Thing extends Component {
				constructor(props, context) {
					super(props, context);
					this.state = { html: this.props.html };
					thing = this;
				}
				render(props, { html }) {
					// eslint-disable-next-line react/no-danger
					return html ? (

					) : (

					);
				}
			}

			render(<Thing html="test" />, scratch);
			expect(scratch.innerHTML).to.equal('
test
');

			thing.setState({ html: false });
			rerender();
			expect(scratch.innerHTML).to.equal('

');

			thing.setState({ html: '<foo><bar>test</bar></foo>' });
			rerender();
			expect(scratch.innerHTML).to.equal(
				'
<foo><bar>test</bar></foo>
'
			);
		});

		it('should not hydrate with dangerouslySetInnerHTML', () => {
			let html = 'foo & bar';
			scratch.innerHTML = `<div>${html}</div>`;
			// eslint-disable-next-line react/no-danger
			render(
, scratch);

			expect(scratch.firstChild).to.have.property('innerHTML', html);
			expect(scratch.innerHTML).to.equal(`<div>${html}</div>`);
		});

		it('should avoid reapplying innerHTML when __html property of dangerouslySetInnerHTML attr remains unchanged', () => {
			class Thing extends Component {
				render() {
					// eslint-disable-next-line react/no-danger
					return (
						<div dangerouslySetInnerHTML={{ __html: 'same' }} />
					);
				}
			}

			let thing;
			render(<Thing ref={r => (thing = r)} />, scratch);

			let firstInnerHTMLChild = scratch.firstChild.firstChild;

			// Re-render
			thing.forceUpdate();

			expect(firstInnerHTMLChild).to.equalNode(scratch.firstChild.firstChild);
		});
	});

	it('should reconcile mutated DOM attributes', () => {
		let check = p => render(<input type="checkbox" checked={p} />, scratch),
			value = () => scratch.lastChild.checked,
			setValue = p => (scratch.lastChild.checked = p);
		check(true);
		expect(value()).to.equal(true);
		check(false);
		expect(value()).to.equal(false);
		check(true);
		expect(value()).to.equal(true);
		setValue(true);
		check(false);
		expect(value()).to.equal(false);
		setValue(false);
		check(true);
		expect(value()).to.equal(true);
	});

	it('should reorder child pairs', () => {
		render(

				a
				b

,
			scratch
		);

		let a = scratch.firstChild.firstChild;
		let b = scratch.firstChild.lastChild;

		expect(a).to.have.property('nodeName', 'A');
		expect(b).to.have.property('nodeName', 'B');

		render(

				b
				a

,
			scratch
		);

		expect(scratch.firstChild.firstChild).to.equalNode(b);
		expect(scratch.firstChild.lastChild).to.equalNode(a);
	});

	// Discussion: https://github.com/preactjs/preact/issues/287
	// <datalist> is not supported in Safari, even though the element
	// constructor is present
	if (supportsDataList()) {
		it('should allow <input list /> to pass through as an attribute', () => {
			render(

					<input type="range" min="0" max="100" list="steplist" />
					<datalist id="steplist">
						<option>0</option>
						<option>50</option>
						<option>100</option>
					</datalist>

,
				scratch
			);

			let html = scratch.firstElementChild.firstElementChild.outerHTML;
			expect(sortAttributes(html)).to.equal(
				sortAttributes('<input type="range" min="0" max="100" list="steplist">')
			);
		});
	}

	// Issue #2284
	it('should not throw when setting size to an invalid value', () => {
		// These values are usually used to reset the `size` attribute to its
		// initial state.
		expect(() => render(<input size={undefined} />, scratch)).to.not.throw();
		expect(() => render(<input size={null} />, scratch)).to.not.throw();
		expect(() => render(<input size={0} />, scratch)).to.not.throw();
	});

	it('should not execute append operation when child is at last', () => {
		// See preactjs/preact#717 for discussion about the issue this addresses

		let todoText = 'new todo that I should complete';
		let input;
		let setText;
		let addTodo;

		const ENTER = 13;

		class TodoList extends Component {
			constructor(props) {
				super(props);
				this.state = { todos: [], text: '' };
				setText = this.setText = this.setText.bind(this);
				addTodo = this.addTodo = this.addTodo.bind(this);
			}
			setText(e) {
				this.setState({ text: e.target.value });
			}
			addTodo(e) {
				if (e.keyCode === ENTER) {
					let { todos, text } = this.state;
					todos = todos.concat({ text });
					this.setState({ todos, text: '' });
				}
			}
			render() {
				const { todos, text } = this.state;
				return (

						{todos.map(todo => [
							{todo.text},

								{' '}
								[ Delete ]
							,

						])}
						<input value={text} onInput={this.setText} ref={i => (input = i)} />

				);
			}
		}

		render(<TodoList />, scratch);

		// Simulate user typing
		input.focus();
		input.value = todoText;
		setText({
			target: input
		});

		// Commit the user typing setState call
		rerender();

		// Simulate user pressing enter
		addTodo({
			keyCode: ENTER
		});

		// Before Preact rerenders, focus should be on the input
		expect(document.activeElement).to.equalNode(input);

		rerender();

		// After Preact rerenders, focus should remain on the input
		expect(document.activeElement).to.equalNode(input);
		expect(scratch.innerHTML).to.contain(`<span>${todoText}</span>`);
	});

	it('should keep value of uncontrolled inputs', () => {
		render(<input value={undefined} />, scratch);
		scratch.firstChild.value = 'foo';
		render(<input value={undefined} />, scratch);
		expect(scratch.firstChild.value).to.equal('foo');
	});

	it('should keep value of uncontrolled checkboxes', () => {
		render(<input type="checkbox" checked={undefined} />, scratch);
		scratch.firstChild.checked = true;
		render(<input type="checkbox" checked={undefined} />, scratch);
		expect(scratch.firstChild.checked).to.equal(true);
	});

	it('should always diff `checked` and `value` properties against the DOM', () => {
		// See https://github.com/preactjs/preact/issues/1324

		let inputs;
		let text;
		let checkbox;

		class Inputs extends Component {
			render() {
				return (

						<input value={'Hello'} ref={el => (text = el)} />
						<input type="checkbox" checked ref={el => (checkbox = el)} />

				);
			}
		}

		render(<Inputs ref={x => (inputs = x)} />, scratch);

		expect(text.value).to.equal('Hello');
		expect(checkbox.checked).to.equal(true);

		text.value = 'World';
		checkbox.checked = false;

		inputs.forceUpdate();
		rerender();

		expect(text.value).to.equal('Hello');
		expect(checkbox.checked).to.equal(true);
	});

	it('should not re-render when a component returns undefined', () => {
		let Dialog = () => undefined;
		let updateState;
		class App extends Component {
			constructor(props) {
				super(props);
				this.state = { name: '' };
				updateState = () => this.setState({ name: ', friend' });
			}

			render(props, { name }) {
				return (

						<Dialog />

Hi{name}

				);
			}
		}

		render(<App />, scratch);
		clearLog();

		updateState();
		rerender();

		// We don't log text updates
		expect(getLog()).to.deep.equal([]);
	});

	it('should not lead to stale DOM nodes', () => {
		let i = 0;
		let updateApp;
		class App extends Component {
			render() {
				updateApp = () => this.forceUpdate();
				return <Parent />;
			}
		}

		let updateParent;
		function Parent() {
			updateParent = () => this.forceUpdate();
			i++;
			return <Child i={i} />;
		}

		function Child({ i }) {
			return i < 3 ? null :
foo
;
		}

		render(<App />, scratch);

		updateApp();
		rerender();
		updateParent();
		rerender();
		updateApp();
		rerender();

		// Without a fix it would render: `<div>foo</div><div></div>`
		expect(scratch.innerHTML).to.equal('
foo
');
	});

	// see preact/#1327
	it('should not reuse unkeyed components', () => {
		class X extends Component {
			constructor() {
				super();
				this.state = { i: 0 };
			}

			update() {
				this.setState(prev => ({ i: prev.i + 1 }));
			}

			componentWillUnmount() {
				clearTimeout(this.id);
			}

			render() {
				return
{this.state.i}
;
			}
		}

		let ref;
		let updateApp;
		class App extends Component {
			constructor() {
				super();
				this.state = { i: 0 };
				updateApp = () => this.setState(prev => ({ i: prev.i ^ 1 }));
			}

			render() {
				return (

						{this.state.i === 0 && <X />}
						<X ref={node => (ref = node)} />

				);
			}
		}

		render(<App />, scratch);
		expect(scratch.textContent).to.equal('00');

		ref.update();
		updateApp();
		rerender();
		expect(scratch.textContent).to.equal('1');

		updateApp();
		rerender();

		expect(scratch.textContent).to.equal('01');
	});

	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(<MyComponent />, 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(<App i={2} />, scratch);
		expect(scratch.innerHTML).to.equal('
2
');

		render(<App i={3} />, scratch, scratch.firstChild);
		expect(scratch.innerHTML).to.equal('
3
');
	});

	it('should not cause infinite loop with referentially equal props', () => {
		let i = 0;
		let prevDiff = options._diff;
		options._diff = () => {
			if (++i > 10) {
				options._diff = prevDiff;
				throw new Error('Infinite loop');
			}
		};

		function App({ children, ...rest }) {
			return (

{children}

			);
		}

		render(<App>10</App>, scratch);
		expect(scratch.textContent).to.equal('10');
		options._diff = prevDiff;
	});

	describe('subsequent replaces', () => {
		it("shouldn't remove elements", () => {
			const placeholder = document.createElement('div');
			scratch.appendChild(placeholder);
			const App = () => (

					New content
					<button>Update</button>

			);

			render(<App />, scratch, placeholder);
			expect(scratch.innerHTML).to.equal(
				'
New content<button>Update</button>
'
			);

			render(<App />, scratch, placeholder);
			expect(scratch.innerHTML).to.equal(
				'
New content<button>Update</button>
'
			);
		});

		it('should remove redundant elements', () => {
			const placeholder = document.createElement('div');
			scratch.appendChild(placeholder);
			const App = () => (

					New content
					<button>Update</button>

			);

			render(<App />, scratch, placeholder);
			expect(scratch.innerHTML).to.equal(
				'
New content<button>Update</button>
'
			);

			placeholder.appendChild(document.createElement('span'));
			expect(scratch.innerHTML).to.equal(
				'
New content<button>Update</button>
'
			);

			render(<App />, scratch, placeholder);
			expect(scratch.innerHTML).to.equal(
				'
New content<button>Update</button>
'
			);
		});
	});

	describe('replaceNode parameter', () => {
		function appendChildToScratch(id) {
			const child = document.createElement('div');
			child.id = id;
			scratch.appendChild(child);
		}

		beforeEach(() => {
			['a', 'b', 'c'].forEach(id => appendChildToScratch(id));
		});

		it('should use replaceNode as render root and not inject into it', () => {
			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', () => {
			const childA = scratch.querySelector('#a');
			render(
, scratch, childA);
			expect(scratch.innerHTML).to.equal(
				'

'
			);
		});

		it('should notice prop changes on replaceNode', () => {
			const childA = scratch.querySelector('#a');
			render(
, scratch, childA);
			expect(sortAttributes(String(scratch.innerHTML))).to.equal(
				sortAttributes(
					'

'
				)
			);
		});

		it('should unmount existing components', () => {
			const newScratch = setupScratch();
			const unmount = sinon.spy();
			const mount = sinon.spy();
			class App extends Component {
				componentDidMount() {
					mount();
				}

				componentWillUnmount() {
					unmount();
				}

				render() {
					return
App
;
				}
			}
			render(

					<App />

,
				newScratch
			);
			expect(newScratch.innerHTML).to.equal('

App

');
			expect(mount).to.be.calledOnce;
			render(
new
, newScratch, newScratch.querySelector('#a'));
			expect(newScratch.innerHTML).to.equal('
new
');
			expect(unmount).to.be.calledOnce;

			newScratch.parentNode.removeChild(newScratch);
		});

		it('should unmount existing components in prerendered HTML', () => {
			const newScratch = setupScratch();
			const unmount = sinon.spy();
			const mount = sinon.spy();
			class App extends Component {
				componentDidMount() {
					mount();
				}

				componentWillUnmount() {
					unmount();
				}

				render() {
					return App;
				}
			}

			newScratch.innerHTML = `<div id="child"></div>`;

			const childContainer = newScratch.querySelector('#child');

			render(<App />, childContainer);
			expect(serializeHtml(childContainer)).to.equal('App');
			expect(mount).to.be.calledOnce;
			render(
, newScratch, newScratch.firstElementChild);
			expect(serializeHtml(newScratch)).to.equal('

');
			expect(unmount).to.be.calledOnce;

			newScratch.parentNode.removeChild(newScratch);
		});

		it('should render multiple render roots in one parentDom', () => {
			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 not call options.debounceRendering unnecessarily', () => {
		let comp;

		class A extends Component {
			constructor(props) {
				super(props);
				this.state = { updates: 0 };
				comp = this;
			}

			render() {
				return
{this.state.updates}
;
			}
		}

		render(, scratch);
		expect(scratch.innerHTML).to.equal('
0
');

		const sandbox = sinon.createSandbox();
		try {
			sandbox.spy(options, 'debounceRendering');

			comp.setState({ updates: 1 }, () => {
				comp.setState({ updates: 2 });
			});
			rerender();
			expect(scratch.innerHTML).to.equal('
2
');

			expect(options.debounceRendering).to.have.been.calledOnce;
		} finally {
			sandbox.restore();
		}
	});

	it('should unmount dangerouslySetInnerHTML', () => {
		let set;

		const TextDiv = () => (

some text

		);

		class App extends Component {
			constructor(props) {
				super(props);
				set = this.setState.bind(this);
				this.state = { show: true };
			}

			render() {
				return this.state.show && <TextDiv />;
			}
		}

		render(<App />, scratch);
		expect(scratch.innerHTML).to.equal('

');

		set({ show: false });
		rerender();
		expect(scratch.innerHTML).to.equal('');
	});
});


import { PreactContext, Ref as PreactRef } from '../..';

type Inputs = ReadonlyArray<unknown>;

export type StateUpdater<S> = (value: S | ((prevState: S) => S)) => void;
/**
 * Returns a stateful value, and a function to update it.
 * @param initialState The initial value (or a function that returns the initial value)
 */
export function useState<S>(initialState: S | (() => S)): [S, StateUpdater<S>];

export type Reducer<S, A> = (prevState: S, action: A) => S;
/**
 * An alternative to `useState`.
 *
 * `useReducer` is usually preferable to `useState` when you have complex state logic that involves
 * multiple sub-values. It also lets you optimize performance for components that trigger deep
 * updates because you can pass `dispatch` down instead of callbacks.
 * @param reducer Given the current state and an action, returns the new state
 * @param initialState The initial value to store as state
 */
export function useReducer<S, A>(
	reducer: Reducer<S, A>,
	initialState: S
): [S, (action: A) => void];

/**
 * An alternative to `useState`.
 *
 * `useReducer` is usually preferable to `useState` when you have complex state logic that involves
 * multiple sub-values. It also lets you optimize performance for components that trigger deep
 * updates because you can pass `dispatch` down instead of callbacks.
 * @param reducer Given the current state and an action, returns the new state
 * @param initialArg The initial argument to pass to the `init` function
 * @param init A function that, given the `initialArg`, returns the initial value to store as state
 */
export function useReducer<S, A, I>(
	reducer: Reducer<S, A>,
	initialArg: I,
	init: (arg: I) => S
): [S, (action: A) => void];

type PropRef<T> = { current?: T };
type Ref<T> = { current: T };

/**
 * `useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument
 * (`initialValue`). The returned object will persist for the full lifetime of the component.
 *
 * Note that `useRef()` is useful for more than the `ref` attribute. It’s handy for keeping any mutable
 * value around similar to how you’d use instance fields in classes.
 */
export function useRef<T>(initialValue: T | null): Ref<T>;

/**
 * `useRef` without an initial value is the special case handling `ref` props.
 * If you want a non prop-based, mutable ref, you can explicitly give it an initial value of undefined/null/etc.
 * You should explicitly set the type parameter for the expected ref value to either a DOM Element like `HTMLInputElement` or a `Component`
 */
export function useRef<T = unknown>(): PropRef<T>;

type EffectCallback = () => void | (() => void);
/**
 * Accepts a function that contains imperative, possibly effectful code.
 * The effects run after browser paint, without blocking it.
 *
 * @param effect Imperative function that can return a cleanup function
 * @param inputs If present, effect will only activate if the values in the list change (using ===).
 */
export function useEffect(effect: EffectCallback, inputs?: Inputs): void;

type CreateHandle = () => object;

/**
 * @param ref The ref that will be mutated
 * @param create The function that will be executed to get the value that will be attached to
 * ref.current
 * @param inputs If present, effect will only activate if the values in the list change (using ===).
 */
export function useImperativeHandle<T, R extends T>(
	ref: PreactRef<T>,
	create: () => R,
	inputs?: Inputs
): void;

/**
 * Accepts a function that contains imperative, possibly effectful code.
 * Use this to read layout from the DOM and synchronously re-render.
 * Updates scheduled inside `useLayoutEffect` will be flushed synchronously, after all DOM mutations but before the browser has a chance to paint.
 * Prefer the standard `useEffect` hook when possible to avoid blocking visual updates.
 *
 * @param effect Imperative function that can return a cleanup function
 * @param inputs If present, effect will only activate if the values in the list change (using ===).
 */
export function useLayoutEffect(effect: EffectCallback, inputs?: Inputs): void;

/**
 * Returns a memoized version of the callback that only changes if one of the `inputs`
 * has changed (using ===).
 */
export function useCallback<T extends Function>(callback: T, inputs: Inputs): T;

/**
 * Pass a factory function and an array of inputs.
 * useMemo will only recompute the memoized value when one of the inputs has changed.
 * This optimization helps to avoid expensive calculations on every render.
 * If no array is provided, a new value will be computed whenever a new function instance is passed as the first argument.
 */
// for `inputs`, allow undefined, but don't make it optional as that is very likely a mistake
export function useMemo<T>(factory: () => T, inputs: Inputs | undefined): T;

/**
 * Returns the current context value, as given by the nearest context provider for the given context.
 * When the provider updates, this Hook will trigger a rerender with the latest context value.
 *
 * @param context The context you want to use
 */
export function useContext<T>(context: PreactContext<T>): T;

/**
 * Customize the displayed value in the devtools panel.
 *
 * @param value Custom hook name or object that is passed to formatter
 * @param formatter Formatter to modify value before sending it to the devtools
 */
export function useDebugValue<T>(
	value: T,
	formatter?: (value: T) => string | number
): void;

export function useErrorBoundary(
	callback?: (error: any) => Promise<void> | void
): [any, () => void];


import { createElement, Component } from 'preact';

function createItems(count = 10) {
	let items = [];
	for (let i = 0; i < count; i++) {
		items.push({
			label: `Item #${i + 1}`,
			key: i + 1
		});
	}
	return items;
}

function random() {
	return Math.random() < 0.5 ? 1 : -1;
}

export default class Reorder extends Component {
	state = {
		items: createItems(),
		count: 1,
		useKeys: false
	};

	shuffle = () => {
		this.setState({ items: this.state.items.slice().sort(random) });
	};

	swapTwo = () => {
		let items = this.state.items.slice(),
			first = Math.floor(Math.random() * items.length),
			second;
		do {
			second = Math.floor(Math.random() * items.length);
		} while (second === first);
		let other = items[first];
		items[first] = items[second];
		items[second] = other;
		this.setState({ items });
	};

	reverse = () => {
		this.setState({ items: this.state.items.slice().reverse() });
	};

	setCount = e => {
		this.setState({ count: Math.round(e.target.value) });
	};

	rotate = () => {
		let { items, count } = this.state;
		items = items.slice(count).concat(items.slice(0, count));
		this.setState({ items });
	};

	rotateBackward = () => {
		let { items, count } = this.state,
			len = items.length;
		items = items.slice(len - count, len).concat(items.slice(0, len - count));
		this.setState({ items });
	};

	toggleKeys = () => {
		this.setState({ useKeys: !this.state.useKeys });
	};

	renderItem = item => (

{item.label}

	);

	render({}, { items, count, useKeys }) {
		return (

					<button onClick={this.shuffle}>Shuffle</button>
					<button onClick={this.swapTwo}>Swap Two</button>
					<button onClick={this.reverse}>Reverse</button>
					<button onClick={this.rotate}>Rotate</button>
					<button onClick={this.rotateBackward}>Rotate Backward</button>

						<input
							type="checkbox"
							onClick={this.toggleKeys}
							checked={useKeys}
						/>{' '}
						use keys?

						<input
							type="number"
							step="1"
							min="1"
							style={{ width: '3em' }}
							onInput={this.setCount}
							value={count}
						/>{' '}
						count

{items.map(this.renderItem)}

		);
	}
}


export { render, hydrate } from './render';
export {
	createElement,
	createElement as h,
	Fragment,
	createRef,
	isValidElement
} from './create-element';
export { Component } from './component';
export { cloneElement } from './clone-element';
export { createContext } from './create-context';
export { toChildArray } from './diff/children';
export { unmount as _unmount } from './diff';
export { default as options } from './options';


newProps.checked !== undefined &&
newProps.checked !== dom.checked
) {
dom.checked = newProps.checked;
}
}
}

setProperty(dom, 'checked', newProps.checked, oldProps.checked, false);


import { options, Fragment } from 'preact';

/**
 * Get human readable name of the component/dom node
 * @param {import('./internal').VNode} vnode
 * @param {import('./internal').VNode} vnode
 * @returns {string}
 */
export function getDisplayName(vnode) {
	if (vnode.type === Fragment) {
		return 'Fragment';
	} else if (typeof vnode.type == 'function') {
		return vnode.type.displayName || vnode.type.name;
	} else if (typeof vnode.type == 'string') {
		return vnode.type;
	}

	return '#text';
}

/**
 * Used to keep track of the currently rendered `vnode` and print it
 * in debug messages.
 */
let renderStack = [];

/**
 * Keep track of the current owners. An owner describes a component
 * which was responsible to render a specific `vnode`. This exclude
 * children that are passed via `props.children`, because they belong
 * to the parent owner.
 *
 * ```jsx
 * const Foo = props => <div>{props.children}</div> // div's owner is Foo
 * const Bar = props => {
 *   return (
 *     <Foo><span /></Foo> // Foo's owner is Bar, span's owner is Bar
 *   )
 * }
 * ```
 *
 * Note: A `vnode` may be hoisted to the root scope due to compiler
 * optimiztions. In these cases the `_owner` will be different.
 */
let ownerStack = [];

/**
 * Get the currently rendered `vnode`
 * @returns {import('./internal').VNode | null}
 */
export function getCurrentVNode() {
	return renderStack.length > 0 ? renderStack[renderStack.length - 1] : null;
}

/**
 * If the user doesn't have `@babel/plugin-transform-react-jsx-source`
 * somewhere in his tool chain we can't print the filename and source
 * location of a component. In that case we just omit that, but we'll
 * print a helpful message to the console, notifying the user of it.
 */
let hasBabelPlugin = false;

/**
 * Check if a `vnode` is a possible owner.
 * @param {import('./internal').VNode} vnode
 */
function isPossibleOwner(vnode) {
	return typeof vnode.type == 'function' && vnode.type != Fragment;
}

/**
 * Return the component stack that was captured up to this point.
 * @param {import('./internal').VNode} vnode
 * @returns {string}
 */
export function getOwnerStack(vnode) {
	const stack = [vnode];
	let next = vnode;
	while (next._owner != null) {
		stack.push(next._owner);
		next = next._owner;
	}

	return stack.reduce((acc, owner) => {
		acc += `  in ${getDisplayName(owner)}`;

		const source = owner.__source;
		if (source) {
			acc += ` (at ${source.fileName}:${source.lineNumber})`;
		} else if (!hasBabelPlugin) {
			hasBabelPlugin = true;
			console.warn(
				'Add @babel/plugin-transform-react-jsx-source to get a more detailed component stack. Note that you should not add it to production builds of your App for bundle size reasons.'
			);
		}

		return (acc += '\n');
	}, '');
}

/**
 * Setup code to capture the component trace while rendering. Note that
 * we cannot simply traverse `vnode._parent` upwards, because we have some
 * debug messages for `this.setState` where the `vnode` is `undefined`.
 */
export function setupComponentStack() {
	let oldDiff = options._diff;
	let oldDiffed = options.diffed;
	let oldRoot = options._root;
	let oldVNode = options.vnode;
	let oldRender = options._render;

	options.diffed = vnode => {
		if (isPossibleOwner(vnode)) {
			ownerStack.pop();
		}
		renderStack.pop();
		if (oldDiffed) oldDiffed(vnode);
	};

	options._diff = vnode => {
		if (isPossibleOwner(vnode)) {
			renderStack.push(vnode);
		}
		if (oldDiff) oldDiff(vnode);
	};

	options._root = (vnode, parent) => {
		ownerStack = [];
		if (oldRoot) oldRoot(vnode, parent);
	};

	options.vnode = vnode => {
		vnode._owner =
			ownerStack.length > 0 ? ownerStack[ownerStack.length - 1] : null;
		if (oldVNode) oldVNode(vnode);
	};

	options._render = vnode => {
		if (isPossibleOwner(vnode)) {
			ownerStack.push(vnode);
		}

		if (oldRender) oldRender(vnode);
	};
}


# These are supported funding model platforms

github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: preact
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
custom: # Replace with a single custom sponsorship URL


it('should support checked on custom elements', () => {

render(<o-checkbox checked />, scratch);

keep keep keep replace keep keep keep keep

it('should set checked attribute on custom elements without checked property', () => {


import { checkPropTypes } from './check-props';
import { options, Component } from 'preact';
import {
	ELEMENT_NODE,
	DOCUMENT_NODE,
	DOCUMENT_FRAGMENT_NODE
} from './constants';
import {
	getOwnerStack,
	setupComponentStack,
	getCurrentVNode,
	getDisplayName
} from './component-stack';

const isWeakMapSupported = typeof WeakMap == 'function';

function getClosestDomNodeParent(parent) {
	if (!parent) return {};
	if (typeof parent.type == 'function') {
		return getClosestDomNodeParent(parent._parent);
	}
	return parent;
}

export function initDebug() {
	setupComponentStack();

	/* eslint-disable no-console */
	let oldBeforeDiff = options._diff;
	let oldDiffed = options.diffed;
	let oldVnode = options.vnode;
	let oldCatchError = options._catchError;
	let oldRoot = options._root;
	let oldHook = options._hook;
	const warnedComponents = !isWeakMapSupported
		? null
		: {
				useEffect: new WeakMap(),
				useLayoutEffect: new WeakMap(),
				lazyPropTypes: new WeakMap()
		  };

	options._catchError = (error, vnode, oldVNode) => {
		let component = vnode && vnode._component;
		if (component && typeof error.then == 'function') {
			const promise = error;
			error = new Error(
				`Missing Suspense. The throwing component was: ${getDisplayName(vnode)}`
			);

			let parent = vnode;
			for (; parent; parent = parent._parent) {
				if (parent._component && parent._component._childDidSuspend) {
					error = promise;
					break;
				}
			}

			// We haven't recovered and we know at this point that there is no
			// Suspense component higher up in the tree
			if (error instanceof Error) {
				throw error;
			}
		}

		oldCatchError(error, vnode, oldVNode);
	};

	options._root = (vnode, parentNode) => {
		if (!parentNode) {
			throw new Error(
				'Undefined parent passed to render(), this is the second argument.\n' +
					'Check if the element is available in the DOM/has the correct id.'
			);
		}
		let isValid;
		switch (parentNode.nodeType) {
			case ELEMENT_NODE:
			case DOCUMENT_FRAGMENT_NODE:
			case DOCUMENT_NODE:
				isValid = true;
				break;
			default:
				isValid = false;
		}

		if (!isValid) {
			let componentName = getDisplayName(vnode);
			throw new Error(
				`Expected a valid HTML node as a second argument to render.	Received ${parentNode} instead: render(<${componentName} />, ${parentNode});`
			);
		}

		if (oldRoot) oldRoot(vnode, parentNode);
	};

	options._diff = vnode => {
		let { type, _parent: parent } = vnode;
		let parentVNode = getClosestDomNodeParent(parent);

		if (type === undefined) {
			throw new Error(
				'Undefined component passed to createElement()\n\n' +
					'You likely forgot to export your component or might have mixed up default and named imports' +
					serializeVNode(vnode) +
					`\n\n${getOwnerStack(vnode)}`
			);
		} else if (type != null && typeof type == 'object') {
			if (type._children !== undefined && type._dom !== undefined) {
				throw new Error(
					`Invalid type passed to createElement(): ${type}\n\n` +
						'Did you accidentally pass a JSX literal as JSX twice?\n\n' +
						`  let My${getDisplayName(vnode)} = ${serializeVNode(type)};\n` +
						`  let vnode = <My${getDisplayName(vnode)} />;\n\n` +
						'This usually happens when you export a JSX literal and not the component.' +
						`\n\n${getOwnerStack(vnode)}`
				);
			}

			throw new Error(
				'Invalid type passed to createElement(): ' +
					(Array.isArray(type) ? 'array' : type)
			);
		}

		if (
			(type === 'thead' || type === 'tfoot' || type === 'tbody') &&
			parentVNode.type !== 'table'
		) {
			console.error(
				'Improper nesting of table. Your <thead/tbody/tfoot> should have a <table> parent.' +
					serializeVNode(vnode) +
					`\n\n${getOwnerStack(vnode)}`
			);
		} else if (
			type === 'tr' &&
			parentVNode.type !== 'thead' &&
			parentVNode.type !== 'tfoot' &&
			parentVNode.type !== 'tbody' &&
			parentVNode.type !== 'table'
		) {
			console.error(
				'Improper nesting of table. Your <tr> should have a <thead/tbody/tfoot/table> parent.' +
					serializeVNode(vnode) +
					`\n\n${getOwnerStack(vnode)}`
			);
		} else if (type === 'td' && parentVNode.type !== 'tr') {
			console.error(
				'Improper nesting of table. Your <td> should have a <tr> parent.' +
					serializeVNode(vnode) +
					`\n\n${getOwnerStack(vnode)}`
			);
		} else if (type === 'th' && parentVNode.type !== 'tr') {
			console.error(
				'Improper nesting of table. Your <th> should have a <tr>.' +
					serializeVNode(vnode) +
					`\n\n${getOwnerStack(vnode)}`
			);
		}

		if (
			vnode.ref !== undefined &&
			typeof vnode.ref != 'function' &&
			typeof vnode.ref != 'object' &&
			!('$$typeof' in vnode) // allow string refs when preact-compat is installed
		) {
			throw new Error(
				`Component's "ref" property should be a function, or an object created ` +
					`by createRef(), but got [${typeof vnode.ref}] instead\n` +
					serializeVNode(vnode) +
					`\n\n${getOwnerStack(vnode)}`
			);
		}

		if (typeof vnode.type == 'string') {
			for (const key in vnode.props) {
				if (
					key[0] === 'o' &&
					key[1] === 'n' &&
					typeof vnode.props[key] != 'function' &&
					vnode.props[key] != null
				) {
					throw new Error(
						`Component's "${key}" property should be a function, ` +
							`but got [${typeof vnode.props[key]}] instead\n` +
							serializeVNode(vnode) +
							`\n\n${getOwnerStack(vnode)}`
					);
				}
			}
		}

		// Check prop-types if available
		if (typeof vnode.type == 'function' && vnode.type.propTypes) {
			if (
				vnode.type.displayName === 'Lazy' &&
				warnedComponents &&
				!warnedComponents.lazyPropTypes.has(vnode.type)
			) {
				const m =
					'PropTypes are not supported on lazy(). Use propTypes on the wrapped component itself. ';
				try {
					const lazyVNode = vnode.type();
					warnedComponents.lazyPropTypes.set(vnode.type, true);
					console.warn(
						m + `Component wrapped in lazy() is ${getDisplayName(lazyVNode)}`
					);
				} catch (promise) {
					console.warn(
						m + "We will log the wrapped component's name once it is loaded."
					);
				}
			}
			checkPropTypes(
				vnode.type.propTypes,
				vnode.props,
				'prop',
				getDisplayName(vnode)
			);
		}

		if (oldBeforeDiff) oldBeforeDiff(vnode);
	};

	options._hook = comp => {
		if (!comp) {
			throw new Error('Hook can only be invoked from render methods.');
		}

		if (oldHook) oldHook(comp);
	};

	const warn = (property, err) => ({
		get() {
			throw new Error(`getting vnode.${property} is deprecated, ${err}`);
		},
		set() {
			throw new Error(`setting vnode.${property} is not allowed, ${err}`);
		}
	});

	const deprecatedAttributes = {
		nodeName: warn('nodeName', 'use vnode.type'),
		attributes: warn('attributes', 'use vnode.props'),
		children: warn('children', 'use vnode.props.children')
	};

	const deprecatedProto = Object.create({}, deprecatedAttributes);

	options.vnode = vnode => {
		const props = vnode.props;
		if (
			vnode.type !== null &&
			props != null &&
			('__source' in props || '__self' in props)
		) {
			const newProps = (vnode.props = {});
			for (let i in props) {
				const v = props[i];
				if (i === '__source') vnode.__source = v;
				else if (i === '__self') vnode.__self = v;
				else newProps[i] = v;
			}
		}
		Object.setPrototypeOf(vnode, deprecatedProto);
		if (oldVnode) oldVnode(vnode);
	};

	options.diffed = vnode => {
		// Check if the user passed plain objects as children. Note that we cannot
		// move this check into `options.vnode` because components can receive
		// children in any shape they want (e.g.
		// `<MyJSONFormatter>{{ foo: 123, bar: "abc" }}</MyJSONFormatter>`).
		// Putting this check in `options.diffed` ensures that
		// `vnode._children` is set and that we only validate the children
		// that were actually rendered.
		if (vnode._children) {
			vnode._children.forEach(child => {
				if (child && child.type === undefined) {
					// Remove internal vnode keys that will always be patched
					delete child._parent;
					delete child._depth;
					const keys = Object.keys(child).join(',');
					throw new Error(
						`Objects are not valid as a child. Encountered an object with the keys {${keys}}.` +
							`\n\n${getOwnerStack(vnode)}`
					);
				}
			});
		}

		/** @type {import('./internal').Component} */
		const component = vnode._component;
		if (component && component.__hooks) {
			let hooks = component.__hooks;
			if (Array.isArray(hooks._list)) {
				hooks._list.forEach(hook => {
					if (hook._factory && (!hook._args || !Array.isArray(hook._args))) {
						let componentName = getDisplayName(vnode);
						console.warn(
							`In ${componentName} you are calling useMemo/useCallback without passing arguments.\n` +
								`This is a noop since it will not be able to memoize, it will execute it every render.` +
								`\n\n${getOwnerStack(vnode)}`
						);
					}
				});
			}
		}

		if (oldDiffed) oldDiffed(vnode);

		if (vnode._children != null) {
			const keys = [];
			for (let i = 0; i < vnode._children.length; i++) {
				const child = vnode._children[i];
				if (!child || child.key == null) continue;

				const key = child.key;
				if (keys.indexOf(key) !== -1) {
					console.error(
						'Following component has two or more children with the ' +
							`same key attribute: "${key}". This may cause glitches and misbehavior ` +
							'in rendering process. Component: \n\n' +
							serializeVNode(vnode) +
							`\n\n${getOwnerStack(vnode)}`
					);

					// Break early to not spam the console
					break;
				}

				keys.push(key);
			}
		}
	};
}

const setState = Component.prototype.setState;
Component.prototype.setState = function(update, callback) {
	if (this._vnode == null) {
		// `this._vnode` will be `null` during componentWillMount. But it
		// is perfectly valid to call `setState` during cWM. So we
		// need an additional check to verify that we are dealing with a
		// call inside constructor.
		if (this.state == null) {
			console.warn(
				`Calling "this.setState" inside the constructor of a component is a ` +
					`no-op and might be a bug in your application. Instead, set ` +
					`"this.state = {}" directly.\n\n${getOwnerStack(getCurrentVNode())}`
			);
		}
	} else if (this._parentDom == null) {
		console.warn(
			`Can't call "this.setState" on an unmounted component. This is a no-op, ` +
				`but it indicates a memory leak in your application. To fix, cancel all ` +
				`subscriptions and asynchronous tasks in the componentWillUnmount method.` +
				`\n\n${getOwnerStack(this._vnode)}`
		);
	}

	return setState.call(this, update, callback);
};

const forceUpdate = Component.prototype.forceUpdate;
Component.prototype.forceUpdate = function(callback) {
	if (this._vnode == null) {
		console.warn(
			`Calling "this.forceUpdate" inside the constructor of a component is a ` +
				`no-op and might be a bug in your application.\n\n${getOwnerStack(
					getCurrentVNode()
				)}`
		);
	} else if (this._parentDom == null) {
		console.warn(
			`Can't call "this.forceUpdate" on an unmounted component. This is a no-op, ` +
				`but it indicates a memory leak in your application. To fix, cancel all ` +
				`subscriptions and asynchronous tasks in the componentWillUnmount method.` +
				`\n\n${getOwnerStack(this._vnode)}`
		);
	}
	return forceUpdate.call(this, callback);
};

/**
 * Serialize a vnode tree to a string
 * @param {import('./internal').VNode} vnode
 * @returns {string}
 */
export function serializeVNode(vnode) {
	let { props } = vnode;
	let name = getDisplayName(vnode);

	let attrs = '';
	for (let prop in props) {
		if (props.hasOwnProperty(prop) && prop !== 'children') {
			let value = props[prop];

			// If it is an object but doesn't have toString(), use Object.toString
			if (typeof value == 'function') {
				value = `function ${value.displayName || value.name}() {}`;
			}

			value =
				Object(value) === value && !value.toString
					? Object.prototype.toString.call(value)
					: value + '';

			attrs += ` ${prop}=${JSON.stringify(value)}`;
		}
	}

	let children = props.children;
	return `<${name}${attrs}${
		children && children.length ? '>..</' + name + '>' : ' />'
	}`;
}


/* eslint-disable */
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const preact = path.join(__dirname, '..', 'src');
const compat = path.join(__dirname, '..', 'compat', 'src');

module.exports = {
	context: __dirname,
	entry: './index',
	output: {
		publicPath: '/'
	},
	resolve: {
		alias: {
			['preact/debug']: path.join(__dirname, '..', 'debug'),
			['preact/devtools']: path.join(__dirname, '..', 'devtools'),
			['preact/hooks']: path.join(__dirname, '..', 'hooks', 'src'),
			preact: preact,
			react: compat,
			'react-dom': compat
		},
		extensions: ['.tsx', '.ts', '.js']
	},
	module: {
		rules: [
			{
				test: /\.tsx?$/,
				loader: 'babel-loader',
				options: {
					sourceMap: true,
					presets: [
						[require.resolve('@babel/preset-typescript'), { jsxPragma: 'h' }],
						[
							require.resolve('@babel/preset-env'),
							{
								targets: {
									browsers: ['last 2 versions', 'IE >= 9']
								},
								modules: false,
								loose: true
							}
						],
						[require.resolve('@babel/preset-react')]
					],
					plugins: [
						[require.resolve('@babel/plugin-transform-runtime')],
						[require.resolve('@babel/plugin-transform-react-jsx-source')],
						[
							require.resolve('@babel/plugin-transform-react-jsx'),
							{ pragma: 'h', pragmaFrag: 'Fragment' }
						],
						[
							require.resolve('@babel/plugin-proposal-decorators'),
							{ legacy: true }
						],
						[
							require.resolve('@babel/plugin-proposal-class-properties'),
							{ loose: true }
						]
					]
				}
			},
			{
				test: /\.js$/,
				loader: 'babel-loader',
				options: {
					sourceMap: true,
					presets: [
						[
							require.resolve('@babel/preset-env'),
							{
								targets: {
									browsers: ['last 2 versions', 'IE >= 9']
								},
								modules: false,
								loose: true
							}
						],
						[require.resolve('@babel/preset-react')]
					],
					plugins: [
						[require.resolve('@babel/plugin-transform-react-jsx-source')],
						[
							require.resolve('@babel/plugin-transform-react-jsx'),
							{ pragma: 'createElement', pragmaFrag: 'Fragment' }
						],
						[require.resolve('@babel/plugin-proposal-class-properties')],
						[
							require.resolve('@babel/plugin-transform-react-constant-elements')
						],
						[require.resolve('@babel/plugin-syntax-dynamic-import')]
					]
				}
			},
			{
				test: /\.s?css$/,
				use: ['style-loader', 'css-loader', 'sass-loader']
			}
		]
	},
	devtool: 'inline-source-map',
	node: {
		process: 'mock',
		Buffer: false,
		setImmediate: false
	},
	devServer: {
		historyApiFallback: true
	},
	plugins: [new HtmlWebpackPlugin()]
};


import { interpolateViridis } from 'd3-scale';
import { createElement } from 'preact';

Math.deg = function(radians) {
	return radians * (180 / Math.PI);
};

const memoizedCalc = (function() {
	const memo = {};

	const key = ({ w, heightFactor, lean }) => `${w}-${heightFactor}-${lean}`;

	return args => {
		let memoKey = key(args);

		if (memo[memoKey]) {
			return memo[memoKey];
		}

		let { w, heightFactor, lean } = args;
		let trigH = heightFactor * w;

		let result = {
			nextRight: Math.sqrt(trigH ** 2 + (w * (0.5 + lean)) ** 2),
			nextLeft: Math.sqrt(trigH ** 2 + (w * (0.5 - lean)) ** 2),
			A: Math.deg(Math.atan(trigH / ((0.5 - lean) * w))),
			B: Math.deg(Math.atan(trigH / ((0.5 + lean) * w)))
		};

		memo[memoKey] = result;
		return result;
	};
})();

export default function Pythagoras({
	w,
	x,
	y,
	heightFactor,
	lean,
	left,
	right,
	lvl,
	maxlvl
}) {
	if (lvl >= maxlvl || w < 1) {
		return null;
	}

	const { nextRight, nextLeft, A, B } = memoizedCalc({
		w,
		heightFactor,
		lean
	});

	let rotate = '';

	if (left) {
		rotate = `rotate(${-A} 0 ${w})`;
	} else if (right) {
		rotate = `rotate(${B} ${w} ${w})`;
	}

	return (
		<g transform={`translate(${x} ${y}) ${rotate}`}>
			<rect
				width={w}
				height={w}
				x={0}
				y={0}
				style={{ fill: interpolateViridis(lvl / maxlvl) }}
			/>

			<Pythagoras
				w={nextLeft}
				x={0}
				y={-nextLeft}
				lvl={lvl + 1}
				maxlvl={maxlvl}
				heightFactor={heightFactor}
				lean={lean}
				left
			/>

			<Pythagoras
				w={nextRight}
				x={w - nextRight}
				y={-nextRight}
				lvl={lvl + 1}
				maxlvl={maxlvl}
				heightFactor={heightFactor}
				lean={lean}
				right
			/>
		</g>
	);
}


it('should mask value on password input elements', () => {

render(<input value="xyz" type="password" />, scratch);

keep keep keep keep add keep keep keep keep

it('should set value attribute on custom elements without value property', () => {

render(<o-input value="test" />, scratch);

import { EMPTY_OBJ, EMPTY_ARR } from '../constants';
import { Component } from '../component';
import { Fragment } from '../create-element';
import { diffChildren } from './children';
import { diffProps } from './props';
import { assign, removeNode } from '../util';
import options from '../options';

/**
 * Diff two virtual nodes and apply proper changes to the DOM
 * @param {import('../internal').PreactElement} parentDom The parent of the DOM element
 * @param {import('../internal').VNode} newVNode The new virtual node
 * @param {import('../internal').VNode} oldVNode The old virtual node
 * @param {object} globalContext The current context object. Modified by getChildContext
 * @param {boolean} isSvg Whether or not this element is an SVG node
 * @param {Array<import('../internal').PreactElement>} excessDomChildren
 * @param {Array<import('../internal').Component>} commitQueue List of components
 * which have callbacks to invoke in commitRoot
 * @param {Element | Text} oldDom The current attached DOM
 * element any new dom elements should be placed around. Likely `null` on first
 * render (except when hydrating). Can be a sibling DOM element when diffing
 * Fragments that have siblings. In most cases, it starts out as `oldChildren[0]._dom`.
 * @param {boolean} [isHydrating] Whether or not we are in hydration
 */
export function diff(
	parentDom,
	newVNode,
	oldVNode,
	globalContext,
	isSvg,
	excessDomChildren,
	commitQueue,
	oldDom,
	isHydrating
) {
	let tmp,
		newType = newVNode.type;

	// When passing through createElement it assigns the object
	// constructor as undefined. This to prevent JSON-injection.
	if (newVNode.constructor !== undefined) return null;

	if ((tmp = options._diff)) tmp(newVNode);

	try {
		outer: if (typeof newType == 'function') {
			let c, isNew, oldProps, oldState, snapshot, clearProcessingException;
			let newProps = newVNode.props;

			// Necessary for createContext api. Setting this property will pass
			// the context value as `this.context` just for this component.
			tmp = newType.contextType;
			let provider = tmp && globalContext[tmp._id];
			let componentContext = tmp
				? provider
					? provider.props.value
					: tmp._defaultValue
				: globalContext;

			// Get component and set it to `c`
			if (oldVNode._component) {
				c = newVNode._component = oldVNode._component;
				clearProcessingException = c._processingException = c._pendingError;
			} else {
				// Instantiate the new component
				if ('prototype' in newType && newType.prototype.render) {
					newVNode._component = c = new newType(newProps, componentContext); // eslint-disable-line new-cap
				} else {
					newVNode._component = c = new Component(newProps, componentContext);
					c.constructor = newType;
					c.render = doRender;
				}
				if (provider) provider.sub(c);

				c.props = newProps;
				if (!c.state) c.state = {};
				c.context = componentContext;
				c._globalContext = globalContext;
				isNew = c._dirty = true;
				c._renderCallbacks = [];
			}

			// Invoke getDerivedStateFromProps
			if (c._nextState == null) {
				c._nextState = c.state;
			}
			if (newType.getDerivedStateFromProps != null) {
				if (c._nextState == c.state) {
					c._nextState = assign({}, c._nextState);
				}

				assign(
					c._nextState,
					newType.getDerivedStateFromProps(newProps, c._nextState)
				);
			}

			oldProps = c.props;
			oldState = c.state;

			// Invoke pre-render lifecycle methods
			if (isNew) {
				if (
					newType.getDerivedStateFromProps == null &&
					c.componentWillMount != null
				) {
					c.componentWillMount();
				}

				if (c.componentDidMount != null) {
					c._renderCallbacks.push(c.componentDidMount);
				}
			} else {
				if (
					newType.getDerivedStateFromProps == null &&
					newProps !== oldProps &&
					c.componentWillReceiveProps != null
				) {
					c.componentWillReceiveProps(newProps, componentContext);
				}

				if (
					(!c._force &&
						c.shouldComponentUpdate != null &&
						c.shouldComponentUpdate(
							newProps,
							c._nextState,
							componentContext
						) === false) ||
					(newVNode._original === oldVNode._original && !c._processingException)
				) {
					c.props = newProps;
					c.state = c._nextState;
					// More info about this here: https://gist.github.com/JoviDeCroock/bec5f2ce93544d2e6070ef8e0036e4e8
					if (newVNode._original !== oldVNode._original) c._dirty = false;
					c._vnode = newVNode;
					newVNode._dom = oldVNode._dom;
					newVNode._children = oldVNode._children;
					if (c._renderCallbacks.length) {
						commitQueue.push(c);
					}

					for (tmp = 0; tmp < newVNode._children.length; tmp++) {
						if (newVNode._children[tmp]) {
							newVNode._children[tmp]._parent = newVNode;
						}
					}

					break outer;
				}

				if (c.componentWillUpdate != null) {
					c.componentWillUpdate(newProps, c._nextState, componentContext);
				}

				if (c.componentDidUpdate != null) {
					c._renderCallbacks.push(() => {
						c.componentDidUpdate(oldProps, oldState, snapshot);
					});
				}
			}

			c.context = componentContext;
			c.props = newProps;
			c.state = c._nextState;

			if ((tmp = options._render)) tmp(newVNode);

			c._dirty = false;
			c._vnode = newVNode;
			c._parentDom = parentDom;

			tmp = c.render(c.props, c.state, c.context);
			let isTopLevelFragment =
				tmp != null && tmp.type == Fragment && tmp.key == null;
			newVNode._children = isTopLevelFragment
				? tmp.props.children
				: Array.isArray(tmp)
				? tmp
				: [tmp];

			if (c.getChildContext != null) {
				globalContext = assign(assign({}, globalContext), c.getChildContext());
			}

			if (!isNew && c.getSnapshotBeforeUpdate != null) {
				snapshot = c.getSnapshotBeforeUpdate(oldProps, oldState);
			}

			diffChildren(
				parentDom,
				newVNode,
				oldVNode,
				globalContext,
				isSvg,
				excessDomChildren,
				commitQueue,
				oldDom,
				isHydrating
			);

			c.base = newVNode._dom;

			if (c._renderCallbacks.length) {
				commitQueue.push(c);
			}

			if (clearProcessingException) {
				c._pendingError = c._processingException = null;
			}

			c._force = false;
		} else if (
			excessDomChildren == null &&
			newVNode._original === oldVNode._original
		) {
			newVNode._children = oldVNode._children;
			newVNode._dom = oldVNode._dom;
		} else {
			newVNode._dom = diffElementNodes(
				oldVNode._dom,
				newVNode,
				oldVNode,
				globalContext,
				isSvg,
				excessDomChildren,
				commitQueue,
				isHydrating
			);
		}

		if ((tmp = options.diffed)) tmp(newVNode);
	} catch (e) {
		newVNode._original = null;
		options._catchError(e, newVNode, oldVNode);
	}

	return newVNode._dom;
}

/**
 * @param {Array<import('../internal').Component>} commitQueue List of components
 * which have callbacks to invoke in commitRoot
 * @param {import('../internal').VNode} root
 */
export function commitRoot(commitQueue, root) {
	if (options._commit) options._commit(root, commitQueue);

	commitQueue.some(c => {
		try {
			commitQueue = c._renderCallbacks;
			c._renderCallbacks = [];
			commitQueue.some(cb => {
				cb.call(c);
			});
		} catch (e) {
			options._catchError(e, c._vnode);
		}
	});
}

/**
 * Diff two virtual nodes representing DOM element
 * @param {import('../internal').PreactElement} dom The DOM element representing
 * the virtual nodes being diffed
 * @param {import('../internal').VNode} newVNode The new virtual node
 * @param {import('../internal').VNode} oldVNode The old virtual node
 * @param {object} globalContext The current context object
 * @param {boolean} isSvg Whether or not this DOM node is an SVG node
 * @param {*} excessDomChildren
 * @param {Array<import('../internal').Component>} commitQueue List of components
 * which have callbacks to invoke in commitRoot
 * @param {boolean} isHydrating Whether or not we are in hydration
 * @returns {import('../internal').PreactElement}
 */
function diffElementNodes(
	dom,
	newVNode,
	oldVNode,
	globalContext,
	isSvg,
	excessDomChildren,
	commitQueue,
	isHydrating
) {
	let i;
	let oldProps = oldVNode.props;
	let newProps = newVNode.props;

	// Tracks entering and exiting SVG namespace when descending through the tree.
	isSvg = newVNode.type === 'svg' || isSvg;

	if (excessDomChildren != null) {
		for (i = 0; i < excessDomChildren.length; i++) {
			const child = excessDomChildren[i];

			// if newVNode matches an element in excessDomChildren or the `dom`
			// argument matches an element in excessDomChildren, remove it from
			// excessDomChildren so it isn't later removed in diffChildren
			if (
				child != null &&
				((newVNode.type === null
					? child.nodeType === 3
					: child.localName === newVNode.type) ||
					dom == child)
			) {
				dom = child;
				excessDomChildren[i] = null;
				break;
			}
		}
	}

	if (dom == null) {
		if (newVNode.type === null) {
			return document.createTextNode(newProps);
		}

		dom = isSvg
			? document.createElementNS('http://www.w3.org/2000/svg', newVNode.type)
			: document.createElement(
					newVNode.type,
					newProps.is && { is: newProps.is }
			  );
		// we created a new parent, so none of the previously attached children can be reused:
		excessDomChildren = null;
		// we are creating a new node, so we can assume this is a new subtree (in case we are hydrating), this deopts the hydrate
		isHydrating = false;
	}

	if (newVNode.type === null) {
		if (oldProps !== newProps && dom.data != newProps) {
			dom.data = newProps;
		}
	} else {
		if (excessDomChildren != null) {
			excessDomChildren = EMPTY_ARR.slice.call(dom.childNodes);
		}

		oldProps = oldVNode.props || EMPTY_OBJ;

		let oldHtml = oldProps.dangerouslySetInnerHTML;
		let newHtml = newProps.dangerouslySetInnerHTML;

		// During hydration, props are not diffed at all (including dangerouslySetInnerHTML)
		// @TODO we should warn in debug mode when props don't match here.
		if (!isHydrating) {
			if (oldProps === EMPTY_OBJ) {
				oldProps = {};
				for (let i = 0; i < dom.attributes.length; i++) {
					oldProps[dom.attributes[i].name] = dom.attributes[i].value;
				}
			}

			if (newHtml || oldHtml) {
				// Avoid re-applying the same '__html' if it did not changed between re-render
				if (!newHtml || !oldHtml || newHtml.__html != oldHtml.__html) {
					dom.innerHTML = (newHtml && newHtml.__html) || '';
				}
			}
		}

		diffProps(dom, newProps, oldProps, isSvg, isHydrating);

		// If the new vnode didn't have dangerouslySetInnerHTML, diff its children
		if (newHtml) {
			newVNode._children = [];
		}
		else {
			newVNode._children = newVNode.props.children;
			diffChildren(
				dom,
				newVNode,
				oldVNode,
				globalContext,
				newVNode.type === 'foreignObject' ? false : isSvg,
				excessDomChildren,
				commitQueue,
				EMPTY_OBJ,
				isHydrating
			);
		}

		// (as above, don't diff props during hydration)
		if (!isHydrating) {
			if (
				'value' in newProps &&
				newProps.value !== undefined &&
				newProps.value !== dom.value
			) {
				dom.value = newProps.value == null ? '' : newProps.value;
			}
			if (
				'checked' in newProps &&
				newProps.checked !== undefined &&
				newProps.checked !== dom.checked
			) {
				dom.checked = newProps.checked;
			}
		}
	}

	return dom;
}

/**
 * Invoke or update a ref, depending on whether it is a function or object ref.
 * @param {object|function} ref
 * @param {any} value
 * @param {import('../internal').VNode} vnode
 */
export function applyRef(ref, value, vnode) {
	try {
		if (typeof ref == 'function') ref(value);
		else ref.current = value;
	} catch (e) {
		options._catchError(e, vnode);
	}
}

/**
 * Unmount a virtual node from the tree and apply DOM changes
 * @param {import('../internal').VNode} vnode The virtual node to unmount
 * @param {import('../internal').VNode} parentVNode The parent of the VNode that
 * initiated the unmount
 * @param {boolean} [skipRemove] Flag that indicates that a parent node of the
 * current element is already detached from the DOM.
 */
export function unmount(vnode, parentVNode, skipRemove) {
	let r;
	if (options.unmount) options.unmount(vnode);

	if ((r = vnode.ref)) {
		if (!r.current || r.current === vnode._dom) applyRef(r, null, parentVNode);
	}

	let dom;
	if (!skipRemove && typeof vnode.type != 'function') {
		skipRemove = (dom = vnode._dom) != null;
	}

	// Must be set to `undefined` to properly clean up `_nextDom`
	// for which `null` is a valid value. See comment in `create-element.js`
	vnode._dom = vnode._nextDom = undefined;

	if ((r = vnode._component) != null) {
		if (r.componentWillUnmount) {
			try {
				r.componentWillUnmount();
			} catch (e) {
				options._catchError(e, parentVNode);
			}
		}

		r.base = r._parentDom = null;
	}

	if ((r = vnode._children)) {
		for (let i = 0; i < r.length; i++) {
			if (r[i]) unmount(r[i], parentVNode, skipRemove);
		}
	}

	if (dom != null) removeNode(dom);
}

/** The `.render()` method for a PFC backing instance. */
function doRender(props, state, context) {
	return this.constructor(props, context);
}