Chromium Code Reviews| Index: dashboard/dashboard/elements/test-picker.html |
| diff --git a/dashboard/dashboard/elements/test-picker.html b/dashboard/dashboard/elements/test-picker.html |
| index 41f5470b5ee83ad22f7357b9236ca09eff296e3a..b039792aa97c2bcf27189ddd801888d10a5de022 100644 |
| --- a/dashboard/dashboard/elements/test-picker.html |
| +++ b/dashboard/dashboard/elements/test-picker.html |
| @@ -81,6 +81,45 @@ found in the LICENSE file. |
| <script> |
| 'use strict'; |
| + // TODO(eakuefner): https://github.com/catapult-project/catapult/issues/3441 |
|
sullivan
2017/04/14 00:37:30
Should add a comment as well: generalize this requ
eakuefner
2017/04/14 17:34:26
Done.
|
| + tr.exportTo('d', function() { |
| + class Subtests { |
| + constructor() { |
| + this.subtests_ = new Map(); |
| + } |
| + |
| + // TODO(eakuefner): implemement cancellation by wrapping |
| + async getSubtestsForPath(path) { |
| + if (this.subtests_.has(path)) { |
| + return await this.subtests_.get(path); |
|
sullivan
2017/04/14 00:37:30
I'm confused. subtests_ is a Map. this.subtests_.g
eakuefner
2017/04/14 17:34:26
Oops, great catch! I actually meant for the promis
|
| + } |
| + const params = { |
| + type: 'pattern' |
| + }; |
| + params.p = `${path}/*`; |
| + const fullSubtests = await simple_xhr.asPromise( |
| + '/list_tests', params); |
| + // TODO(eakuefner): Standardize logic for dealing with test paths on |
| + // the client side. See |
| + // https://github.com/catapult-project/catapult/issues/3443. |
| + const subtests = []; |
| + for (const fullSubtest of fullSubtests) { |
| + subtests.push({ |
| + name: fullSubtest.substring(fullSubtest.lastIndexOf('/') + 1)}); |
| + } |
| + this.subtests_[path] = subtests; |
| + return subtests; |
| + } |
| + |
| + prepopulate(obj) { |
| + for (const [path, subtests] of Object.entries(obj)) { |
| + this.subtests_.set(path, Promise.resolve(subtests)); |
| + } |
| + } |
| + } |
| + return {Subtests}; |
| + }); |
| + |
| Polymer({ |
| is: 'test-picker', |
| @@ -134,6 +173,11 @@ found in the LICENSE file. |
| ]) |
| }, |
| + subtests: { |
| + type: Object, |
| + value: () => new d.Subtests() |
| + }, |
| + |
| xsrfToken: { notify: true } |
| }, |
| @@ -146,6 +190,7 @@ found in the LICENSE file. |
| this.enableAddSeries = false; |
| this.selectedSuite = null; |
| this.suiteDescription = null; |
| + this.updatingSubtestMenus = false; |
| this.set('selectionModels.0.datalist', this.getSuiteItems()); |
| }, |
| @@ -227,13 +272,12 @@ found in the LICENSE file. |
| onDropdownSelect: function(event) { |
| var model = event.model; |
| var boxIndex = model.index; |
| + if (this.updatingSubtestMenus) return; |
| if (boxIndex === undefined) { |
| return; |
| } else if (boxIndex == 0) { |
| this.updateTestSuiteDescription(); |
| this.updateBotMenu(); |
| - } else if (boxIndex == 1) { |
| - this.sendSubtestRequest(); |
| } else { |
| // Update all the next dropdown menus for subtests. |
| this.updateSubtestMenus(boxIndex + 1); |
| @@ -262,15 +306,17 @@ found in the LICENSE file. |
| /** |
| * Updates bot dropdown menu with bot items. |
| */ |
| - updateBotMenu: function() { |
| + updateBotMenu: async function() { |
| var menu = this.getSelectionMenu(1); |
| var botItems = this.getBotItems(); |
| menu.set('items', botItems); |
| menu.set('disabled', botItems.length === 0); |
| this.subtestDict = null; |
| - // If there's a selection, send a subtest request. |
| + // If there's a selection, update the subtest menus. |
| if (menu.selectedItem) { |
| - this.sendSubtestRequest(); |
| + this.updatingSubtestMenus = true; |
| + await this.updateSubtestMenus(2); |
| + this.updatingSubtestMenus = false; |
| } else { |
| // Clear all subtest menus. |
| this.splice('selectionModels', 2); |
| @@ -279,84 +325,52 @@ found in the LICENSE file. |
| }, |
| /** |
| - * Sends a request for subtestDict base on selected test suite and bot. |
| - */ |
| - sendSubtestRequest: function() { |
| - if (this.subtestXhr) { |
| - this.subtestXhr.abort(); |
| - this.subtestXhr = null; |
| - } |
| - var bot = this.getCheckedBot(); |
| - // If no bot is selected, just leave the current subtests. |
| - if (bot === null) { |
| - return; |
| - } |
| - var suite = this.getCheckedSuite(); |
| - if (!suite) { |
| - return; |
| - } |
| - |
| - this.loading = true; |
| - |
| - var params = { |
| - type: 'sub_tests', |
| - suite: suite, |
| - bots: bot, |
| - xsrf_token: this.xsrfToken |
| - }; |
| - this.subtestXhr = simple_xhr.send( |
| - '/list_tests', |
| - params, |
| - function(response) { |
| - this.loading = false; |
| - this.subtestDict = response; |
| - // Start at first subtest menu. |
| - this.updateSubtestMenus(2); |
| - }.bind(this), |
| - function(error) { |
| - // TODO: Display error. |
| - this.loading = false; |
| - }.bind(this) |
| - ); |
| - }, |
| - |
| - /** |
| * Updates all subtest menus starting at 'startIndex'. |
| */ |
| - updateSubtestMenus: function(startIndex) { |
| - var subtestDict = this.getSubtestAtIndex(startIndex); |
| + updateSubtestMenus: async function(startIndex) { |
| + let subtests = await this.subtests.getSubtestsForPath( |
| + this.getCurrentSelectedPathUpTo(startIndex)); |
| // Update existing subtest menu. |
| for (var i = startIndex; i < this.selectionModels.length; i++) { |
| - // Remove the rest of the menu if no subtestDict. |
| - if (!subtestDict || Object.keys(subtestDict).length == 0) { |
| + // Remove the rest of the menu if no subtests. |
| + if (subtests.length === 0) { |
| this.splice('selectionModels', i); |
| this.updateAddButtonState(); |
| return; |
| } |
| - var subtestItems = this.getSubtestItems(subtestDict); |
| - var menu = this.getSelectionMenu(i); |
| - menu.set('items', subtestItems); |
| + const menu = this.getSelectionMenu(i); |
| + this.updatingSubtestMenus = true; |
| + menu.set('items', subtests); |
| + this.updatingSubtestMenus = false; |
| - // If there are selected item, update next menu. |
| + // If there is a selected item, update the next menu. |
| if (menu.selectedItem) { |
| - subtestDict = subtestDict[menu.selectedName]['sub_tests']; |
| + const selectedPath = this.getCurrentSelectedPathUpTo(i + 1, false); |
| + if (selectedPath !== undefined) { |
| + subtests = await this.subtests.getSubtestsForPath(selectedPath); |
| + } else { |
| + subtests = []; |
| + } |
| } else { |
| - subtestDict = null; |
| + subtests = []; |
| } |
| } |
| - // Check if we still need to add a subtest menu. |
| - if (subtestDict && Object.keys(subtestDict).length > 0) { |
| - var subtestItems = this.getSubtestItems(subtestDict); |
| + // If we reached the last iteration but still have subtests, that means |
| + // that the last extant subtest selection still has subtests and we need |
| + // to put those in a new menu. |
| + if (subtests.length > 0) { |
| this.push('selectionModels', { |
| placeholder: this.SUBTEST_LABEL, |
| - datalist: subtestItems, |
| + datalist: subtests, |
| disabled: false, |
| }); |
| + this.updatingSubtestMenus = true; |
| Polymer.dom.flush(); |
| - var menu = this.getSelectionMenu(this.selectionModels.length - 1); |
| - menu.set('items', subtestItems); |
| + const menu = this.getSelectionMenu(this.selectionModels.length - 1); |
| + menu.set('items', subtests); |
| + this.updatingSubtestMenus = false; |
| } |
| this.updateAddButtonState(); |
| @@ -367,32 +381,6 @@ found in the LICENSE file. |
| (await this.getCurrentSelection()) instanceof Array); |
| }, |
| - getSubtestAtIndex: function(index) { |
| - var subtestDict = this.subtestDict; |
| - for (var i = 2; i < index; i++) { |
| - var test = this.getSelectionMenu(i).selectedName; |
| - if (test in subtestDict) { |
| - subtestDict = subtestDict[test]['sub_tests']; |
| - } else { |
| - return null; |
| - } |
| - } |
| - return subtestDict; |
| - }, |
| - |
| - getSubtestItems: function(subtestDict) { |
| - var subtestItems = []; |
| - var subtestNames = Object.keys(subtestDict).sort(); |
| - for (var i = 0; i < subtestNames.length; i++) { |
| - var name = subtestNames[i]; |
| - subtestItems.push({ |
| - name: name, |
| - tag: (subtestDict[name]['deprecated'] ? this.DEPRECATED_TAG : '') |
| - }); |
| - } |
| - return subtestItems; |
| - }, |
| - |
| getCheckedBot: function() { |
| var botMenu = this.getSelectionMenu(1); |
| if (botMenu.selectedItem) { |
| @@ -437,29 +425,7 @@ found in the LICENSE file. |
| * Gets the current selection from the menus. |
| */ |
| getCurrentSelection: async function() { |
| - var level = 0; |
| - var parts = []; |
| - while (true) { |
| - var menu = this.getSelectionMenu(level); |
| - if (!menu || !menu.selectedItem) { |
| - // A selection is only valid if it specifies at least one subtest |
| - // component, which is the third level. |
| - if (level <= 2) return null; |
| - break; |
| - } else { |
| - // We want to collect all the subtest components so we can form |
| - // the full test path after this loop is done. |
| - if (level >= 2) parts.push(menu.selectedItem.name); |
| - } |
| - level += 1; |
| - } |
| - |
| - var suite = this.getSelectionMenu(0).selectedItem.name; |
| - var bot = this.getCheckedBot(); |
| - parts.unshift(suite); |
| - parts.unshift(bot); |
| - |
| - var path = parts.join('/'); |
| + const path = this.getCurrentSelectedPathUpTo(-1, true); |
| // If the paths are the same, this means that the selected path has |
| // already been confirmed as valid by the previous request to |
| @@ -478,7 +444,6 @@ found in the LICENSE file. |
| this.getSubtestsForPath(path, true), |
| this.getSubtestsForPath(path, false)]); |
| } catch (e) { |
| - // TODO(eakuefner): Improve this error handling. |
| this.loading = false; |
| return null; |
| } |
| @@ -486,10 +451,42 @@ found in the LICENSE file. |
| this.currentSelectedPath_ = path; |
| this.currentSelectedTests_ = selectedTests; |
| this.currentUnselectedTests_ = unselectedTests; |
| - this.updateSubtestMenus(2); |
| + this.updatingSubtestMenus = true; |
| + await this.updateSubtestMenus(2); |
| + this.updatingSubtestMenus = false; |
| return selectedTests; |
| }, |
| + getCurrentSelectedPathUpTo: function(maxLevel, onlyValid) { |
| + let level = 0; |
| + const parts = []; |
| + while (true) { |
| + if (maxLevel !== -1 && level >= maxLevel) { |
| + break; |
| + } |
| + const menu = this.getSelectionMenu(level); |
| + if (onlyValid && (!menu || !menu.selectedItem)) { |
| + // A selection is only valid if it specifies at least one subtest |
| + // component, which is the third level. |
| + if (level <= 2) return undefined; |
| + break; |
| + } else { |
| + // We want to collect all the subtest components so we can form |
| + // the full test path after this loop is done. |
| + if (level >= 2) parts.push(menu.selectedItem.name); |
| + } |
| + level += 1; |
| + } |
| + |
| + const suite = this.getSelectionMenu(0).selectedItem.name; |
| + const bot = this.getCheckedBot(); |
| + parts.unshift(suite); |
| + parts.unshift(bot); |
| + |
| + if (parts.length < maxLevel) return undefined; |
| + return parts.join('/'); |
| + }, |
| + |
| getCurrentSelectedPath: function() { |
| return this.currentSelectedPath_; |
| }, |