/
githubmirror
/
preact
Обзор
Документация
Войти
/
githubmirror
/
preact
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
hooks/test/browser/useId.test.jsx
478 строк
10 KB
Jovi De Croock
convert to oxfmt
14 июл 2026, 21:48
14 июл 2026, 21:48
041216f
Код
Авторство
О чём код?
import { createElement, Fragment, hydrate, render } from 'preact'; import { useId, useReducer, useState } from 'preact/hooks'; import { setupRerender } from 'preact/test-utils'; import { render as rts } from 'preact-render-to-string'; import { setupScratch, teardown } from '../../../test/_util/helpers'; describe('useId', () => { /** @type {HTMLDivElement} */ let scratch, rerender; beforeEach(() => { scratch = setupScratch(); rerender = setupRerender(); }); afterEach(() => { teardown(scratch); }); it('keeps the id consistent after an update', () => { function Comp() { const id = useId(); return <div id={id} />; } render(<Comp />, scratch); const id = scratch.firstChild.getAttribute('id'); expect(scratch.firstChild.getAttribute('id')).to.equal(id); render(<Comp />, scratch); expect(scratch.firstChild.getAttribute('id')).to.equal(id); }); it('ids are unique according to dom-depth', () => { function Child() { const id = useId(); const spanId = useId(); return ( <div id={id}> <span id={spanId}>h</span> </div> ); } function Comp() { const id = useId(); return ( <div id={id}> <Child /> </div> ); } render(<Comp />, scratch); expect(scratch.innerHTML).to.equal( '<div id="P0-0"><div id="P0-1"><span id="P0-2">h</span></div></div>' ); render(<Comp />, scratch); expect(scratch.innerHTML).to.equal( '<div id="P0-0"><div id="P0-1"><span id="P0-2">h</span></div></div>' ); }); it('ids are unique across siblings', () => { function Child() { const id = useId(); return <span id={id}>h</span>; } function Comp() { const id = useId(); return ( <div id={id}> <Child /> <Child /> <Child /> </div> ); } render(<Comp />, scratch); expect(scratch.innerHTML).to.equal( '<div id="P0-0"><span id="P0-1">h</span><span id="P0-2">h</span><span id="P0-3">h</span></div>' ); render(<Comp />, scratch); expect(scratch.innerHTML).to.equal( '<div id="P0-0"><span id="P0-1">h</span><span id="P0-2">h</span><span id="P0-3">h</span></div>' ); }); it('correctly handles new elements', () => { let set; function Child() { const id = useId(); return <span id={id}>h</span>; } function Stateful() { const [state, setState] = useState(false); set = setState; return ( <div> <Child /> {state && <Child />} </div> ); } function Comp() { const id = useId(); return ( <div id={id}> <Stateful /> </div> ); } render(<Comp />, scratch); expect(scratch.innerHTML).to.equal( '<div id="P0-0"><div><span id="P0-1">h</span></div></div>' ); set(true); rerender(); expect(scratch.innerHTML).to.equal( '<div id="P0-0"><div><span id="P0-1">h</span><span id="P0-2">h</span></div></div>' ); }); it('matches with rts', () => { const ChildFragmentReturn = ({ children }) => { return <Fragment>{children}</Fragment>; }; const ChildReturn = ({ children }) => { return children; }; const SomeMessage = ({ msg }) => { const id = useId(); return ( <p> {msg} {id} </p> ); }; const Stateful = () => { const [count, add] = useReducer(c => c + 1, 0); const id = useId(); return ( <div> id: {id}, count: {count} <button onClick={add}>+1</button> </div> ); }; const Component = ({ showStateful = false }) => { const rootId = useId(); const paragraphId = useId(); return ( <main> ID: {rootId} <p>Hello world id: {paragraphId}</p> {showStateful ? ( <Stateful /> ) : ( <ChildReturn> <SomeMessage msg="child-return" /> <ChildReturn> <SomeMessage msg="child-return" /> <ChildReturn> <SomeMessage msg="child-return" /> </ChildReturn> </ChildReturn> </ChildReturn> )} <ChildFragmentReturn> <SomeMessage msg="child-fragment-return" /> <SomeMessage msg="child-fragment-return-2" /> <SomeMessage msg="child-fragment-return-3" /> <SomeMessage msg="child-fragment-return-4" /> <ChildReturn> <SomeMessage msg="child-return" /> <ChildFragmentReturn> <SomeMessage msg="child-fragment-return" /> </ChildFragmentReturn> </ChildReturn> </ChildFragmentReturn> </main> ); }; const rtsOutput = rts(<Component />); render(<Component />, scratch); expect(rtsOutput === scratch.innerHTML).to.equal(true); }); it('matches with rts after hydration', () => { const ChildFragmentReturn = ({ children }) => { return <Fragment>{children}</Fragment>; }; const ChildReturn = ({ children }) => { return children; }; const SomeMessage = ({ msg }) => { const id = useId(); return ( <p> {msg} {id} </p> ); }; const Stateful = () => { const [count, add] = useReducer(c => c + 1, 0); const id = useId(); return ( <div> id: {id}, count: {count} <button onClick={add}>+1</button> </div> ); }; const Component = ({ showStateful = false }) => { const rootId = useId(); const paragraphId = useId(); return ( <main> ID: {rootId} <p>Hello world id: {paragraphId}</p> {showStateful ? ( <Stateful /> ) : ( <ChildReturn> <SomeMessage msg="child-return" /> <ChildReturn> <SomeMessage msg="child-return" /> <ChildReturn> <SomeMessage msg="child-return" /> </ChildReturn> </ChildReturn> </ChildReturn> )} <ChildFragmentReturn> <SomeMessage msg="child-fragment-return" /> <SomeMessage msg="child-fragment-return-2" /> <SomeMessage msg="child-fragment-return-3" /> <SomeMessage msg="child-fragment-return-4" /> <ChildReturn> <SomeMessage msg="child-return" /> <ChildFragmentReturn> <SomeMessage msg="child-fragment-return" /> </ChildFragmentReturn> </ChildReturn> </ChildFragmentReturn> </main> ); }; const rtsOutput = rts(<Component />); scratch.innerHTML = rtsOutput; hydrate(<Component />, scratch); expect(rtsOutput).to.equal(scratch.innerHTML); }); it('should be unique across Fragments', () => { const ids = []; function Foo() { const id = useId(); ids.push(id); return <p>{id}</p>; } function App() { return ( <div> <Foo /> <Fragment> <Foo /> </Fragment> </div> ); } render(<App />, scratch); expect(ids[0]).not.to.equal(ids[1]); }); it('should match implicite Fragments with RTS', () => { function Foo() { const id = useId(); return <p>{id}</p>; } function Bar(props) { return props.children; } function App() { return ( <Bar> <Foo /> <Fragment> <Foo /> </Fragment> </Bar> ); } const rtsOutput = rts(<App />); scratch.innerHTML = rtsOutput; hydrate(<App />, scratch); expect(rtsOutput).to.equal(scratch.innerHTML); }); it('should skip component top level Fragment child', () => { const Wrapper = ({ children }) => { return <Fragment>{children}</Fragment>; }; const ids = []; function Foo() { const id = useId(); ids.push(id); return <p>{id}</p>; } function App() { const id = useId(); ids.push(id); return ( <div> <p>{id}</p> <Wrapper> <Foo /> </Wrapper> </div> ); } render(<App />, scratch); expect(ids[0]).not.to.equal(ids[1]); }); it('should skip over HTML', () => { const ids = []; function Foo() { const id = useId(); ids.push(id); return <p>{id}</p>; } function App() { return ( <div> <span> <Foo /> </span> <span> <Foo /> </span> </div> ); } render(<App />, scratch); expect(ids[0]).not.to.equal(ids[1]); }); it('should reset for each renderToString roots', () => { const ids = []; function Foo() { const id = useId(); ids.push(id); return <p>{id}</p>; } function App() { return ( <div> <span> <Foo /> </span> <span> <Foo /> </span> </div> ); } const res1 = rts(<App />); const res2 = rts(<App />); expect(res1).to.equal(res2); }); it('should work with conditional components', () => { function Foo() { const id = useId(); return <p>{id}</p>; } function Bar() { const id = useId(); return <p>{id}</p>; } let update; function App() { const [v, setV] = useState(false); update = setV; return <div>{!v ? <Foo /> : <Bar />}</div>; } render(<App />, scratch); const first = scratch.innerHTML; update(v => !v); rerender(); expect(first).not.to.equal(scratch.innerHTML); }); it('should return a unique id across invocations of render', () => { const Id = () => { const id = useId(); return <div>My id is {id}</div>; }; const App = props => { return ( <div> <Id /> {props.secondId ? <Id /> : null} </div> ); }; render(createElement(App, { secondId: false }), scratch); expect(scratch.innerHTML).to.equal('<div><div>My id is P0-0</div></div>'); render(createElement(App, { secondId: true }), scratch); expect(scratch.innerHTML).to.equal( '<div><div>My id is P0-0</div><div>My id is P0-1</div></div>' ); }); it('should not crash for rendering null after a non-null render', () => { const Id = () => { const id = useId(); return <div>My id is {id}</div>; }; const App = props => { return ( <div> <Id /> {props.secondId ? <Id /> : null} </div> ); }; render(createElement(App, { secondId: false }), scratch); expect(scratch.innerHTML).to.equal('<div><div>My id is P0-0</div></div>'); render(null, scratch); expect(scratch.innerHTML).to.equal(''); }); });