Files
react/packages/react-devtools-shared/src/__tests__/ownersListContext-test.js
Sebastian Markbåge 25c584f567 [DevTools] Further Refactoring of Unmounts (#30658)
Stacked on #30625 and #30657.

This ensures that we only create instances during the commit
reconciliation and that we don't create unnecessary instances for things
that are filtered or not mounted. This ensures that we also can rely on
the reconciliation to do all the clean up. Now everything is created and
deleted as a pair in the same pass.

Previously we were including unfiltered components in the owner stack
which probably doesn't make sense since you're intending to filter them
everywhere presumably. However, it also means that those links were
broken since you can't link into owners that don't exist in the parent
tree.

The main complication is the component filters. It relied on not
unmounting the old instances. I had to update some tests that asserted
on ids that are now shifted.

For warnings/errors tracking I now restore them back into the pending
set when they unmount. Basically it puts them back into their
"pre-commit" state. That way when they remount they’re still there.

For restoring the current selection I use the tracked path mechanism
instead of relying on the id being unchanged. This is better anyway
because if you filter out the currently selected item it's better to
select the nearest match instead of just losing the selection.
2024-08-12 12:41:29 -04:00

215 lines
5.8 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import typeof ReactTestRenderer from 'react-test-renderer';
import type {Element} from 'react-devtools-shared/src/frontend/types';
import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
import type Store from 'react-devtools-shared/src/devtools/store';
import {getVersionedRenderImplementation} from './utils';
describe('OwnersListContext', () => {
let React;
let TestRenderer: ReactTestRenderer;
let bridge: FrontendBridge;
let store: Store;
let utils;
let BridgeContext;
let OwnersListContext;
let OwnersListContextController;
let StoreContext;
let TreeContextController;
beforeEach(() => {
utils = require('./utils');
utils.beforeEachProfiling();
bridge = global.bridge;
store = global.store;
store.collapseNodesByDefault = false;
React = require('react');
TestRenderer = utils.requireTestRenderer();
BridgeContext =
require('react-devtools-shared/src/devtools/views/context').BridgeContext;
OwnersListContext =
require('react-devtools-shared/src/devtools/views/Components/OwnersListContext').OwnersListContext;
OwnersListContextController =
require('react-devtools-shared/src/devtools/views/Components/OwnersListContext').OwnersListContextController;
StoreContext =
require('react-devtools-shared/src/devtools/views/context').StoreContext;
TreeContextController =
require('react-devtools-shared/src/devtools/views/Components/TreeContext').TreeContextController;
});
const {render} = getVersionedRenderImplementation();
const Contexts = ({children, defaultOwnerID = null}) => (
<BridgeContext.Provider value={bridge}>
<StoreContext.Provider value={store}>
<TreeContextController defaultOwnerID={defaultOwnerID}>
<OwnersListContextController>{children}</OwnersListContextController>
</TreeContextController>
</StoreContext.Provider>
</BridgeContext.Provider>
);
async function getOwnersListForOwner(owner) {
let ownerDisplayNames = null;
function Suspender() {
const read = React.useContext(OwnersListContext);
const owners = read(owner.id);
ownerDisplayNames = owners.map(({displayName}) => displayName);
return null;
}
await utils.actAsync(() =>
TestRenderer.create(
<Contexts defaultOwnerID={owner.id}>
<React.Suspense fallback={null}>
<Suspender owner={owner} />
</React.Suspense>
</Contexts>,
),
);
expect(ownerDisplayNames).not.toBeNull();
return ownerDisplayNames;
}
it('should fetch the owners list for the selected element', async () => {
const Grandparent = () => <Parent />;
const Parent = () => {
return (
<React.Fragment>
<Child />
<Child />
</React.Fragment>
);
};
const Child = () => null;
utils.act(() => render(<Grandparent />));
expect(store).toMatchInlineSnapshot(`
[root]
▾ <Grandparent>
▾ <Parent>
<Child>
<Child>
`);
const parent = ((store.getElementAtIndex(1): any): Element);
const firstChild = ((store.getElementAtIndex(2): any): Element);
expect(await getOwnersListForOwner(parent)).toMatchInlineSnapshot(`
[
"Grandparent",
"Parent",
]
`);
expect(await getOwnersListForOwner(firstChild)).toMatchInlineSnapshot(`
[
"Grandparent",
"Parent",
"Child",
]
`);
});
it('should fetch the owners list for the selected element that includes filtered components', async () => {
store.componentFilters = [utils.createDisplayNameFilter('^Parent$')];
const Grandparent = () => <Parent />;
const Parent = () => {
return (
<React.Fragment>
<Child />
<Child />
</React.Fragment>
);
};
const Child = () => null;
utils.act(() => render(<Grandparent />));
expect(store).toMatchInlineSnapshot(`
[root]
▾ <Grandparent>
<Child>
<Child>
`);
const firstChild = ((store.getElementAtIndex(1): any): Element);
expect(await getOwnersListForOwner(firstChild)).toMatchInlineSnapshot(`
[
"Grandparent",
"Child",
]
`);
});
it('should include the current element even if there are no other owners', async () => {
store.componentFilters = [utils.createDisplayNameFilter('^Parent$')];
const Grandparent = () => <Parent />;
const Parent = () => null;
utils.act(() => render(<Grandparent />));
expect(store).toMatchInlineSnapshot(`
[root]
<Grandparent>
`);
const grandparent = ((store.getElementAtIndex(0): any): Element);
expect(await getOwnersListForOwner(grandparent)).toMatchInlineSnapshot(`
[
"Grandparent",
]
`);
});
it('should include all owners for a component wrapped in react memo', async () => {
const InnerComponent = (props, ref) => <div ref={ref} />;
const ForwardRef = React.forwardRef(InnerComponent);
const Memo = React.memo(ForwardRef);
const Grandparent = () => {
const ref = React.createRef();
return <Memo ref={ref} />;
};
utils.act(() => render(<Grandparent />));
expect(store).toMatchInlineSnapshot(`
[root]
▾ <Grandparent>
▾ <InnerComponent> [Memo]
<InnerComponent> [ForwardRef]
`);
const wrapped = ((store.getElementAtIndex(2): any): Element);
expect(await getOwnersListForOwner(wrapped)).toMatchInlineSnapshot(`
[
"Grandparent",
"InnerComponent",
"InnerComponent",
]
`);
});
});