Testing Children Utility Implementation in preactjs/preact
This test suite validates the implementation of React’s Children utilities in Preact’s compatibility layer. It ensures proper handling of child components and elements through methods like count, only, map, and forEach.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
preactjs/preact
compat/test/browser/Children.test.js
import {
setupScratch,
teardown,
serializeHtml
} from '../../../test/_util/helpers';
import { div, span } from '../../../test/_util/dom';
import React, { createElement, Children, render } from 'preact/compat';
describe('Children', () => {
/** @type {HTMLDivElement} */
let scratch;
beforeEach(() => {
scratch = setupScratch();
});
afterEach(() => {
teardown(scratch);
});
describe('.count', () => {
let count;
function Foo(props) {
count = Children.count(props.children);
return <div>{count}</div>;
}
it('should return 0 for no children', () => {
render(<Foo />, scratch);
expect(count).to.equal(0);
});
it('should return number of children', () => {
render(
<Foo>
<div />
foo
</Foo>,
scratch
);
expect(count).to.equal(2);
});
});
describe('.only', () => {
let actual;
function Foo(props) {
actual = Children.only(props.children);
return <div>{actual}</div>;
}
it('should only allow 1 child', () => {
render(<Foo>foo</Foo>, scratch);
expect(actual).to.equal('foo');
});
it('should throw if no children are passed', () => {
// eslint-disable-next-line prefer-arrow-callback
expect(function () {
render(<Foo />, scratch);
}).to.throw();
});
it('should throw if more children are passed', () => {
// eslint-disable-next-line prefer-arrow-callback
expect(function () {
render(
<Foo>
foo
<span />
</Foo>,
scratch
);
}).to.throw();
});
});
describe('.map', () => {
function Foo(props) {
let children = Children.map(props.children, child => (
<span>{child}</span>
));
return <div>{children}</div>;
}
it('should iterate over children', () => {
render(
<Foo>
foo<div>bar</div>
</Foo>,
scratch
);
let expected = div([span('foo'), span(div('bar'))]);
expect(serializeHtml(scratch)).to.equal(expected);
});
it('should work with no children', () => {
render(<Foo />, scratch);
expect(serializeHtml(scratch)).to.equal('<div></div>');
});
it('should work with children as zero number', () => {
const testNumber = 0;
render(<Foo>{testNumber}</Foo>, scratch);
expect(serializeHtml(scratch)).to.equal('<div><span>0</span></div>');
});
it('should flatten result', () => {
const ProblemChild = ({ children }) => {
return React.Children.map(children, child => {
return React.Children.map(child.props.children, x => x);
}).filter(React.isValidElement);
};
const App = () => {
return (
<ProblemChild>
<div>
<div>1</div>
<div>2</div>
</div>
</ProblemChild>
);
};
render(<App />, scratch);
expect(scratch.textContent).to.equal('12');
});
it('should call with indices', () => {
const assertion = [];
const ProblemChild = ({ children }) => {
return React.Children.map(children, (child, i) => {
assertion.push(i);
return React.Children.map(child.props.children, (x, j) => {
assertion.push(j);
return x;
});
}).filter(React.isValidElement);
};
const App = () => {
return (
<ProblemChild>
<div>
<div>1</div>
<div>2</div>
</div>
<div>
<div>3</div>
<div>4</div>
</div>
</ProblemChild>
);
};
render(<App />, scratch);
expect(scratch.textContent).to.equal('1234');
expect(assertion.length).to.equal(6);
});
});
describe('.forEach', () => {
function Foo(props) {
let children = [];
Children.forEach(props.children, child =>
children.push(<span>{child}</span>)
);
return <div>{children}</div>;
}
it('should iterate over children', () => {
render(
<Foo>
foo<div>bar</div>
</Foo>,
scratch
);
let expected = div([span('foo'), span(div('bar'))]);
expect(serializeHtml(scratch)).to.equal(expected);
});
});
});