Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(168)

Side by Side Diff: third_party/WebKit/LayoutTests/inspector-protocol/resources/inspector-protocol-test.js

Issue 2942573003: [DevTools] New harness for inspector-protocol layout tests (Closed)
Patch Set: unified Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 var InspectorTest = {};
chenwilliam 2017/06/19 19:09:20 Just a thought for f/u work, what do you think abo
dgozman 2017/06/19 21:49:22 Yeah, I actually want to turn this into TestRunner
6 InspectorTest._outputElement = null;
7 InspectorTest._dumpInspectorProtocolMessages = false;
8 InspectorTest._sessions = new Map();
9
10 InspectorTest.startDumpingProtocolMessages = function() {
11 InspectorTest._dumpInspectorProtocolMessages = true;
12 };
13
14 InspectorTest.completeTest = function() {
15 testRunner.notifyDone();
16 };
17
18 InspectorTest.log = function(text) {
19 if (!InspectorTest._outputElement) {
20 var intermediate = document.createElement('div');
21 document.body.appendChild(intermediate);
22 var intermediate2 = document.createElement('div');
23 intermediate.appendChild(intermediate2);
24 InspectorTest._outputElement = document.createElement('div');
25 InspectorTest._outputElement.className = 'output';
26 InspectorTest._outputElement.id = 'output';
27 InspectorTest._outputElement.style.whiteSpace = 'pre';
28 intermediate2.appendChild(InspectorTest._outputElement);
29 }
30 InspectorTest._outputElement.appendChild(document.createTextNode(text));
31 InspectorTest._outputElement.appendChild(document.createElement('br'));
32 };
33
34 InspectorTest.url = function(relative) {
35 return InspectorTest._baseURL + relative;
36 };
37
38 InspectorTest.logMessage = function(originalMessage) {
39 var message = JSON.parse(JSON.stringify(originalMessage));
40 if (message.id)
41 message.id = "<messageId>";
42 const nonStableFields = new Set(['nodeId', 'objectId', 'scriptId', 'timestamp' ]);
43 var objects = [message];
44 while (objects.length) {
45 var object = objects.shift();
46 for (var key in object) {
47 if (nonStableFields.has(key))
48 object[key] = `<${key}>`;
49 else if (typeof object[key] === "string" && object[key].match(/\d+:\d+:\d+ :debug/))
50 object[key] = object[key].replace(/\d+/, '<scriptId>');
51 else if (typeof object[key] === "object")
52 objects.push(object[key]);
53 }
54 }
55 InspectorTest.logObject(message);
56 return originalMessage;
57 };
58
59 InspectorTest.logObject = function(object, title) {
60 var lines = [];
61
62 function dumpValue(value, prefix, prefixWithName) {
63 if (typeof value === "object" && value !== null) {
64 if (value instanceof Array)
65 dumpItems(value, prefix, prefixWithName);
66 else
67 dumpProperties(value, prefix, prefixWithName);
68 } else {
69 lines.push(prefixWithName + String(value).replace(/\n/g, " "));
70 }
71 }
72
73 function dumpProperties(object, prefix, firstLinePrefix) {
74 prefix = prefix || "";
75 firstLinePrefix = firstLinePrefix || prefix;
76 lines.push(firstLinePrefix + "{");
77
78 var propertyNames = Object.keys(object);
79 propertyNames.sort();
80 for (var i = 0; i < propertyNames.length; ++i) {
81 var name = propertyNames[i];
82 if (!object.hasOwnProperty(name))
83 continue;
84 var prefixWithName = " " + prefix + name + " : ";
85 dumpValue(object[name], " " + prefix, prefixWithName);
86 }
87 lines.push(prefix + "}");
88 }
89
90 function dumpItems(object, prefix, firstLinePrefix) {
91 prefix = prefix || "";
92 firstLinePrefix = firstLinePrefix || prefix;
93 lines.push(firstLinePrefix + "[");
94 for (var i = 0; i < object.length; ++i)
95 dumpValue(object[i], " " + prefix, " " + prefix + "[" + i + "] : ");
96 lines.push(prefix + "]");
97 }
98
99 dumpValue(object, "", title || "");
100 InspectorTest.log(lines.join("\n"));
101 };
102
103 InspectorTest._checkExpectation = function(fail, name, messageObject) {
104 if (fail === !!messageObject.error) {
105 InspectorTest.log("PASS: " + name);
106 return true;
107 }
108
109 InspectorTest.log("FAIL: " + name + ": " + JSON.stringify(messageObject));
110 InspectorTest.completeTest();
111 return false;
112 }
113 InspectorTest.expectedSuccess = InspectorTest._checkExpectation.bind(null, false );
114 InspectorTest.expectedError = InspectorTest._checkExpectation.bind(null, true);
115
116 InspectorTest.die = function(message, error) {
117 InspectorTest.log(`${message}: ${error}\n${error.stack}`);
118 InspectorTest.completeTest();
119 throw new Error();
120 };
121
122 InspectorTest.createPage = async function() {
123 var targetId = (await DevToolsAPI._sendCommandOrDie('Target.createTarget', {ur l: 'about:blank'})).targetId;
124 await DevToolsAPI._sendCommandOrDie('Target.activateTarget', {targetId});
125 var page = new InspectorTest.Page(targetId);
126 var dummyURL = window.location.href;
127 dummyURL = dummyURL.substring(0, dummyURL.indexOf('inspector-protocol-test.htm l')) + 'inspector-protocol-page.html';
128 await page._navigate(dummyURL);
129 return page;
130 };
131
132 InspectorTest.Page = class {
133 constructor(targetId) {
134 this._targetId = targetId;
135 }
136
137 async createSession() {
138 await DevToolsAPI._sendCommandOrDie('Target.attachToTarget', {targetId: this ._targetId});
139 var session = new InspectorTest.Session(this);
140 InspectorTest._sessions.set(this._targetId, session);
141 return session;
142 }
143
144 navigate(url) {
145 return this._navigate(InspectorTest.url(url));
146 }
147
148 async _navigate(url) {
149 if (InspectorTest._sessions.get(this._targetId))
150 InspectorTest.die(`Cannot navigate to ${url} with active session`, new Err or());
151
152 var session = await this.createSession();
153 session.Protocol.Page.enable();
154 session.Protocol.Page.navigate({url: url});
155
156 var callback;
157 var promise = new Promise(f => callback = f);
158 session.Protocol.Page.onFrameNavigated(message => {
159 if (!message.params.frame.parentId)
160 callback();
161 });
162 await promise;
163
164 await session.disconnect();
165 }
166
167 async loadHTML(html) {
168 if (InspectorTest._sessions.get(this._targetId))
169 InspectorTest.die('Cannot loadHTML with active session', new Error());
170
171 html = html.replace(/'/g, "\\'").replace(/\n/g, "\\n");
172 var session = await this.createSession();
173 await session.Protocol.Runtime.evaluate({expression: `document.body.innerHTM L='${html}'`});
174 await session.disconnect();
175 }
176 };
177
178 InspectorTest.Session = class {
179 constructor(page) {
180 this._page = page;
181 this._requestId = 0;
182 this._dispatchTable = new Map();
183 this._eventHandlers = new Map();
184 this.Protocol = this._setupProtocol();
185 }
186
187 async disconnect() {
188 await DevToolsAPI._sendCommandOrDie('Target.detachFromTarget', {targetId: th is._page._targetId});
189 InspectorTest._sessions.delete(this._page._targetId);
190 }
191
192 sendRawCommand(requestId, message) {
193 DevToolsAPI._sendCommandOrDie('Target.sendMessageToTarget', {targetId: this. _page._targetId, message: message});
194 return new Promise(f => this._dispatchTable.set(requestId, f));
195 }
196
197 sendCommand(method, params) {
198 var requestId = ++this._requestId;
199 var messageObject = {'id': requestId, 'method': method, 'params': params};
200 if (InspectorTest._dumpInspectorProtocolMessages)
201 InspectorTest.log(`frontend => backend: ${JSON.stringify(messageObject)}`) ;
202 return this.sendRawCommand(requestId, JSON.stringify(messageObject));
203 }
204
205 async evaluate(code) {
206 var response = await this.Protocol.Runtime.evaluate({expression: code, retur nByValue: true});
207 if (response.error) {
208 InspectorTest.log(`Error while evaluating '${code}': ${response.error}`);
209 InspectorTest.completeTest();
210 } else {
211 return response.result.result.value;
212 }
213 }
214
215 async evaluateAsync(code) {
216 var response = await this.Protocol.Runtime.evaluate({expression: code, retur nByValue: true, awaitPromise: true});
217 if (response.error) {
218 InspectorTest.log(`Error while evaluating async '${code}': ${response.erro r}`);
219 InspectorTest.completeTest();
220 } else {
221 return response.result.result.value;
222 }
223 }
224
225 _dispatchMessage(message) {
226 if (InspectorTest._dumpInspectorProtocolMessages)
227 InspectorTest.log(`backend => frontend: ${JSON.stringify(message)}`);
228 if (typeof message.id === 'number') {
229 var handler = this._dispatchTable.get(message.id);
230 if (handler) {
231 this._dispatchTable.delete(message.id);
232 handler(message);
233 }
234 } else {
235 var eventName = message.method;
236 for (var handler of (this._eventHandlers.get(eventName) || []))
237 handler(message);
238 }
239 }
240
241 _setupProtocol() {
242 return new Proxy({}, { get: (target, agentName, receiver) => new Proxy({}, {
243 get: (target, methodName, receiver) => {
244 const eventPattern = /^(on(ce)?|off)([A-Z][A-Za-z0-9]+)/;
245 var match = eventPattern.exec(methodName);
246 if (!match)
247 return args => this.sendCommand(`${agentName}.${methodName}`, args || {});
248 var eventName = match[3];
249 eventName = eventName.charAt(0).toLowerCase() + eventName.slice(1);
250 if (match[1] === 'once')
251 return () => this._waitForEvent(`${agentName}.${eventName}`);
252 if (match[1] === 'off')
253 return listener => this._removeEventHandler(`${agentName}.${eventName} `, listener);
254 return listener => this._addEventHandler(`${agentName}.${eventName}`, li stener);
255 }
256 })});
257 }
258
259 _addEventHandler(eventName, handler) {
260 var handlers = this._eventHandlers.get(eventName) || [];
261 handlers.push(handler);
262 this._eventHandlers.set(eventName, handlers);
263 }
264
265 _removeEventHandler(eventName, handler) {
266 var handlers = this._eventHandlers.get(eventName) || [];
267 var index = handlers.indexOf(handler);
268 if (index === -1)
269 return;
270 handlers.splice(index, 1);
271 this._eventHandlers.set(eventName, handlers);
272 }
273
274 _waitForEvent(eventName) {
275 return new Promise(callback => {
276 var handler = result => {
277 this._removeEventHandler(eventName, handler);
278 callback(result);
279 };
280 this._addEventHandler(eventName, handler);
281 });
282 }
283 };
284
285 var DevToolsAPI = {};
286 DevToolsAPI._requestId = 0;
287 DevToolsAPI._embedderMessageId = 0;
288 DevToolsAPI._dispatchTable = new Map();
289
290 DevToolsAPI.dispatchMessage = function(messageOrObject) {
291 var messageObject = (typeof messageOrObject === 'string' ? JSON.parse(messageO rObject) : messageOrObject);
292 var messageId = messageObject.id;
293 try {
294 if (typeof messageId === 'number') {
295 var handler = DevToolsAPI._dispatchTable.get(messageId);
296 if (handler) {
297 DevToolsAPI._dispatchTable.delete(messageId);
298 handler(messageObject);
299 }
300 } else {
301 var eventName = messageObject.method;
302 if (eventName === 'Target.receivedMessageFromTarget') {
303 var targetId = messageObject.params.targetId;
304 var message = messageObject.params.message;
305 var session = InspectorTest._sessions.get(targetId);
306 if (session)
307 session._dispatchMessage(JSON.parse(message));
308 }
309 }
310 } catch(e) {
311 InspectorTest.die(`Exception when dispatching message\n${JSON.stringify(mess ageObject)}`, e);
312 }
313 };
314
315 DevToolsAPI._sendCommand = function(method, params) {
316 var requestId = ++DevToolsAPI._requestId;
317 var messageObject = {'id': requestId, 'method': method, 'params': params};
318 var embedderMessage = {'id': ++DevToolsAPI._embedderMessageId, 'method': 'disp atchProtocolMessage', 'params': [JSON.stringify(messageObject)]};
319 DevToolsHost.sendMessageToEmbedder(JSON.stringify(embedderMessage));
320 return new Promise(f => DevToolsAPI._dispatchTable.set(requestId, f));
321 };
322
323 DevToolsAPI._sendCommandOrDie = function(method, params, errorMessage) {
324 errorMessage = errorMessage || 'Unexpected error';
325 return DevToolsAPI._sendCommand(method, params).then(message => {
326 if (message.error)
327 InspectorTest.die('Error starting the test', new Error(message.error));
328 return message.result;
329 });
330 };
331
332 InspectorTest._start = async function(description, html, url) {
333 try {
334 InspectorTest.log(description);
335 var page = await InspectorTest.createPage();
336 if (url)
337 await page.navigate(url);
338 if (html)
339 await page.loadHTML(html);
340 var session = await page.createSession();
341 return { page: page, session: session, Protocol: session.Protocol };
342 } catch (e) {
343 InspectorTest.die('Error starting the test', e);
344 }
345 };
346 InspectorTest.startBlank = description => InspectorTest._start(description, null , null);
chenwilliam 2017/06/19 19:09:20 It seems like the description parameter is never a
dgozman 2017/06/19 21:49:22 It is in some tests. This is actually to force new
347 InspectorTest.startHTML = (html, description) => InspectorTest._start(descriptio n, html, null);
348 InspectorTest.startURL = (url, description) => InspectorTest._start(description, null, url);
349
350 InspectorTest.debugTest = function(testFunction) {
chenwilliam 2017/06/19 19:09:20 after this lands, let's include a short descriptio
dgozman 2017/06/19 21:49:22 Sure, thanks!
351 window.test = testFunction;
352 InspectorTest.log = console.log;
353 InspectorTest.completeTest = () => console.log('Test completed');
354 var dispatch = DevToolsAPI.dispatchMessage;
355 var messages = [];
356 DevToolsAPI.dispatchMessage = message => {
357 if (!messages.length) {
358 setTimeout(() => {
359 for (var message of messages.splice(0))
360 dispatch(message);
361 }, 0);
362 }
363 messages.push(message);
364 };
365 return () => {};
366 };
367
368 InspectorTest._fetch = function(url) {
369 return new Promise(fulfill => {
370 var xhr = new XMLHttpRequest();
371 xhr.open('GET', url, true);
372 xhr.onreadystatechange = e => {
373 if (xhr.readyState !== XMLHttpRequest.DONE)
374 return;
375 if ([0, 200, 304].indexOf(xhr.status) === -1) // Testing harness file:/// results in 0.
376 InspectorTest.die(`${xhr.status} while fetching ${url}`, new Error());
377 else
378 fulfill(e.target.response);
379 };
380 xhr.send(null);
381 });
382 };
383
384 InspectorTest.loadScript = async function(url) {
385 var source = await InspectorTest._fetch(InspectorTest.url(url));
386 return eval(`${source}\n//# sourceURL=${url}`);
387 };
388
389 window.addEventListener('load', async () => {
390 testRunner.dumpAsText();
391 testRunner.waitUntilDone();
392 testRunner.setCanOpenWindows(true);
393 try {
394 var testScriptURL = window.location.search.substring(1);
395 InspectorTest._baseURL = testScriptURL.substring(0, testScriptURL.lastIndexO f('/') + 1);
396 var testScript = await InspectorTest._fetch(testScriptURL);
397 eval(`${testScript}\n//# sourceURL=${testScriptURL}`);
398 } catch(e) {
399 InspectorTest.die('Unable to execute test script', e);
400 }
401 }, false);
402
403 window['onerror'] = (message, source, lineno, colno, error) => {
404 InspectorTest.die('General error', error);
405 };
406
407 window.addEventListener('unhandledrejection', e => {
408 InspectorTest.die('General error', new Error(e.reason));
409 }, false);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698