ice/packages/miniapp-react-dom/tests/unmountComponentAtNode.spec...

120 lines
4.0 KiB
JavaScript

import { expect, describe, test, vi } from 'vitest';
import * as React from 'react';
import { document } from '@ice/miniapp-runtime';
import { render, unmountComponentAtNode, unstable_batchedUpdates as batchedUpdates } from '../src/index';
describe('unmountComponentAtNode', () => {
test('throws when given a non-node', () => {
expect(() => {
unmountComponentAtNode(null);
}).toThrowError(
'unmountComponentAtNode(...): Target container is not a DOM element.',
);
});
test('returns false on non-React containers', () => {
const d = document.createElement('div');
d.id = 'test';
expect(unmountComponentAtNode(d)).toBe(false);
expect(d.id).toBe('test');
});
test('returns true on React containers', () => {
const d = document.createElement('div');
render(<b>hellooo</b>, d);
expect(d.textContent).toBe('hellooo');
expect(unmountComponentAtNode(d)).toBe(true);
expect(d.textContent).toBe('');
});
test('should render different components in same root', () => {
const container = document.createElement('container');
render(<div />, container);
expect(container.firstChild.tagName).toBe('DIV');
render(<span />, container);
expect(container.firstChild.tagName).toBe('SPAN');
});
test('should unmount and remount if the key changes', () => {
const container = document.createElement('container');
const mockMount = vi.fn();
const mockUnmount = vi.fn();
class Component extends React.Component {
componentDidMount = mockMount;
componentWillUnmount = mockUnmount;
render() {
return <span>{this.props.text}</span>;
}
}
expect(mockMount.mock.calls.length).toBe(0);
expect(mockUnmount.mock.calls.length).toBe(0);
render(<Component text="orange" key="A" />, container);
expect(container.firstChild.textContent).toBe('orange');
expect(mockMount.mock.calls.length).toBe(1);
expect(mockUnmount.mock.calls.length).toBe(0);
// If we change the key, the component is unmounted and remounted
render(<Component text="green" key="B" />, container);
expect(container.firstChild.textContent).toBe('green');
expect(mockMount.mock.calls.length).toBe(2);
expect(mockUnmount.mock.calls.length).toBe(1);
// But if we don't change the key, the component instance is reused
render(<Component text="blue" key="B" />, container);
expect(container.firstChild.textContent).toBe('blue');
expect(mockMount.mock.calls.length).toBe(2);
expect(mockUnmount.mock.calls.length).toBe(1);
});
test('should reuse markup if rendering to the same target twice', () => {
const container = document.createElement('container');
const instance1 = render(<div />, container);
const instance2 = render(<div />, container);
expect(instance1 === instance2).toBe(true);
});
test.skip('initial mount is sync inside batchedUpdates, but task work is deferred until the end of the batch', () => {
const container1 = document.createElement('div');
const container2 = document.createElement('div');
class Foo extends React.Component {
state = { active: false };
componentDidMount() {
this.setState({ active: true });
}
render() {
return (
<div>{this.props.children + (this.state.active ? '!' : '')}</div>
);
}
}
render(<Foo>a</Foo>, container2);
batchedUpdates(() => {
// Update. Does not flush yet.
render(<div>2</div>, container1);
expect(container1.textContent).toEqual('1');
// Initial mount on another root. Should flush immediately.
render(<Foo>a</Foo>, container2);
// The update did not flush yet.
expect(container1.textContent).toEqual('1');
// The initial mount flushed, but not the update scheduled in cDU.
expect(container2.textContent).toEqual('a');
});
// All updates have flushed.
expect(container1.textContent).toEqual('2');
expect(container2.textContent).toEqual('a!');
});
});