OLD | NEW |
1 <!DOCTYPE html> | 1 <!DOCTYPE html> |
2 <!-- | 2 <!-- |
3 Copyright 2016 The Chromium Authors. All rights reserved. | 3 Copyright 2016 The Chromium Authors. All rights reserved. |
4 Use of this source code is governed by a BSD-style license that can be | 4 Use of this source code is governed by a BSD-style license that can be |
5 found in the LICENSE file. | 5 found in the LICENSE file. |
6 --> | 6 --> |
7 | 7 |
8 <link rel="import" href="/components/iron-flex-layout/iron-flex-layout-classes.h
tml"> | 8 <link rel="import" href="/components/iron-flex-layout/iron-flex-layout-classes.h
tml"> |
9 <link rel="import" href="/components/paper-button/paper-button.html"> | 9 <link rel="import" href="/components/paper-button/paper-button.html"> |
10 <link rel="import" href="/components/paper-icon-button/paper-icon-button.html"> | 10 <link rel="import" href="/components/paper-icon-button/paper-icon-button.html"> |
11 <link rel="import" href="/components/paper-progress/paper-progress.html"> | 11 <link rel="import" href="/components/paper-progress/paper-progress.html"> |
12 | 12 |
13 <link rel="import" href="/dashboard/elements/autocomplete-box.html"> | 13 <link rel="import" href="/dashboard/elements/autocomplete-box.html"> |
14 <link rel="import" href="/dashboard/static/simple_xhr.html"> | 14 <link rel="import" href="/dashboard/static/simple_xhr.html"> |
15 | 15 |
| 16 <link rel="import" href="/tracing/base/base.html"> |
| 17 |
16 <dom-module id="test-picker"> | 18 <dom-module id="test-picker"> |
17 <template> | 19 <template> |
18 <style include="iron-flex iron-flex-alignment"> | 20 <style include="iron-flex iron-flex-alignment"> |
19 #container * { | 21 #container * { |
20 margin-right: 3px; | 22 margin-right: 3px; |
21 } | 23 } |
22 | 24 |
23 #dnd-btn { | 25 #dnd-btn { |
24 padding: 1px 2px; | 26 padding: 1px 2px; |
25 margin: 2px 4px 2px 2px; | 27 margin: 2px 4px 2px 2px; |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
94 on-click="onAddButtonClicked" | 96 on-click="onAddButtonClicked" |
95 disabled$="{{!enableAddSeries}}">Add</paper-button> | 97 disabled$="{{!enableAddSeries}}">Add</paper-button> |
96 </div> | 98 </div> |
97 | 99 |
98 <div id="suite-description"></div> | 100 <div id="suite-description"></div> |
99 </template> | 101 </template> |
100 | 102 |
101 <script> | 103 <script> |
102 'use strict'; | 104 'use strict'; |
103 | 105 |
| 106 // TODO(eakuefner): https://github.com/catapult-project/catapult/issues/3441 |
| 107 tr.exportTo('d', function() { |
| 108 class Subtests { |
| 109 constructor() { |
| 110 this.subtests_ = new Map(); |
| 111 } |
| 112 |
| 113 // TODO(eakuefner): implemement cancellation by wrapping |
| 114 async getSubtestsForPath(path) { |
| 115 if (this.subtests_.has(path)) { |
| 116 return await this.subtests_.get(path); |
| 117 } |
| 118 |
| 119 const params = { |
| 120 type: 'pattern' |
| 121 }; |
| 122 params.p = `${path}/*`; |
| 123 const fullSubtests = await simple_xhr.asPromise( |
| 124 '/list_tests', params); |
| 125 // TODO(eakuefner): Standardize logic for dealing with test paths on |
| 126 // the client side. See |
| 127 // https://github.com/catapult-project/catapult/issues/3443. |
| 128 const subtests = []; |
| 129 for (const fullSubtest of fullSubtests) { |
| 130 subtests.push({ |
| 131 name: fullSubtest.substring(fullSubtest.lastIndexOf('/') + 1)}); |
| 132 } |
| 133 |
| 134 this.subtests_[path] = subtests; |
| 135 return subtests; |
| 136 } |
| 137 |
| 138 prepopulate(obj) { |
| 139 for (const [path, subtests] of obj) { |
| 140 this.subtests_.set(path, Promise.resolve(subtests)); |
| 141 } |
| 142 } |
| 143 } |
| 144 |
| 145 return {Subtests}; |
| 146 }); |
| 147 |
104 Polymer({ | 148 Polymer({ |
105 | 149 |
106 is: 'test-picker', | 150 is: 'test-picker', |
107 properties: { | 151 properties: { |
108 SUBTEST_LABEL: { | 152 SUBTEST_LABEL: { |
109 type: String, | 153 type: String, |
110 value: 'Subtest', | 154 value: 'Subtest', |
111 }, | 155 }, |
112 DEPRECATED_TAG: { | 156 DEPRECATED_TAG: { |
113 type: String, | 157 type: String, |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
147 disabled: false, | 191 disabled: false, |
148 }, | 192 }, |
149 { | 193 { |
150 datalist: [], | 194 datalist: [], |
151 placeholder: 'Bot', | 195 placeholder: 'Bot', |
152 disabled: true, | 196 disabled: true, |
153 } | 197 } |
154 ]) | 198 ]) |
155 }, | 199 }, |
156 | 200 |
| 201 subtests: { |
| 202 type: Object, |
| 203 value: () => new d.Subtests() |
| 204 }, |
| 205 |
157 xsrfToken: { notify: true } | 206 xsrfToken: { notify: true } |
158 | 207 |
159 }, | 208 }, |
160 | 209 |
161 computeAnd: (x, y) => x && y, | 210 computeAnd: (x, y) => x && y, |
162 | 211 |
163 ready: function() { | 212 ready: function() { |
164 this.pageStateLoading = true; | 213 this.pageStateLoading = true; |
165 this.hasChart = false; | 214 this.hasChart = false; |
166 this.enableAddSeries = false; | 215 this.enableAddSeries = false; |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
246 * Handles dropdown menu select; updates the subsequent menu accordingly. | 295 * Handles dropdown menu select; updates the subsequent menu accordingly. |
247 */ | 296 */ |
248 onDropdownSelect: function(event) { | 297 onDropdownSelect: function(event) { |
249 var model = event.model; | 298 var model = event.model; |
250 var boxIndex = model.index; | 299 var boxIndex = model.index; |
251 if (boxIndex === undefined) { | 300 if (boxIndex === undefined) { |
252 return; | 301 return; |
253 } else if (boxIndex == 0) { | 302 } else if (boxIndex == 0) { |
254 this.updateTestSuiteDescription(); | 303 this.updateTestSuiteDescription(); |
255 this.updateBotMenu(); | 304 this.updateBotMenu(); |
256 } else if (boxIndex == 1) { | |
257 this.sendSubtestRequest(); | |
258 } else { | 305 } else { |
259 // Update all the next dropdown menus for subtests. | 306 // Update all the next dropdown menus for subtests. |
260 this.updateSubtestMenus(boxIndex + 1); | 307 this.updateSubtestMenus(boxIndex + 1); |
261 } | 308 } |
262 }, | 309 }, |
263 | 310 |
264 updateTestSuiteDescription: function() { | 311 updateTestSuiteDescription: function() { |
265 // Display the test suite description if there is one. | 312 // Display the test suite description if there is one. |
266 var descriptionElement = this.$['suite-description']; | 313 var descriptionElement = this.$['suite-description']; |
267 var suite = this.getSelectionMenu(0).selectedName; | 314 var suite = this.getSelectionMenu(0).selectedName; |
(...skipping 13 matching lines...) Expand all Loading... |
281 }, | 328 }, |
282 | 329 |
283 /** | 330 /** |
284 * Updates bot dropdown menu with bot items. | 331 * Updates bot dropdown menu with bot items. |
285 */ | 332 */ |
286 updateBotMenu: function() { | 333 updateBotMenu: function() { |
287 var menu = this.getSelectionMenu(1); | 334 var menu = this.getSelectionMenu(1); |
288 var botItems = this.getBotItems(); | 335 var botItems = this.getBotItems(); |
289 menu.set('items', botItems); | 336 menu.set('items', botItems); |
290 menu.set('disabled', botItems.length === 0); | 337 menu.set('disabled', botItems.length === 0); |
291 this.subtestDict = null; | 338 // If there's a selection, update the subtest menus. |
292 // If there's a selection, send a subtest request. | |
293 if (menu.selectedItem) { | 339 if (menu.selectedItem) { |
294 this.sendSubtestRequest(); | 340 this.updateSubtestMenus(2); |
295 } else { | 341 } else { |
296 // Clear all subtest menus. | 342 // Clear all subtest menus. |
297 this.splice('selectionModels', 2); | 343 this.splice('selectionModels', 2); |
298 } | 344 } |
299 this.updateAddButtonState(); | 345 this.updateAddButtonState(); |
300 }, | 346 }, |
301 | 347 |
302 /** | 348 /** |
303 * Sends a request for subtestDict base on selected test suite and bot. | |
304 */ | |
305 sendSubtestRequest: function() { | |
306 if (this.subtestXhr) { | |
307 this.subtestXhr.abort(); | |
308 this.subtestXhr = null; | |
309 } | |
310 var bot = this.getCheckedBot(); | |
311 // If no bot is selected, just leave the current subtests. | |
312 if (bot === null) { | |
313 return; | |
314 } | |
315 var suite = this.getCheckedSuite(); | |
316 if (!suite) { | |
317 return; | |
318 } | |
319 | |
320 this.loading = true; | |
321 | |
322 var params = { | |
323 type: 'sub_tests', | |
324 suite: suite, | |
325 bots: bot, | |
326 xsrf_token: this.xsrfToken | |
327 }; | |
328 this.subtestXhr = simple_xhr.send( | |
329 '/list_tests', | |
330 params, | |
331 function(response) { | |
332 this.loading = false; | |
333 this.subtestDict = response; | |
334 // Start at first subtest menu. | |
335 this.updateSubtestMenus(2); | |
336 }.bind(this), | |
337 function(error) { | |
338 // TODO: Display error. | |
339 this.loading = false; | |
340 }.bind(this) | |
341 ); | |
342 }, | |
343 | |
344 /** | |
345 * Updates all subtest menus starting at 'startIndex'. | 349 * Updates all subtest menus starting at 'startIndex'. |
346 */ | 350 */ |
347 updateSubtestMenus: function(startIndex) { | 351 updateSubtestMenus: async function(startIndex) { |
348 var subtestDict = this.getSubtestAtIndex(startIndex); | 352 let subtests = await this.subtests.getSubtestsForPath( |
349 | 353 this.getCurrentSelectedPathUpTo(startIndex - 1)); |
350 // Update existing subtest menu. | 354 // Update existing subtest menu. |
351 for (var i = startIndex; i < this.selectionModels.length; i++) { | 355 for (var i = startIndex; i < this.selectionModels.length; i++) { |
352 // Remove the rest of the menu if no subtestDict. | 356 // Remove the rest of the menu if no subtests. |
353 if (!subtestDict || Object.keys(subtestDict).length == 0) { | 357 if (subtests === []) { |
354 this.splice('selectionModels', i); | 358 this.splice('selectionModels', i); |
355 this.updateAddButtonState(); | 359 this.updateAddButtonState(); |
356 return; | 360 return; |
357 } | 361 } |
358 var subtestItems = this.getSubtestItems(subtestDict); | 362 const menu = this.getSelectionMenu(i); |
359 var menu = this.getSelectionMenu(i); | 363 menu.set('items', subtests); |
360 menu.set('items', subtestItems); | |
361 | 364 |
362 // If there are selected item, update next menu. | 365 // If there are selected item, update next menu. |
363 if (menu.selectedItem) { | 366 if (menu.selectedItem) { |
364 subtestDict = subtestDict[menu.selectedName]['sub_tests']; | 367 subtests = await this.subtests.getSubtestsForPath( |
| 368 this.getCurrentSelectedPathUpTo(i - 1, false)); |
365 } else { | 369 } else { |
366 subtestDict = null; | 370 subtests = []; |
367 } | 371 } |
368 } | 372 } |
369 | 373 |
370 // Check if we still need to add a subtest menu. | 374 // If we reached the last iteration but still have subtests, that means |
371 if (subtestDict && Object.keys(subtestDict).length > 0) { | 375 // that means that the last extant subtest selection still has subtests |
372 var subtestItems = this.getSubtestItems(subtestDict); | 376 // and we need to put those in a new menu. |
| 377 if (subtests !== []) { |
373 this.push('selectionModels', { | 378 this.push('selectionModels', { |
374 placeholder: this.SUBTEST_LABEL, | 379 placeholder: this.SUBTEST_LABEL, |
375 datalist: subtestItems, | 380 datalist: subtests, |
376 disabled: false, | 381 disabled: false, |
377 }); | 382 }); |
378 Polymer.dom.flush(); | 383 Polymer.dom.flush(); |
379 var menu = this.getSelectionMenu(this.selectionModels.length - 1); | 384 const menu = this.getSelectionMenu(this.selectionModels.length - 1); |
380 menu.set('items', subtestItems); | 385 menu.set('items', subtests); |
381 } | 386 } |
382 | 387 |
383 this.updateAddButtonState(); | 388 this.updateAddButtonState(); |
384 }, | 389 }, |
385 | 390 |
386 updateAddButtonState: function() { | 391 updateAddButtonState: function() { |
387 this.enableAddSeries = this.getCurrentSelection() instanceof Array; | 392 this.enableAddSeries = this.getCurrentSelection() instanceof Array; |
388 }, | 393 }, |
389 | 394 |
390 getSubtestAtIndex: function(index) { | |
391 var subtestDict = this.subtestDict; | |
392 for (var i = 2; i < index; i++) { | |
393 var test = this.getSelectionMenu(i).selectedName; | |
394 if (test in subtestDict) { | |
395 subtestDict = subtestDict[test]['sub_tests']; | |
396 } else { | |
397 return null; | |
398 } | |
399 } | |
400 return subtestDict; | |
401 }, | |
402 | |
403 getSubtestItems: function(subtestDict) { | |
404 var subtestItems = []; | |
405 var subtestNames = Object.keys(subtestDict).sort(); | |
406 for (var i = 0; i < subtestNames.length; i++) { | |
407 var name = subtestNames[i]; | |
408 subtestItems.push({ | |
409 name: name, | |
410 tag: (subtestDict[name]['deprecated'] ? this.DEPRECATED_TAG : '') | |
411 }); | |
412 } | |
413 return subtestItems; | |
414 }, | |
415 | |
416 getCheckedBot: function() { | 395 getCheckedBot: function() { |
417 var botMenu = this.getSelectionMenu(1); | 396 var botMenu = this.getSelectionMenu(1); |
418 if (botMenu.selectedItem) { | 397 if (botMenu.selectedItem) { |
419 let item = botMenu.selectedItem; | 398 let item = botMenu.selectedItem; |
420 return item['group'] + '/' + item['name']; | 399 return item['group'] + '/' + item['name']; |
421 } | 400 } |
422 return null; | 401 return null; |
423 }, | 402 }, |
424 | 403 |
425 getCheckedSuite: function() { | 404 getCheckedSuite: function() { |
(...skipping 25 matching lines...) Expand all Loading... |
451 */ | 430 */ |
452 onAddButtonClicked: function(event, detail) { | 431 onAddButtonClicked: function(event, detail) { |
453 this.fire('add'); | 432 this.fire('add'); |
454 }, | 433 }, |
455 | 434 |
456 /** | 435 /** |
457 * Gets the current selection from the menus. Returns null unless there | 436 * Gets the current selection from the menus. Returns null unless there |
458 * is a valid selection. | 437 * is a valid selection. |
459 */ | 438 */ |
460 getCurrentSelection: function() { | 439 getCurrentSelection: function() { |
461 var level = 0; | 440 const path = this.getCurrentSelectedPathUpTo(-1, true); |
462 var parts = []; | 441 |
| 442 if (this.currentSelectedPath_ === path) { |
| 443 return this.currentSelectedTests_; |
| 444 } |
| 445 this.sendListTestsRequest(path); |
| 446 return null; |
| 447 }, |
| 448 |
| 449 getCurrentSelectedPathUpTo: function(maxLevel, onlyValid) { |
| 450 let level = 0; |
| 451 const parts = []; |
463 while (true) { | 452 while (true) { |
464 var menu = this.getSelectionMenu(level); | 453 if (maxLevel !== -1 && level <= maxLevel) { |
465 if (!menu || !menu.selectedItem) { | 454 break; |
| 455 } |
| 456 const menu = this.getSelectionMenu(level); |
| 457 if (onlyValid && (!menu || !menu.selectedItem)) { |
466 // A selection is only valid if it specifies at least one subtest | 458 // A selection is only valid if it specifies at least one subtest |
467 // component, which is the third level. | 459 // component, which is the third level. |
468 if (level <= 2) return null; | 460 if (level <= 2) return undefined; |
469 break; | 461 break; |
470 } else { | 462 } else { |
471 // We want to collect all the subtest components so we can form | 463 // We want to collect all the subtest components so we can form |
472 // the full test path after this loop is done. | 464 // the full test path after this loop is done. |
473 if (level >= 2) parts.push(menu.selectedItem.name); | 465 if (level >= 2) parts.push(menu.selectedItem.name); |
474 } | 466 } |
475 level += 1; | 467 level += 1; |
476 } | 468 } |
477 | 469 |
478 var suite = this.getSelectionMenu(0).selectedItem.name; | 470 const suite = this.getSelectionMenu(0).selectedItem.name; |
479 var bot = this.getCheckedBot(); | 471 const bot = this.getCheckedBot(); |
480 parts.unshift(suite); | 472 parts.unshift(suite); |
481 parts.unshift(bot); | 473 parts.unshift(bot); |
482 | 474 |
483 var path = parts.join('/'); | 475 return parts.join('/'); |
484 | |
485 if (this.currentSelectedPath_ === path) | |
486 return this.currentSelectedTests_; | |
487 this.sendListTestsRequest(path); | |
488 return null; | |
489 }, | 476 }, |
490 | 477 |
491 getCurrentSelectedPath: function() { | 478 getCurrentSelectedPath: function() { |
492 return this.currentSelectedPath_; | 479 return this.currentSelectedPath_; |
493 }, | 480 }, |
494 | 481 |
495 getCurrentUnselected: function() { | 482 getCurrentUnselected: function() { |
496 return this.currentUnselectedTests_; | 483 return this.currentUnselectedTests_; |
497 }, | 484 }, |
498 | 485 |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
576 * Converts a link in markdown format to a HTML link (anchor elements). | 563 * Converts a link in markdown format to a HTML link (anchor elements). |
577 * @param {string} text A link in markdown format. | 564 * @param {string} text A link in markdown format. |
578 * @return {string} A hyperlink string. | 565 * @return {string} A hyperlink string. |
579 */ | 566 */ |
580 convertMarkdownLinks: function(text) { | 567 convertMarkdownLinks: function(text) { |
581 return text.replace(/\[(.+?)\]\((.+?)\)/g, '<a href="$2">$1</a>'); | 568 return text.replace(/\[(.+?)\]\((.+?)\)/g, '<a href="$2">$1</a>'); |
582 } | 569 } |
583 }); | 570 }); |
584 </script> | 571 </script> |
585 </dom-module> | 572 </dom-module> |
OLD | NEW |