diff --git a/client/galaxy/scripts/components/Masthead/Masthead.test.js b/client/galaxy/scripts/components/Masthead/Masthead.test.js new file mode 100644 index 000000000000..e90fa9448c1a --- /dev/null +++ b/client/galaxy/scripts/components/Masthead/Masthead.test.js @@ -0,0 +1,112 @@ +import Masthead from "./Masthead.vue"; +import { mount, createLocalVue } from "@vue/test-utils"; +import Scratchbook from "layout/scratchbook"; + +describe("Masthead.vue", () => { + let wrapper; + let localVue; + let scratchbook; + let quotaRendered, quotaEl; + + beforeEach(() => { + localVue = createLocalVue(); + quotaRendered = false; + quotaEl = null; + + const quotaMeter = { + setElement: function (el) { + quotaEl = el; + }, + render: function () { + quotaRendered = true; + }, + }; + + const tabs = [ + // Main Analysis Tab.. + { + id: "analysis", + title: "Analyze", + menu: false, + url: "root", + }, + { + id: "shared", + title: "Shared Items", + menu: [{ title: "_menu_title", url: "_menu_url", target: "_menu_target" }], + }, + // Hidden tab (pre-Vue framework supported this, not sure it is used + // anywhere?) + { + id: "hiddentab", + title: "Hidden Title", + menu: false, + hidden: true, + }, + ]; + const activeTab = "shared"; + + // scratchbook assumes this is a Backbone collection - mock that out. + tabs.add = (x) => { + tabs.push(x); + return x; + }; + scratchbook = new Scratchbook({ + collection: tabs, + }); + const frames = scratchbook.getFrames(); + + wrapper = mount(Masthead, { + propsData: { + quotaMeter, + frames, + tabs, + activeTab, + appRoot: "prefix/", + }, + localVue, + attachToDocument: true, + }); + }); + + it("set quota element and renders it", () => { + expect(quotaEl).to.not.equals(null); + expect(quotaRendered).to.equals(true); + }); + + it("should render simple tab item links", () => { + expect(wrapper.findAll("li.nav-item").length).to.equals(5); + // Ensure specified link title respected. + expect(wrapper.find("#analysis a").text()).to.equals("Analyze"); + expect(wrapper.find("#analysis a").attributes("href")).to.equals("prefix/root"); + }); + + it("should render tab items with menus", () => { + // Ensure specified link title respected. + expect(wrapper.find("#shared a").text()).to.equals("Shared Items"); + expect(wrapper.find("#shared").classes("dropdown")).to.equals(true); + + expect(wrapper.findAll("#shared .dropdown-menu li").length).to.equals(1); + expect(wrapper.find("#shared .dropdown-menu li a").attributes().href).to.equals("prefix/_menu_url"); + expect(wrapper.find("#shared .dropdown-menu li a").attributes().target).to.equals("_menu_target"); + expect(wrapper.find("#shared .dropdown-menu li a").text()).to.equals("_menu_title"); + }); + + it("should make hidden tabs hidden", () => { + expect(wrapper.find("#analysis").attributes().style).to.not.contain("display: none"); + expect(wrapper.find("#hiddentab").attributes().style).to.contain("display: none"); + }); + + it("should highlight the active tab", () => { + expect(wrapper.find("#analysis").classes("active")).to.equals(false); + expect(wrapper.find("#shared").classes("active")).to.equals(true); + }); + + it("should display scratchbook button", async () => { + expect(wrapper.find("#enable-scratchbook a span").classes("fa-th")).to.equals(true); + expect(scratchbook.active).to.equals(false); + // wrapper.find("#enable-scratchbook a").trigger("click"); + // await localVue.nextTick(); + // expect(scratchbook.active).to.equals(true); + }); +}); diff --git a/client/galaxy/scripts/components/Masthead/Masthead.vue b/client/galaxy/scripts/components/Masthead/Masthead.vue new file mode 100644 index 000000000000..f3e27aaad4e8 --- /dev/null +++ b/client/galaxy/scripts/components/Masthead/Masthead.vue @@ -0,0 +1,116 @@ + + + + + diff --git a/client/galaxy/scripts/components/Masthead/MastheadItem.test.js b/client/galaxy/scripts/components/Masthead/MastheadItem.test.js new file mode 100644 index 000000000000..9bd4145fa9a3 --- /dev/null +++ b/client/galaxy/scripts/components/Masthead/MastheadItem.test.js @@ -0,0 +1,42 @@ +import MastheadItem from "./MastheadItem.vue"; +import { mount, createLocalVue } from "@vue/test-utils"; + +describe("Masthead.vue", () => { + let wrapper; + let localVue; + let active, menu; + + beforeEach(() => { + localVue = createLocalVue(); + }); + + function m() { + const tab = { + id: "mytab", + menu: menu, + }; + return mount(MastheadItem, { + propsData: { + tab, + activeTab: active, + }, + localVue, + }); + } + + it("should render active tab with menus", async () => { + active = "mytab"; + menu = true; + wrapper = m(); + expect(wrapper.vm.active).to.equals(true); + expect(wrapper.vm.menu).to.equals(true); + }); + + it("should render inactive tabs without menus", async () => { + active = "othertab"; + menu = false; + wrapper = m(); + expect(wrapper.vm.active).to.equals(false); + expect(wrapper.vm.menu).to.equals(false); + }); +}); diff --git a/client/galaxy/scripts/components/Masthead/MastheadItem.vue b/client/galaxy/scripts/components/Masthead/MastheadItem.vue new file mode 100644 index 000000000000..81fb2da36fb9 --- /dev/null +++ b/client/galaxy/scripts/components/Masthead/MastheadItem.vue @@ -0,0 +1,182 @@ + + + + + diff --git a/client/galaxy/scripts/components/Masthead/initMasthead.js b/client/galaxy/scripts/components/Masthead/initMasthead.js index 5eba73b2ecdf..796fa052e15c 100644 --- a/client/galaxy/scripts/components/Masthead/initMasthead.js +++ b/client/galaxy/scripts/components/Masthead/initMasthead.js @@ -2,15 +2,31 @@ * Temporary function used to mount the masthead inside the current application. * This function is exposed with the rest of the page-globals in bundledEntries. */ -import Vue from "vue"; -import Masthead from "components/Masthead"; +// import Vue from "vue"; +// import Masthead from "./Masthead"; +import Masthead from "../../layout/masthead"; +import $ from "jquery"; export function initMasthead(config, container) { console.log("initMasthead"); - const Component = Vue.extend(Masthead); - return new Component({ - props: Object.keys(config), - propsData: config, - el: container, - }); + + const masthead = new Masthead.View(config); + masthead.render(); + + const $masthead = $("#masthead"); + + if (config.hide_masthead) { + $masthead.remove(); + } else { + if (container) { + $(container).replaceWith(masthead.el); + } + } + + // const Component = Vue.extend(Masthead); + // return new Component({ + // props: Object.keys(config), + // propsData: config, + // el: container + // }); } diff --git a/client/galaxy/scripts/layout/masthead.js b/client/galaxy/scripts/layout/masthead.js index d5eb3816c09e..f88d0dbd1fe4 100644 --- a/client/galaxy/scripts/layout/masthead.js +++ b/client/galaxy/scripts/layout/masthead.js @@ -4,30 +4,20 @@ import Menu from "layout/menu"; import Scratchbook from "layout/scratchbook"; import QuotaMeter from "mvc/user/user-quotameter"; import { getGalaxyInstance } from "app"; +import Masthead from "../components/Masthead/Masthead"; +import { mountVueComponent } from "../utils/mountVueComponent"; +import { getAppRoot } from "onload/loadConfig"; /** Masthead **/ const View = Backbone.View.extend({ initialize: function (options) { const Galaxy = getGalaxyInstance(); - const self = this; this.options = options; - this.setElement(this._template()); - this.$navbarBrandLink = this.$(".navbar-brand"); - this.$navbarBrandImage = this.$(".navbar-brand-image"); - this.$navbarBrandTitle = this.$(".navbar-brand-title"); - this.$navbarTabs = this.$(".navbar-nav"); - this.$quoteMeter = this.$(".quota-meter-container"); // build tabs this.collection = new Menu.Collection(); this.collection - .on("add", (model) => { - self.$navbarTabs.append(new Menu.Tab({ model: model }).render().$el); - }) - .on("reset", () => { - self.$navbarTabs.empty(); - }) .on("dispatch", (callback) => { self.collection.each((m) => { callback(m); @@ -36,7 +26,7 @@ const View = Backbone.View.extend({ .fetch(this.options); // highlight initial active view - this.highlight(options.active_view); + this.highlight(options.active_view); // covered // scratchbook Galaxy.frame = this.frame = new Scratchbook({ @@ -47,7 +37,6 @@ const View = Backbone.View.extend({ // add quota meter to masthead Galaxy.quotaMeter = this.quotaMeter = new QuotaMeter.UserQuotaMeter({ model: Galaxy.user, - el: this.$quoteMeter, }); // loop through beforeunload functions if the user attempts to unload the page @@ -77,35 +66,38 @@ const View = Backbone.View.extend({ }, render: function () { - let brand = this.options.display_galaxy_brand ? "Galaxy " : ""; + const el = document.createElement("div"); + this.el.appendChild(el); // use this.el directly when feature parity is accomplished + let brandTitle = this.options.display_galaxy_brand ? "Galaxy " : ""; if (this.options.brand) { - brand += this.options.brand; + brandTitle += this.options.brand; } - this.$navbarBrandTitle.html(brand); - this.$navbarBrandLink.attr("href", this.options.logo_url); - this.$navbarBrandImage.attr("src", this.options.logo_src); - this.quotaMeter.render(); + const tabs = this.collection.models.map((el) => { + return el.toJSON(); + }); + mountVueComponent(Masthead)( + { + brandTitle: brandTitle, + brandLink: this.options.logo_url, + brandImage: this.options.logo_src, + quotaMeter: this.quotaMeter, + activeTab: this.activeView, + tabs: tabs, + frames: this.frame.getFrames(), + appRoot: getAppRoot(), + Galaxy: getGalaxyInstance(), + }, + el + ); return this; }, highlight: function (id) { + this.activeView = id; this.collection.forEach(function (model) { model.set("active", model.id == id); }); }, - - /** body template */ - _template: function () { - return ` - `; - }, }); export default { diff --git a/client/galaxy/scripts/layout/menu.js b/client/galaxy/scripts/layout/menu.js index e258419dbdee..f649715bf1be 100644 --- a/client/galaxy/scripts/layout/menu.js +++ b/client/galaxy/scripts/layout/menu.js @@ -1,9 +1,7 @@ /** Masthead Collection **/ -import _ from "underscore"; import $ from "jquery"; import axios from "axios"; import Backbone from "backbone"; -import { getAppRoot } from "onload/loadConfig"; import { getGalaxyInstance } from "app"; import _l from "utils/localization"; import { CommunicationServerView } from "layout/communication-server-view"; @@ -345,197 +343,6 @@ const Collection = Backbone.Collection.extend({ }, }); -/** Masthead tab **/ -const Tab = Backbone.View.extend({ - initialize: function (options) { - this.model = options.model; - this.setElement(this._template()); - this.$link = this.$(".nav-link"); - this.$note = this.$(".nav-note"); - this.$menu = this.$(".dropdown-menu"); - this.listenTo(this.model, "change", this.render, this); - }, - - events: { - "click .nav-link": "_toggleClick", - }, - - render: function () { - $(".tooltip").remove(); - this.$el - .removeClass() - .addClass(this.model.get("disabled") && "disabled") - .addClass(this.model.get("active") && "active") - .addClass(this.model.get("menu") && "dropdown") - .attr("id", this.model.id) - .css({ - visibility: (this.model.get("visible") && "visible") || "hidden", - }); - this.model.set("url", this._formatUrl(this.model.get("url"))); - this.$note - .html(this.model.get("note") || "") - .removeClass() - .addClass("nav-note") - .addClass(this.model.get("note_cls")) - .css({ - display: (this.model.get("show_note") && "block") || "none", - }); - this.$link - .html(this.model.get("title") || "") - .removeClass() - .addClass("nav-link") - .addClass(this.model.get("cls")) - .addClass(this.model.get("icon") && `nav-icon fa ${this.model.get("icon")}`) - .addClass(this.model.get("menu") && "dropdown-toggle") - .addClass(this.model.get("toggle") && "toggle") - .attr("id", this.model.get("menu") && `dropdown-button-${this.model.get("id")}`) - .attr("aria-haspopup", this.model.get("menu") && "true") - .attr("target", this.model.get("target")) - .attr("href", this.model.get("url")) - .attr("title", this.model.get("tooltip")) - .tooltip("dispose"); - if (this.model.get("tooltip")) { - this.$link.tooltip({ placement: "bottom" }); - } - if (this.model.get("menu") && this.model.get("show_menu")) { - this.$menu.show(); - $("#dd-helper") - .show() - .off() - .on("click", () => { - $("#dd-helper").hide(); - this.model.set("show_menu", false); - }); - } else { - this.$menu.hide(); - $("#dd-helper").hide(); - } - this.$menu.empty().removeClass(); - if (this.model.get("menu")) { - _.each(this.model.get("menu"), (menuItem) => { - this.$menu.append(this._buildMenuItem(menuItem)); - if (menuItem.divider) { - this.$menu.append($("
").addClass("dropdown-divider")); - } - }); - this.$menu.addClass("dropdown-menu"); - this.$menu.attr("aria-labelledby", this.$menu.siblings(".dropdown-toggle").attr("id")); - this.$menu.attr("role", "menu"); - this.$link.append($("").addClass("caret")); - } - return this; - }, - - /** Add new menu item */ - _buildMenuItem: function (options) { - options = _.defaults(options || {}, { - title: "", - url: "", - target: "_parent", - noscratchbook: false, - }); - options.url = this._formatUrl(options.url); - return $("") - .addClass("dropdown-item") - .attr("href", options.url) - .attr("target", options.target) - .attr("class", options.class) - .attr("role", "menuitem") - .html(options.title) - .on("click", (e) => { - e.preventDefault(); - this.model.set("show_menu", false); - if (options.onclick) { - options.onclick(); - } else { - const Galaxy = getGalaxyInstance(); - if (options.target == "__use_router__" && typeof Galaxy.page != "undefined") { - Galaxy.page.router.executeUseRouter(options.url); - } else { - try { - Galaxy.frame.add(options); - } catch (err) { - console.warn("Missing frame element on galaxy instance", err); - } - } - } - }); - }, - - buildLink: function (label, url) { - return $("
") - .append( - $("") - .attr("href", getAppRoot() + url) - .html(label) - ) - .html(); - }, - - /** Handle click event */ - _toggleClick: function (e) { - const model = this.model; - e.preventDefault(); - $(".tooltip").hide(); - model.trigger("dispatch", (m) => { - if (model.id !== m.id && m.get("menu")) { - m.set("show_menu", false); - } - }); - if (!model.get("disabled")) { - if (!model.get("menu")) { - if (model.get("onclick")) { - model.get("onclick")(); - } else { - const Galaxy = getGalaxyInstance(); - if (model.attributes.target == "__use_router__" && typeof Galaxy.page != "undefined") { - Galaxy.page.router.executeUseRouter(model.attributes.url); - } else { - Galaxy.frame.add(model.attributes); - } - } - } else { - model.set("show_menu", true); - } - } else { - if (this.$link.popover) { - this.$link.popover("dispose"); - } - this.$link - .popover({ - html: true, - placement: "bottom", - content: `Please ${this.buildLink("login or register", "login")} to use this feature.`, - }) - .popover("show"); - window.setTimeout(() => { - this.$link.popover("dispose"); - }, 3000); - } - }, - - /** Url formatting */ - _formatUrl: function (url) { - return typeof url == "string" && - url.indexOf("mailto:") === -1 && - url.indexOf("//") === -1 && - url.charAt(0) != "/" - ? getAppRoot() + url - : url; - }, - - /** body tempate */ - _template: function () { - return ` - `; - }, -}); - export default { Collection: Collection, - Tab: Tab, }; diff --git a/client/galaxy/scripts/layout/scratchbook.js b/client/galaxy/scripts/layout/scratchbook.js index ba1d06cc0b6b..1d2a9288d907 100644 --- a/client/galaxy/scripts/layout/scratchbook.js +++ b/client/galaxy/scripts/layout/scratchbook.js @@ -70,6 +70,11 @@ export default Backbone.View.extend({ this.history_cache = {}; }, + getFrames() { + // needed for Vue.js integration + return this.frames; + }, + /** Add a dataset to the frames */ addDataset: function (dataset_id) { const self = this; diff --git a/client/galaxy/scripts/qunit/test.js b/client/galaxy/scripts/qunit/test.js index 41abaa55e995..2702237bedef 100644 --- a/client/galaxy/scripts/qunit/test.js +++ b/client/galaxy/scripts/qunit/test.js @@ -11,7 +11,6 @@ import "./tests/form_tests"; import "./tests/list_of_pairs_collection_creator_tests"; import "./tests/workflow_editor_tests"; -import "./tests/masthead_tests"; import "./tests/graph_tests"; import "./tests/job_dag_tests"; import "./tests/history_contents_model_tests"; diff --git a/client/galaxy/scripts/qunit/tests/masthead_tests.js b/client/galaxy/scripts/qunit/tests/masthead_tests.js deleted file mode 100644 index 88021ed15adb..000000000000 --- a/client/galaxy/scripts/qunit/tests/masthead_tests.js +++ /dev/null @@ -1,124 +0,0 @@ -/* global QUnit */ -import $ from "jquery"; -import testApp from "qunit/test-app"; -import Masthead from "layout/masthead"; -import { getAppRoot } from "onload"; -import { getGalaxyInstance } from "app"; - -QUnit.module("Masthead test", { - beforeEach: function () { - testApp.create(); - this.masthead = new Masthead.View({ - brand: "brand", - use_remote_user: "use_remote_user", - remote_user_logout_href: "remote_user_logout_href", - lims_doc_url: "lims_doc_url", - support_url: "support_url", - search_url: "search_url", - mailing_lists: "mailing_lists", - screencasts_url: "screencasts_url", - wiki_url: "wiki_url", - citation_url: "citation_url", - terms_url: "terms_url", - logo_url: "logo_url", - logo_src: "../../../static/favicon.png", - is_admin_user: "is_admin_user", - active_view: "analysis", - ftp_upload_site: "ftp_upload_site", - datatypes_disable_auto: true, - allow_user_creation: true, - enable_cloud_launch: true, - user_requests: true, - }); - this.container = this.masthead.render().$el; - $("body").append(this.container); - }, - afterEach: function () { - testApp.destroy(); - this.container.remove(); - }, -}); - -QUnit.test("tabs", function (assert) { - var tab = this.masthead.collection.findWhere({ id: "analysis" }); - var $tab = $("#analysis"); - var $toggle = $tab.find(".nav-link"); - var $note = $tab.find(".nav-note"); - var $menu = $tab.find(".dropdown-menu"); - assert.ok(tab && $tab.length == 1, "Found analysis tab"); - tab.set("title", "Analyze"); - assert.ok($toggle.html() == "Analyze", "Correct title"); - assert.ok($toggle.css("visibility") == "visible", "Tab visible"); - tab.set("visible", false); - assert.ok($toggle.css("visibility") == "hidden", "Tab hidden"); - tab.set("visible", true); - assert.ok($toggle.css("visibility") == "visible", "Tab visible, again"); - assert.ok($toggle.attr("href") == getAppRoot(), "Correct initial url"); - tab.set("url", "_url"); - assert.ok($toggle.attr("href") == "/_url", "Correct test url"); - tab.set("url", "http://_url"); - assert.ok($toggle.attr("href") == "http://_url", "Correct http url"); - tab.set("tooltip", "_tooltip"); - $toggle.trigger("mouseover"); - assert.ok($(".tooltip-inner").html() == "_tooltip", "Correct tooltip"); - tab.set("tooltip", null); - $toggle.trigger("mouseover"); - assert.ok($(".tooltip-inner").length === 0, "Tooltip removed"); - tab.set("tooltip", "_tooltip_new"); - $toggle.trigger("mouseover"); - assert.ok($(".tooltip-inner").html() == "_tooltip_new", "Correct new tooltip"); - tab.set("cls", "_cls"); - assert.ok($toggle.hasClass("_cls"), "Correct extra class"); - tab.set("cls", "_cls_new"); - assert.ok($toggle.hasClass("_cls_new") && !$toggle.hasClass("_cls"), "Correct new extra class"); - assert.ok($note.html() === "", "Correct empty note"); - tab.set({ note: "_note", show_note: true }); - assert.ok($note.html() == "_note", "Correct new note"); - tab.set("toggle", true); - assert.ok($toggle.hasClass("toggle"), "Toggled"); - tab.set("toggle", false); - assert.ok(!$tab.hasClass("toggle"), "Untoggled"); - tab.set("disabled", true); - assert.ok($tab.hasClass("disabled"), "Correctly disabled"); - tab.set("disabled", false); - assert.ok(!$tab.hasClass("disabled"), "Correctly enabled"); - assert.ok($tab.hasClass("active"), "Highlighted"); - tab.set("active", false); - assert.ok(!$tab.hasClass("active"), "Not highlighted"); - tab.set("active", true); - assert.ok($tab.hasClass("active"), "Highlighted, again"); - assert.ok($menu.length === 0, "Dropdown menu correctly empty"); - tab.set("menu", [{ title: "_menu_title", url: "_menu_url", target: "_menu_target" }]); - $menu = $tab.find(".dropdown-menu"); - assert.ok($menu.hasClass("dropdown-menu"), "Menu has correct class"); - assert.ok($menu.css("display") == "none", "Menu hidden"); - $toggle.trigger("click"); - assert.ok($menu.css("display") == "block", "Menu shown"); - var $item = $menu.find("a"); - assert.ok($item.length == 1, "Added one menu item"); - assert.ok($item.html() == "_menu_title", "Menu item has correct title"); - assert.ok($item.attr("href") == "/_menu_url", "Menu item has correct url"); - assert.ok($item.attr("target") == "_menu_target", "Menu item has correct target"); - tab.set("menu", null); - $item = $menu.find("a"); - assert.ok($item.length === 0, "All menu items removed"); - tab.set("menu", [ - { title: "_menu_title_0", url: "_menu_url_0", target: "_menu_target_0" }, - { title: "_menu_title_1", url: "_menu_url_1", target: "_menu_target_1" }, - ]); - $item = $menu.find("a"); - assert.ok($item.length == 2, "Two menu items added"); - tab.set("show_menu", false); - assert.ok($menu.css("display", "none"), "Menu manually hidden"); - tab.set("show_menu", true); - assert.ok($menu.css("display", "block"), "Menu manually shown, again"); - tab = this.masthead.collection.findWhere({ id: "enable-scratchbook" }); - $tab = $("#enable-scratchbook"); - $toggle = $tab.find(".nav-link"); - assert.ok(tab && $toggle.length == 1, "Found tab to enable scratchbook"); - assert.ok(!$toggle.hasClass("toggle"), "Untoggled before click"); - $toggle.trigger("click"); - assert.ok($toggle.hasClass("toggle"), "Toggled after click"); - let galaxy = getGalaxyInstance(); - assert.ok(galaxy.frame.active, "Scratchbook is active"); -}); diff --git a/client/galaxy/style/scss/base.scss b/client/galaxy/style/scss/base.scss index 93d1db5807fa..9a7084a5abee 100644 --- a/client/galaxy/style/scss/base.scss +++ b/client/galaxy/style/scss/base.scss @@ -359,7 +359,7 @@ body { background-color: $brand-masthead; height: $masthead-height; .navbar-nav { - min-height: 100%; + height: $masthead-height; > li { // This allows the background color to fill the full height of the // masthead, while still keeping the contents centered (using flex) diff --git a/lib/galaxy/selenium/navigates_galaxy.py b/lib/galaxy/selenium/navigates_galaxy.py index d84922aa153c..5290c65b14c5 100644 --- a/lib/galaxy/selenium/navigates_galaxy.py +++ b/lib/galaxy/selenium/navigates_galaxy.py @@ -362,7 +362,7 @@ def click_grid_popup_option(self, item_name, option_label): def published_grid_search_for(self, search_term=None): return self._inline_search_for( - '#input-free-text-search-filter', + self.navigation.grids.free_text_search, search_term, ) @@ -374,7 +374,9 @@ def is_logged_in(self): @retry_during_transitions def _inline_search_for(self, selector, search_term=None): - search_box = self.wait_for_and_click_selector(selector) + # Clear tooltip resulting from clicking on the masthead to get here. + self.clear_tooltips() + search_box = self.wait_for_and_click(selector) search_box.clear() if search_term is not None: search_box.send_keys(search_term) @@ -406,7 +408,7 @@ def submit_login(self, email, password=None, assert_valid=True, retries=0): 'login': email, 'password': password, } - self.click_masthead_user() + self.components.masthead.register_or_login.wait_for_and_click() self.sleep_for(WAIT_TYPES.UX_RENDER) form = self.wait_for_visible(self.navigation.login.selectors.form) self.fill(form, login_info) @@ -435,7 +437,7 @@ def register(self, email=None, password=None, username=None, confirm=None, asser username = email.split("@")[0] self.home() - self.click_masthead_user() + self.components.masthead.register_or_login.wait_for_and_click() self.wait_for_and_click(self.navigation.registration.selectors.toggle) form = self.wait_for_visible(self.navigation.registration.selectors.form) self.fill(form, dict( @@ -470,8 +472,8 @@ def register(self, email=None, password=None, username=None, confirm=None, asser assert email in text assert self.get_logged_in_user()["email"] == email - # Hide masthead menu click - self.click_center() + # clicking away no longer closes menu post Masthead -> VueJS + self.click_masthead_user() def wait_for_logged_in(self): try: @@ -954,6 +956,12 @@ def wait_for_overlays_cleared(self): self.wait_for_selector_absent_or_hidden(".ui-modal", wait_type=WAIT_TYPES.UX_POPUP) self.wait_for_selector_absent_or_hidden(".toast", wait_type=WAIT_TYPES.UX_POPUP) + def clear_tooltips(self): + action_chains = self.action_chains() + center_element = self.driver.find_element_by_css_selector("#center") + action_chains.move_to_element(center_element).perform() + self.wait_for_selector_absent_or_hidden(".b-tooltip", wait_type=WAIT_TYPES.UX_POPUP) + def workflow_index_open(self): self.home() self.click_masthead_workflow() @@ -977,7 +985,7 @@ def workflow_index_click_search(self): def workflow_index_search_for(self, search_term=None): return self._inline_search_for( - "#workflow-search", + self.navigation.workflows.search_box, search_term, ) diff --git a/lib/galaxy/selenium/navigation.yml b/lib/galaxy/selenium/navigation.yml index 99c230c83474..87d1cbbda52c 100644 --- a/lib/galaxy/selenium/navigation.yml +++ b/lib/galaxy/selenium/navigation.yml @@ -31,21 +31,19 @@ masthead: selectors: _: '#masthead' - user: - type: xpath - selector: '//li[@id="user"]' + # bootstrap-vue a tag doesn't work as link target, need to hit span inside + user: '#user.loggedin-only > a.nav-link.dropdown-toggle > span' + register_or_login: '#user.loggedout-only > .nav-link' - user_menu: 'li#user .dropdown-menu' - workflow: - type: xpath - selector: '//li[@id="workflow"]' + user_menu: '#user .dropdown-menu a' + workflow: '#workflow .nav-link' user_email: type: xpath selector: '//a[contains(text(), "Logged in as")]' - logged_in_only: 'a.loggedin-only' - logged_out_only: 'a.loggedout-only' + logged_in_only: '.loggedin-only' + logged_out_only: '.loggedout-only' labels: # top-level menus @@ -303,6 +301,7 @@ workflows: new_button: '#workflow-create' import_button: '#workflow-import' save_button: '#workflow-save-button' + search_box: "#workflow-search" workflow_run: