Skip to content

Commit 5204f56

Browse files
Brian Vaughnzhengjitf
Brian Vaughn
authored andcommitted
Use ReactDOM Test Selector API in DevTools e2e tests (facebook#22978)
Builds on top of the existing Playwright tests to plug in the test selector API: https://gist.github.com/bvaughn/d3c8b8842faf2ac2439bb11773a19cec My goals in doing this are to... 1. Experiment with the new API to see what works and what doesn't. 2. Add some test selector attributes (and remove DOM-structure based selectors). 3. Focus the tests on DevTools itself (rather than the test app). I also took this opportunity to add a few new test cases– like named hooks, editable props, component search, and profiling- just to play around more with the Playwright API. Relates to issue facebook#22646
1 parent 238a4f0 commit 5204f56

24 files changed

+549
-74
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
/** @flow */
2+
3+
'use strict';
4+
5+
const listAppUtils = require('./list-app-utils');
6+
const devToolsUtils = require('./devtools-utils');
7+
const {test, expect} = require('@playwright/test');
8+
const config = require('../../playwright.config');
9+
test.use(config);
10+
test.describe('Components', () => {
11+
let page;
12+
13+
test.beforeEach(async ({browser}) => {
14+
page = await browser.newPage();
15+
16+
await page.goto('http://localhost:8080/e2e.html', {
17+
waitUntil: 'domcontentloaded',
18+
});
19+
20+
await page.waitForSelector('#iframe');
21+
22+
await devToolsUtils.clickButton(page, 'TabBarButton-components');
23+
});
24+
25+
test('Should display initial React components', async () => {
26+
const appRowCount = await page.evaluate(() => {
27+
const {createTestNameSelector, findAllNodes} = window.REACT_DOM_APP;
28+
const container = document.getElementById('iframe').contentDocument;
29+
const rows = findAllNodes(container, [
30+
createTestNameSelector('ListItem'),
31+
]);
32+
return rows.length;
33+
});
34+
expect(appRowCount).toBe(3);
35+
36+
const devToolsRowCount = await devToolsUtils.getElementCount(
37+
page,
38+
'ListItem'
39+
);
40+
expect(devToolsRowCount).toBe(3);
41+
});
42+
43+
test('Should display newly added React components', async () => {
44+
await listAppUtils.addItem(page, 'four');
45+
46+
const count = await devToolsUtils.getElementCount(page, 'ListItem');
47+
expect(count).toBe(4);
48+
});
49+
50+
test('Should allow elements to be inspected', async () => {
51+
// Select the first list item in DevTools.
52+
await devToolsUtils.selectElement(page, 'ListItem', 'List\nApp');
53+
54+
// Then read the inspected values.
55+
const [propName, propValue, sourceText] = await page.evaluate(() => {
56+
const {createTestNameSelector, findAllNodes} = window.REACT_DOM_DEVTOOLS;
57+
const container = document.getElementById('devtools');
58+
59+
const editableName = findAllNodes(container, [
60+
createTestNameSelector('InspectedElementPropsTree'),
61+
createTestNameSelector('EditableName'),
62+
])[0];
63+
const editableValue = findAllNodes(container, [
64+
createTestNameSelector('InspectedElementPropsTree'),
65+
createTestNameSelector('EditableValue'),
66+
])[0];
67+
const source = findAllNodes(container, [
68+
createTestNameSelector('InspectedElementView-Source'),
69+
])[0];
70+
71+
return [editableName.value, editableValue.value, source.innerText];
72+
});
73+
74+
expect(propName).toBe('label');
75+
expect(propValue).toBe('"one"');
76+
expect(sourceText).toContain('ListApp.js');
77+
});
78+
79+
test('should allow props to be edited', async () => {
80+
// Select the first list item in DevTools.
81+
await devToolsUtils.selectElement(page, 'ListItem', 'List\nApp');
82+
83+
// Then edit the label prop.
84+
await page.evaluate(() => {
85+
const {createTestNameSelector, focusWithin} = window.REACT_DOM_DEVTOOLS;
86+
const container = document.getElementById('devtools');
87+
88+
focusWithin(container, [
89+
createTestNameSelector('InspectedElementPropsTree'),
90+
createTestNameSelector('EditableValue'),
91+
]);
92+
});
93+
94+
page.keyboard.press('Backspace'); // "
95+
page.keyboard.press('Backspace'); // e
96+
page.keyboard.press('Backspace'); // n
97+
page.keyboard.press('Backspace'); // o
98+
page.keyboard.insertText('new"');
99+
page.keyboard.press('Enter');
100+
101+
await page.waitForFunction(() => {
102+
const {createTestNameSelector, findAllNodes} = window.REACT_DOM_APP;
103+
const container = document.getElementById('iframe').contentDocument;
104+
const rows = findAllNodes(container, [
105+
createTestNameSelector('ListItem'),
106+
])[0];
107+
return rows.innerText === 'new';
108+
});
109+
});
110+
111+
test('should load and parse hook names for the inspected element', async () => {
112+
// Select the List component DevTools.
113+
await devToolsUtils.selectElement(page, 'List', 'App');
114+
115+
// Then click to load and parse hook names.
116+
await devToolsUtils.clickButton(page, 'LoadHookNamesButton');
117+
118+
// Make sure the expected hook names are parsed and displayed eventually.
119+
await page.waitForFunction(
120+
hookNames => {
121+
const {
122+
createTestNameSelector,
123+
findAllNodes,
124+
} = window.REACT_DOM_DEVTOOLS;
125+
const container = document.getElementById('devtools');
126+
127+
const hooksTree = findAllNodes(container, [
128+
createTestNameSelector('InspectedElementHooksTree'),
129+
])[0];
130+
131+
if (!hooksTree) {
132+
return false;
133+
}
134+
135+
const hooksTreeText = hooksTree.innerText;
136+
137+
for (let i = 0; i < hookNames.length; i++) {
138+
if (!hooksTreeText.includes(hookNames[i])) {
139+
return false;
140+
}
141+
}
142+
143+
return true;
144+
},
145+
['State(items)', 'Ref(inputRef)']
146+
);
147+
});
148+
149+
test('should allow searching for component by name', async () => {
150+
async function getComponentSearchResultsCount() {
151+
return await page.evaluate(() => {
152+
const {
153+
createTestNameSelector,
154+
findAllNodes,
155+
} = window.REACT_DOM_DEVTOOLS;
156+
const container = document.getElementById('devtools');
157+
158+
const element = findAllNodes(container, [
159+
createTestNameSelector('ComponentSearchInput-ResultsCount'),
160+
])[0];
161+
return element.innerText;
162+
});
163+
}
164+
165+
await page.evaluate(() => {
166+
const {createTestNameSelector, focusWithin} = window.REACT_DOM_DEVTOOLS;
167+
const container = document.getElementById('devtools');
168+
169+
focusWithin(container, [
170+
createTestNameSelector('ComponentSearchInput-Input'),
171+
]);
172+
});
173+
174+
page.keyboard.insertText('List');
175+
let count = await getComponentSearchResultsCount();
176+
expect(count).toBe('1 | 4');
177+
178+
page.keyboard.insertText('Item');
179+
count = await getComponentSearchResultsCount();
180+
expect(count).toBe('1 | 3');
181+
182+
page.keyboard.press('Enter');
183+
count = await getComponentSearchResultsCount();
184+
expect(count).toBe('2 | 3');
185+
186+
page.keyboard.press('Enter');
187+
count = await getComponentSearchResultsCount();
188+
expect(count).toBe('3 | 3');
189+
190+
page.keyboard.press('Enter');
191+
count = await getComponentSearchResultsCount();
192+
expect(count).toBe('1 | 3');
193+
194+
page.keyboard.press('Shift+Enter');
195+
count = await getComponentSearchResultsCount();
196+
expect(count).toBe('3 | 3');
197+
198+
page.keyboard.press('Shift+Enter');
199+
count = await getComponentSearchResultsCount();
200+
expect(count).toBe('2 | 3');
201+
202+
page.keyboard.press('Shift+Enter');
203+
count = await getComponentSearchResultsCount();
204+
expect(count).toBe('1 | 3');
205+
});
206+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
'use strict';
2+
3+
/** @flow */
4+
5+
async function clickButton(page, buttonTestName) {
6+
await page.evaluate(testName => {
7+
const {createTestNameSelector, findAllNodes} = window.REACT_DOM_DEVTOOLS;
8+
const container = document.getElementById('devtools');
9+
10+
const button = findAllNodes(container, [
11+
createTestNameSelector(testName),
12+
])[0];
13+
button.click();
14+
}, buttonTestName);
15+
}
16+
17+
async function getElementCount(page, displayName) {
18+
return await page.evaluate(listItemText => {
19+
const {
20+
createTestNameSelector,
21+
createTextSelector,
22+
findAllNodes,
23+
} = window.REACT_DOM_DEVTOOLS;
24+
const container = document.getElementById('devtools');
25+
const rows = findAllNodes(container, [
26+
createTestNameSelector('ComponentTreeListItem'),
27+
createTextSelector(listItemText),
28+
]);
29+
return rows.length;
30+
}, displayName);
31+
}
32+
33+
async function selectElement(page, displayName, waitForOwnersText) {
34+
await page.evaluate(listItemText => {
35+
const {
36+
createTestNameSelector,
37+
createTextSelector,
38+
findAllNodes,
39+
} = window.REACT_DOM_DEVTOOLS;
40+
const container = document.getElementById('devtools');
41+
42+
const listItem = findAllNodes(container, [
43+
createTestNameSelector('ComponentTreeListItem'),
44+
createTextSelector(listItemText),
45+
])[0];
46+
listItem.click();
47+
}, displayName);
48+
49+
if (waitForOwnersText) {
50+
// Wait for selected element's props to load.
51+
await page.waitForFunction(
52+
({titleText, ownersListText}) => {
53+
const {
54+
createTestNameSelector,
55+
findAllNodes,
56+
} = window.REACT_DOM_DEVTOOLS;
57+
const container = document.getElementById('devtools');
58+
59+
const title = findAllNodes(container, [
60+
createTestNameSelector('InspectedElement-Title'),
61+
])[0];
62+
63+
const ownersList = findAllNodes(container, [
64+
createTestNameSelector('InspectedElementView-Owners'),
65+
])[0];
66+
67+
return (
68+
title &&
69+
title.innerText.includes(titleText) &&
70+
ownersList &&
71+
ownersList.innerText.includes(ownersListText)
72+
);
73+
},
74+
{titleText: displayName, ownersListText: waitForOwnersText}
75+
);
76+
}
77+
}
78+
79+
module.exports = {
80+
clickButton,
81+
getElementCount,
82+
selectElement,
83+
};

packages/react-devtools-inline/__tests__/__e2e__/inspecting-props.test.js

-52
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
'use strict';
2+
3+
/** @flow */
4+
5+
async function addItem(page, newItemText) {
6+
await page.evaluate(text => {
7+
const {createTestNameSelector, findAllNodes} = window.REACT_DOM_APP;
8+
const container = document.getElementById('iframe').contentDocument;
9+
10+
const input = findAllNodes(container, [
11+
createTestNameSelector('AddItemInput'),
12+
])[0];
13+
input.value = text;
14+
15+
const button = findAllNodes(container, [
16+
createTestNameSelector('AddItemButton'),
17+
])[0];
18+
19+
button.click();
20+
}, newItemText);
21+
}
22+
23+
module.exports = {
24+
addItem,
25+
};

0 commit comments

Comments
 (0)