main
js 276 lines 9.18 KB
Raw
1 /** @flow */
2
3 'use strict';
4
5 const {runOnlyForReactRange} = require('./utils');
6 const listAppUtils = require('./list-app-utils');
7 const devToolsUtils = require('./devtools-utils');
8 const {test, expect} = require('@playwright/test');
9 const config = require('../../playwright.config');
10 const semver = require('semver');
11
12 test.use(config);
13 test.describe('Components', () => {
14 let page;
15
16 test.beforeEach(async ({browser}) => {
17 page = await browser.newPage();
18
19 await page.goto(config.use.url, {
20 waitUntil: 'domcontentloaded',
21 });
22
23 await page.waitForSelector('#iframe');
24
25 await devToolsUtils.clickButton(page, 'TabBarButton-components');
26 });
27
28 test('Should display initial React components', async () => {
29 const appRowCount = await page.evaluate(() => {
30 const {createTestNameSelector, findAllNodes} = window.REACT_DOM_APP;
31 const container = document.getElementById('iframe').contentDocument;
32 const rows = findAllNodes(container, [
33 createTestNameSelector('ListItem'),
34 ]);
35 return rows.length;
36 });
37 expect(appRowCount).toBe(3);
38
39 const devToolsRowCount = await devToolsUtils.getElementCount(
40 page,
41 'ListItem'
42 );
43 expect(devToolsRowCount).toBe(3);
44 });
45
46 test('Should display newly added React components', async () => {
47 await listAppUtils.addItem(page, 'four');
48
49 const count = await devToolsUtils.getElementCount(page, 'ListItem');
50 expect(count).toBe(4);
51 });
52
53 test('Should allow elements to be inspected', async () => {
54 // Select the first list item in DevTools.
55 await devToolsUtils.selectElement(page, 'ListItem', '<List>\n<App>');
56
57 // Prop names/values may not be editable based on the React version.
58 // If they're not editable, make sure they degrade gracefully
59 const isEditableName = semver.gte(config.use.react_version, '17.0.0');
60 const isEditableValue = semver.gte(config.use.react_version, '16.8.0');
61
62 // Then read the inspected values.
63 const {
64 name: propName,
65 value: propValue,
66 existingNameElementsSize,
67 existingValueElementsSize,
68 } = await page.evaluate(
69 isEditable => {
70 const {createTestNameSelector, findAllNodes} =
71 window.REACT_DOM_DEVTOOLS;
72 const container = document.getElementById('devtools');
73
74 // Get name of first prop
75 const nameSelector = isEditable.name
76 ? 'EditableName'
77 : 'NonEditableName';
78 // Get value of first prop
79 const valueSelector = isEditable.value
80 ? 'EditableValue'
81 : 'NonEditableValue';
82
83 const existingNameElements = findAllNodes(container, [
84 createTestNameSelector('InspectedElementPropsTree'),
85 createTestNameSelector('KeyValue'),
86 createTestNameSelector(nameSelector),
87 ]);
88 const existingValueElements = findAllNodes(container, [
89 createTestNameSelector('InspectedElementPropsTree'),
90 createTestNameSelector('KeyValue'),
91 createTestNameSelector(valueSelector),
92 ]);
93
94 const name = isEditable.name
95 ? existingNameElements[0].value
96 : existingNameElements[0].innerText
97 // remove trailing colon
98 .slice(0, -1);
99 const value = isEditable.value
100 ? existingValueElements[0].value
101 : existingValueElements[0].innerText;
102
103 return {
104 name,
105 value,
106 existingNameElementsSize: existingNameElements.length,
107 existingValueElementsSize: existingValueElements.length,
108 };
109 },
110 {name: isEditableName, value: isEditableValue}
111 );
112
113 expect(existingNameElementsSize).toBe(1);
114 expect(existingValueElementsSize).toBe(1);
115 expect(propName).toBe('label');
116 expect(propValue).toBe('"one"');
117 });
118
119 test('Should allow inspecting source of the element', async () => {
120 // Source inspection is available only in modern renderer.
121 runOnlyForReactRange('>=16.8');
122
123 // Select the first list item in DevTools.
124 await devToolsUtils.selectElement(page, 'ListItem', '<List>\n<App>', true);
125
126 // Then read the inspected values.
127 const sourceText = await page.evaluate(() => {
128 const {createTestNameSelector, findAllNodes} = window.REACT_DOM_DEVTOOLS;
129 const container = document.getElementById('devtools');
130
131 const source = findAllNodes(container, [
132 createTestNameSelector('InspectedElementView-FormattedSourceString'),
133 ])[0];
134
135 return source.innerText;
136 });
137
138 // If React version is specified, the e2e-regression.html page will be used
139 // If not, then e2e.html, see playwright.config.js, how url is constructed
140 expect(sourceText).toMatch(/e2e-app[\-a-zA-Z]*\.js/);
141 });
142
143 test('should allow props to be edited', async () => {
144 runOnlyForReactRange('>=16.8');
145
146 // Select the first list item in DevTools.
147 await devToolsUtils.selectElement(page, 'ListItem', '<List>\n<App>');
148
149 // Then edit the label prop.
150 await page.evaluate(() => {
151 const {createTestNameSelector, focusWithin} = window.REACT_DOM_DEVTOOLS;
152 const container = document.getElementById('devtools');
153
154 focusWithin(container, [
155 createTestNameSelector('InspectedElementPropsTree'),
156 createTestNameSelector('KeyValue'),
157 createTestNameSelector('EditableValue'),
158 ]);
159 });
160
161 page.keyboard.press('Backspace'); // "
162 page.keyboard.press('Backspace'); // e
163 page.keyboard.press('Backspace'); // n
164 page.keyboard.press('Backspace'); // o
165 page.keyboard.insertText('new"');
166 page.keyboard.press('Enter');
167
168 await page.waitForFunction(() => {
169 const {createTestNameSelector, findAllNodes} = window.REACT_DOM_APP;
170 const container = document.getElementById('iframe').contentDocument;
171 const rows = findAllNodes(container, [
172 createTestNameSelector('ListItem'),
173 ])[0];
174 return rows.innerText === 'new';
175 });
176 });
177
178 test('should load and parse hook names for the inspected element', async () => {
179 runOnlyForReactRange('>=16.8');
180
181 // Select the List component DevTools.
182 await devToolsUtils.selectElement(page, 'List', '<App>');
183
184 // Then click to load and parse hook names.
185 await devToolsUtils.clickButton(page, 'LoadHookNamesButton');
186
187 // Make sure the expected hook names are parsed and displayed eventually.
188 await page.waitForFunction(
189 hookNames => {
190 const {createTestNameSelector, findAllNodes} =
191 window.REACT_DOM_DEVTOOLS;
192 const container = document.getElementById('devtools');
193
194 const hooksTree = findAllNodes(container, [
195 createTestNameSelector('InspectedElementHooksTree'),
196 ])[0];
197
198 if (!hooksTree) {
199 return false;
200 }
201
202 const hooksTreeText = hooksTree.innerText;
203
204 for (let i = 0; i < hookNames.length; i++) {
205 if (!hooksTreeText.includes(hookNames[i])) {
206 return false;
207 }
208 }
209
210 return true;
211 },
212 ['State(items)', 'Ref(inputRef)']
213 );
214 });
215
216 test('should allow searching for component by name', async () => {
217 async function waitForComponentSearchResultsCount(text) {
218 return await page.waitForFunction(expectedElementText => {
219 const {createTestNameSelector, findAllNodes} =
220 window.REACT_DOM_DEVTOOLS;
221 const container = document.getElementById('devtools');
222
223 // The current result index is an editable input, so its value is not
224 // part of the wrapper's innerText. Combine the input value with the
225 // total result count to reconstruct the "X | Y" label.
226 const indexInput = findAllNodes(container, [
227 createTestNameSelector('ComponentSearchInput-ResultIndexInput'),
228 ])[0];
229 const resultsCount = findAllNodes(container, [
230 createTestNameSelector('ComponentSearchInput-ResultsCount'),
231 ])[0];
232 if (indexInput === undefined || resultsCount === undefined) {
233 return false;
234 }
235 const totalCount = resultsCount.innerText.replace(/[^0-9]/g, '');
236 return `${indexInput.value} | ${totalCount}` === expectedElementText;
237 }, text);
238 }
239
240 async function focusComponentSearch() {
241 await page.evaluate(() => {
242 const {createTestNameSelector, focusWithin} = window.REACT_DOM_DEVTOOLS;
243 const container = document.getElementById('devtools');
244
245 focusWithin(container, [
246 createTestNameSelector('ComponentSearchInput-Input'),
247 ]);
248 });
249 }
250
251 await focusComponentSearch();
252 await page.keyboard.insertText('List');
253 await waitForComponentSearchResultsCount('1 | 4');
254
255 await page.keyboard.insertText('Item');
256 await waitForComponentSearchResultsCount('1 | 3');
257
258 await page.keyboard.press('Enter');
259 await waitForComponentSearchResultsCount('2 | 3');
260
261 await page.keyboard.press('Enter');
262 await waitForComponentSearchResultsCount('3 | 3');
263
264 await page.keyboard.press('Enter');
265 await waitForComponentSearchResultsCount('1 | 3');
266
267 await page.keyboard.press('Shift+Enter');
268 await waitForComponentSearchResultsCount('3 | 3');
269
270 await page.keyboard.press('Shift+Enter');
271 await waitForComponentSearchResultsCount('2 | 3');
272
273 await page.keyboard.press('Shift+Enter');
274 await waitForComponentSearchResultsCount('1 | 3');
275 });
276 });