Skip to content

Commit

Permalink
added two new tests
Browse files Browse the repository at this point in the history
  • Loading branch information
lunaruan committed Nov 11, 2019
1 parent 83efddd commit 4baef71
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 14 deletions.
6 changes: 4 additions & 2 deletions packages/react-debug-tools/src/ReactDebugHooks.js
Expand Up @@ -265,11 +265,13 @@ function useDeferredValue<T>(value: T, config: TimeoutConfig | null | void): T {

function useUniqueID(): string | IdObject {
const hook = nextHook();
const value = hook === null ? 'FAKE_ID' : hook.memoizedState;
hookLog.push({
primitive: 'UniqueId',
primitive: 'UniqueID',
stackError: new Error(),
value,
});
return hook === null ? 'FAKE_ID' : hook.memoizedState;
return value;
}

const Dispatcher: DispatcherType = {
Expand Down
103 changes: 92 additions & 11 deletions packages/react-dom/src/__tests__/ReactDOMHooks-test.js
Expand Up @@ -109,7 +109,7 @@ describe('ReactDOMHooks', () => {
expect(labelRef.current.innerHTML).toBe('abc');
});

it('useUniqueID does not change id even if the component updates', async () => {
it('useUniqueID does not change id even if the component updates during client render', async () => {
const {act} = TestUtils;
const {useUniqueID, useState} = React;
let _setShowId;
Expand All @@ -130,22 +130,103 @@ describe('ReactDOMHooks', () => {
root.render(<App />);
});

expect(container.childNodes.length).toBe(1);
expect(container.firstChild.childNodes.length).toBe(1);

const id = container.firstChild.firstChild.getAttribute('aria-labelledby');
expect(container.innerHTML).toMatchInlineSnapshot(
`"<div><div aria-labelledby=\\"c_1\\"></div></div>"`,
);

await act(async () => {
_setShowId(true);
});

expect(container.childNodes.length).toBe(1);
expect(container.firstChild.childNodes.length).toBe(2);
expect(container.innerHTML).toMatchInlineSnapshot(
`"<div><div aria-labelledby=\\"c_1\\"></div><div id=\\"c_1\\"></div></div>"`,
);
});

it('useUniqueID: IDs match when, after hydration, a new component that uses the ID is rendered', async () => {
const {act} = TestUtils;
const {useUniqueID, useState} = React;
let _setShowDiv;
function App() {
const id = useUniqueID();
const [showDiv, setShowDiv] = useState(false);
_setShowDiv = setShowDiv;

expect(
container.firstChild.firstChild.getAttribute('aria-labelledby'),
).toEqual(id);
expect(container.firstChild.lastChild.getAttribute('id')).toEqual(id);
return (
<div>
<div id={id}>Child One</div>
{showDiv && <div id={id}>Child Two</div>}
</div>
);
}

container.innerHTML = ReactDOMServer.renderToString(<App />);
const root = ReactDOM.createRoot(container, {hydrate: true});
await act(async () => {
root.render(<App />);
});
expect(container.innerHTML).toMatchInlineSnapshot(
`"<div data-reactroot=\\"\\"><div id=\\"s_0\\">Child One</div></div>"`,
);
await act(async () => {
_setShowDiv(true);
});
expect(container.innerHTML).toMatchInlineSnapshot(
`"<div data-reactroot=\\"\\"><div id=\\"c_0\\">Child One</div><div id=\\"c_0\\">Child Two</div></div>"`,
);
});

it('useUniqueID: IDs match when part of the DOM tree is server rendered and part is client rendered', async () => {
const {useUniqueID} = React;
let suspend = false;
let resolve;
let promise = new Promise(resolvePromise => (resolve = resolvePromise));

function Child({text}) {
if (suspend) {
throw promise;
} else {
return text;
}
}

function App() {
const id = useUniqueID();
return (
<div>
<div id={id}>Child One</div>
<React.Suspense fallback={'Fallback'}>
<div id={id}>
<Child text="Child Two" />
</div>
</React.Suspense>
</div>
);
}

container.innerHTML =
'<div data-reactroot=""><div id="s_0">Child One</div><!--$!--><div>Fallback</div><!--/$--></div>';

suspend = true;
const root = ReactDOM.createRoot(container, {hydrate: true});
root.render(<App />);
Scheduler.unstable_flushAll();
jest.runAllTimers();

expect(container.innerHTML).toMatchInlineSnapshot(
`"<div data-reactroot=\\"\\"><div id=\\"s_0\\">Child One</div>Fallback</div>"`,
);

suspend = false;
resolve();
await promise;

Scheduler.unstable_flushAll();
jest.runAllTimers();

expect(container.innerHTML).toMatchInlineSnapshot(
`"<div data-reactroot=\\"\\"><div id=\\"c_0\\">Child One</div><div id=\\"c_0\\">Child Two</div></div>"`,
);
});

it('useUniqueID throws if you try to use the result as a string', async () => {
Expand Down
6 changes: 5 additions & 1 deletion packages/react-dom/src/client/ToStringValue.js
Expand Up @@ -22,7 +22,11 @@ export opaque type ToStringValue =
// around this limitation, we use an opaque type that can only be obtained by
// passing the value through getToStringValue first.
export function toString(value: ToStringValue): string {
if (value && value.$$typeof === REACT_OPAQUE_OBJECT_TYPE) {
if (
value !== null &&
typeof value === 'object' &&
value.$$typeof === REACT_OPAQUE_OBJECT_TYPE
) {
return value;
}
return '' + (value: any);
Expand Down

0 comments on commit 4baef71

Please sign in to comment.