Skip to content

Commit 18d8faa

Browse files
committed
[react-devtools-facade] multi-root and multi-renderer tests
Adds cross-cutting tests that exercise the tools across multiple React roots: - inject() registers additional renderers and initializes their internals - getComponentTree and findComponents aggregate across roots with globally unique labels - getComponentByLabel resolves labels from any root - profiling records one commit per root with correctly attributed components Completes the react-devtools-facade building-block package.
1 parent 873639a commit 18d8faa

1 file changed

Lines changed: 173 additions & 0 deletions

File tree

packages/react-devtools-facade/src/__tests__/DevToolsFacade-test.js

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1891,4 +1891,177 @@ describe('react-devtools-facade', () => {
18911891
}).not.toThrow();
18921892
});
18931893
});
1894+
1895+
describe('multiple roots and renderers', () => {
1896+
it('inject() registers a new renderer and initializes its internals', () => {
1897+
// react-dom registered itself as a renderer when it was required. Here we
1898+
// register a second (simulated) renderer to cover the registration
1899+
// contract; the cross-root tests below use a single react-dom renderer.
1900+
const before = facade.hook.renderers.size;
1901+
const id = facade.hook.inject({
1902+
reconcilerVersion: '18.2.0',
1903+
version: '18.2.0',
1904+
});
1905+
expect(typeof id).toBe('number');
1906+
expect(facade.hook.renderers.size).toBe(before + 1);
1907+
expect(facade.rendererInternals.has(id)).toBe(true);
1908+
});
1909+
1910+
it('getComponentTree aggregates components from multiple roots', () => {
1911+
function AppA() {
1912+
return <div>A</div>;
1913+
}
1914+
function AppB() {
1915+
return <div>B</div>;
1916+
}
1917+
1918+
const containerB = document.createElement('div');
1919+
document.body.appendChild(containerB);
1920+
try {
1921+
act(() => {
1922+
ReactDOMClient.createRoot(container).render(<AppA />);
1923+
ReactDOMClient.createRoot(containerB).render(<AppB />);
1924+
});
1925+
1926+
const tree = createTools(facade).getComponentTree();
1927+
// Two roots → exactly two HostRoot nodes.
1928+
expect(tree.filter(n => n.type === 'root')).toHaveLength(2);
1929+
1930+
const appA = tree.find(n => n.name === 'AppA');
1931+
const appB = tree.find(n => n.name === 'AppB');
1932+
// Labels come from a per-call counter that spans every root, in root
1933+
// order: root A gets @c0–@c2, root B gets @c3–@c5, so labels are
1934+
// globally unique across roots.
1935+
expect(appA).toEqual({
1936+
label: '@c0',
1937+
type: 'function',
1938+
name: 'AppA',
1939+
key: null,
1940+
firstChild: '@c2',
1941+
nextSibling: null,
1942+
});
1943+
expect(appB).toEqual({
1944+
label: '@c3',
1945+
type: 'function',
1946+
name: 'AppB',
1947+
key: null,
1948+
firstChild: '@c5',
1949+
nextSibling: null,
1950+
});
1951+
} finally {
1952+
document.body.removeChild(containerB);
1953+
}
1954+
});
1955+
1956+
it('findComponents finds matches across multiple roots', () => {
1957+
function Shared() {
1958+
return <span>shared</span>;
1959+
}
1960+
function RootA() {
1961+
return <Shared />;
1962+
}
1963+
function RootB() {
1964+
return <Shared />;
1965+
}
1966+
1967+
const containerB = document.createElement('div');
1968+
document.body.appendChild(containerB);
1969+
try {
1970+
act(() => {
1971+
ReactDOMClient.createRoot(container).render(<RootA />);
1972+
ReactDOMClient.createRoot(containerB).render(<RootB />);
1973+
});
1974+
1975+
const result = createTools(facade).findComponents('Shared');
1976+
expect(result.totalCount).toBe(2);
1977+
expect(result.results.map(r => r.name)).toEqual(['Shared', 'Shared']);
1978+
// Globally unique labels, assigned in result order across both roots.
1979+
expect(result.results.map(r => r.label)).toEqual(['@c0', '@c2']);
1980+
} finally {
1981+
document.body.removeChild(containerB);
1982+
}
1983+
});
1984+
1985+
it('resolves labels from any root via getComponentByLabel', () => {
1986+
function Widget() {
1987+
return <div>w</div>;
1988+
}
1989+
function RootA() {
1990+
return <Widget />;
1991+
}
1992+
function RootB() {
1993+
return <Widget />;
1994+
}
1995+
1996+
const containerB = document.createElement('div');
1997+
document.body.appendChild(containerB);
1998+
try {
1999+
act(() => {
2000+
ReactDOMClient.createRoot(container).render(<RootA />);
2001+
ReactDOMClient.createRoot(containerB).render(<RootB />);
2002+
});
2003+
2004+
const tools = createTools(facade);
2005+
const widgets = tools.findComponents('Widget').results;
2006+
expect(widgets).toHaveLength(2);
2007+
widgets.forEach(w => {
2008+
expect(tools.getComponentByLabel(w.label).name).toBe('Widget');
2009+
});
2010+
} finally {
2011+
document.body.removeChild(containerB);
2012+
}
2013+
});
2014+
2015+
it('profiling records commits from all roots', () => {
2016+
function CounterA({count}) {
2017+
return <div>{'A:' + count}</div>;
2018+
}
2019+
function CounterB({count}) {
2020+
return <div>{'B:' + count}</div>;
2021+
}
2022+
2023+
const containerB = document.createElement('div');
2024+
document.body.appendChild(containerB);
2025+
try {
2026+
const rootA = ReactDOMClient.createRoot(container);
2027+
const rootB = ReactDOMClient.createRoot(containerB);
2028+
act(() => {
2029+
rootA.render(<CounterA count={0} />);
2030+
rootB.render(<CounterB count={0} />);
2031+
});
2032+
2033+
const tools = createTools(facade);
2034+
tools.startProfiling('multi-root-trace');
2035+
act(() => {
2036+
rootA.render(<CounterA count={1} />);
2037+
});
2038+
act(() => {
2039+
rootB.render(<CounterB count={1} />);
2040+
});
2041+
2042+
expect(tools.stopProfiling()).toEqual({
2043+
status: 'stopped',
2044+
trace: 'multi-root-trace',
2045+
commits: 2,
2046+
});
2047+
2048+
const overview = tools.getTraceOverview('multi-root-trace');
2049+
expect(overview).toHaveLength(2);
2050+
// Separate act() blocks force exactly one commit each, in order, so
2051+
// commit 0 is rootA's re-render and commit 1 is rootB's.
2052+
const names0 = tools
2053+
.getCommitReport('multi-root-trace', 0)
2054+
.components.map(c => c.name);
2055+
const names1 = tools
2056+
.getCommitReport('multi-root-trace', 1)
2057+
.components.map(c => c.name);
2058+
expect(names0).toContain('CounterA');
2059+
expect(names0).not.toContain('CounterB');
2060+
expect(names1).toContain('CounterB');
2061+
expect(names1).not.toContain('CounterA');
2062+
} finally {
2063+
document.body.removeChild(containerB);
2064+
}
2065+
});
2066+
});
18942067
});

0 commit comments

Comments
 (0)