Files
react/packages/react-devtools-inline/__tests__/__e2e__/devtools-utils.js
Sebastian Markbåge 7a934a16b8 [DevTools] Show Owner Stacks in "rendered by" View (#34130)
This shows the stack trace of the JSX at each level so now you can also
jump to the code location for the JSX callsite. The visual is similar to
the owner stacks with `createTask` except when you click the `<...>` you
jump to the Instance in the Components panel.

<img width="593" height="450" alt="Screenshot 2025-08-08 at 12 19 21 AM"
src="https://github.com/user-attachments/assets/dac35faf-9d99-46ce-8b41-7c6fe24625d2"
/>

I'm not sure it's really necessary to have all the JSX stacks of every
owner. We could just have it for the current component and then the rest
of the owners you could get to if you just click that owner instance.

As a bonus, I also use the JSX callsite as the fallback for the "View
Source" button. This is primarily useful for built-ins like `<div>` and
`<Suspense>` that don't have any implementation to jump to anyway. It's
useful to be able to jump to where a boundary was defined.
2025-08-11 11:41:30 -04:00

109 lines
3.0 KiB
JavaScript

'use strict';
/** @flow */
async function clickButton(page, buttonTestName) {
await page.evaluate(testName => {
const {createTestNameSelector, findAllNodes} = window.REACT_DOM_DEVTOOLS;
const container = document.getElementById('devtools');
const button = findAllNodes(container, [
createTestNameSelector(testName),
])[0];
button.click();
}, buttonTestName);
}
async function getElementCount(page, displayName) {
return await page.evaluate(listItemText => {
const {createTestNameSelector, createTextSelector, findAllNodes} =
window.REACT_DOM_DEVTOOLS;
const container = document.getElementById('devtools');
const rows = findAllNodes(container, [
createTestNameSelector('ComponentTreeListItem'),
createTextSelector(listItemText),
]);
return rows.length;
}, displayName);
}
async function selectElement(
page,
displayName,
waitForOwnersText,
waitForSourceLoaded = false
) {
await page.evaluate(listItemText => {
const {createTestNameSelector, createTextSelector, findAllNodes} =
window.REACT_DOM_DEVTOOLS;
const container = document.getElementById('devtools');
const listItem = findAllNodes(container, [
createTestNameSelector('ComponentTreeListItem'),
createTextSelector(listItemText),
])[0];
listItem.dispatchEvent(
new MouseEvent('mousedown', {bubbles: true, cancelable: true})
);
}, displayName);
if (waitForOwnersText) {
// Wait for selected element's props to load.
await page.waitForFunction(
({titleText, ownersListText}) => {
const {createTestNameSelector, findAllNodes} =
window.REACT_DOM_DEVTOOLS;
const container = document.getElementById('devtools');
const title = findAllNodes(container, [
createTestNameSelector('InspectedElement-Title'),
])[0];
const ownersList = findAllNodes(container, [
createTestNameSelector('InspectedElementView-Owners'),
])[0];
if (!ownersList) {
return false;
}
const owners = findAllNodes(ownersList, [
createTestNameSelector('OwnerView'),
]);
return (
title &&
title.innerText.includes(titleText) &&
owners &&
owners
.map(node => node.innerText)
.join('\n')
.includes(ownersListText)
);
},
{titleText: displayName, ownersListText: waitForOwnersText}
);
}
if (waitForSourceLoaded) {
await page.waitForFunction(() => {
const {createTestNameSelector, findAllNodes} = window.REACT_DOM_DEVTOOLS;
const container = document.getElementById('devtools');
const sourceStringBlock = findAllNodes(container, [
createTestNameSelector('InspectedElementView-FormattedSourceString'),
])[0];
// Wait for a new source line to be fetched
return sourceStringBlock != null && sourceStringBlock.innerText != null;
});
}
}
module.exports = {
clickButton,
getElementCount,
selectElement,
};