mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Switch to a new more structured YAML format for navigation data.
Break everything into components.
This commit is contained in:
@@ -1,5 +1,158 @@
|
||||
import re
|
||||
import string
|
||||
|
||||
import yaml
|
||||
|
||||
from pkg_resources import resource_string
|
||||
|
||||
data_yaml = resource_string(__name__, 'navigation-data.yml').decode("UTF-8")
|
||||
NAVIGATION_DATA = yaml.safe_load(data_yaml)
|
||||
from selenium.webdriver.common.by import By
|
||||
|
||||
from galaxy.util.bunch import Bunch
|
||||
|
||||
|
||||
old_data_yaml = resource_string(__name__, 'navigation-data.yml').decode("UTF-8")
|
||||
NAVIGATION_DATA = yaml.safe_load(old_data_yaml)
|
||||
new_data_yaml = resource_string(__name__, 'navigation.yml').decode("UTF-8")
|
||||
NAVIGATION_RAW = yaml.safe_load(new_data_yaml)
|
||||
|
||||
|
||||
class SelectorTemplate(object):
|
||||
|
||||
def __init__(self, selector, selector_type, kwds=None, with_classes=None):
|
||||
self._selector = selector
|
||||
self.selector_type = selector_type
|
||||
self.__kwds = kwds or {}
|
||||
self.with_classes = with_classes or []
|
||||
|
||||
@staticmethod
|
||||
def from_dict(raw_value):
|
||||
if isinstance(raw_value, dict):
|
||||
return SelectorTemplate(raw_value["selector"], raw_value.get("type", "css"))
|
||||
else:
|
||||
return SelectorTemplate(raw_value, "css")
|
||||
|
||||
def with_class(self, class_):
|
||||
assert self.selector_type == "css"
|
||||
return SelectorTemplate(self._selector, self.selector_type, kwds=self.__kwds, with_classes=self.with_classes + [class_])
|
||||
|
||||
def __call__(self, **kwds):
|
||||
new_kwds = self.__kwds
|
||||
new_kwds.update(**kwds)
|
||||
return SelectorTemplate(self._selector, self.selector_type, kwds=new_kwds, with_classes=self.with_classes)
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
if self.selector_type == "css":
|
||||
template = "CSS selector [%s]"
|
||||
elif self.selector_type == "xpath":
|
||||
template = "XPATH selector [%s]"
|
||||
elif self.selector_type == "id":
|
||||
template = "DOM element with id [%s]"
|
||||
return template % self.selector
|
||||
|
||||
@property
|
||||
def selector(self):
|
||||
selector = self._selector
|
||||
if self.__kwds is not None:
|
||||
selector = string.Template(selector).substitute(self.__kwds)
|
||||
selector = selector + "".join([".%s" % c for c in self.with_classes])
|
||||
return selector
|
||||
|
||||
@property
|
||||
def element_locator(self):
|
||||
if self.selector_type == "css":
|
||||
by = By.CSS_SELECTOR
|
||||
elif self.selector_type == "xpath":
|
||||
by = By.XPATH
|
||||
elif self.selector_type == "id":
|
||||
by = By.ID
|
||||
else:
|
||||
raise Exception("Unknown selector type")
|
||||
return (by, self.selector)
|
||||
|
||||
@property
|
||||
def as_css_class(self):
|
||||
assert self.selector_type == "css"
|
||||
assert re.compile(r"\.\w+").match(self._selector)
|
||||
return self._selector[1:]
|
||||
|
||||
|
||||
class Label(object):
|
||||
|
||||
def __init__(self, text):
|
||||
self.text = text
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
return "Link text [%s]" % self.text
|
||||
|
||||
@property
|
||||
def element_locator(self):
|
||||
return (By.LINK_TEXT, self.text)
|
||||
|
||||
|
||||
class Text(object):
|
||||
|
||||
def __init__(self, text):
|
||||
self.text = text
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
return "Text containing [%s]" % self.text
|
||||
|
||||
@property
|
||||
def element_locator(self):
|
||||
return (By.PARTIAL_LINK_TEXT, self.text)
|
||||
|
||||
|
||||
class Component(object):
|
||||
|
||||
def __init__(self, name, sub_components, selectors, labels, text):
|
||||
self.name = name
|
||||
self._sub_components = sub_components
|
||||
self._selectors = selectors
|
||||
self._labels = labels
|
||||
self._text = text
|
||||
|
||||
self.selectors = Bunch(**self._selectors)
|
||||
self.labels = Bunch(**self._labels)
|
||||
self.text = Bunch(**self._text)
|
||||
|
||||
@property
|
||||
def selector(self):
|
||||
if "_" in self._selectors:
|
||||
return self._selectors["_"]
|
||||
else:
|
||||
raise Exception("No _ selector for [%s]" % self)
|
||||
|
||||
@staticmethod
|
||||
def from_dict(name, raw_value):
|
||||
selectors = {}
|
||||
labels = {}
|
||||
text = {}
|
||||
sub_components = {}
|
||||
|
||||
for key, value in raw_value.items():
|
||||
if key == "selectors":
|
||||
for selector_key, selector_value in value.items():
|
||||
selectors[selector_key] = SelectorTemplate.from_dict(selector_value)
|
||||
elif key == "labels":
|
||||
for label_key, label_value in value.items():
|
||||
labels[label_key] = Label(label_value)
|
||||
elif key == "text":
|
||||
for text_key, text_value in value.items():
|
||||
text[text_key] = Text(text_value)
|
||||
else:
|
||||
component = Component.from_dict(key, value)
|
||||
sub_components[key] = component
|
||||
|
||||
return Component(name, sub_components, selectors, labels, text)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return self._sub_components[attr]
|
||||
|
||||
def __str__(self):
|
||||
return "Component[%s]" % self.name
|
||||
|
||||
|
||||
NAVIGATION = Component.from_dict("root", NAVIGATION_RAW)
|
||||
|
||||
@@ -28,13 +28,28 @@ class HasDriver:
|
||||
for element in elements:
|
||||
assert not element.is_displayed()
|
||||
|
||||
def assert_absent_or_hidden(self, selector_template):
|
||||
elements = self.find_elements(selector_template)
|
||||
for element in elements:
|
||||
assert not element.is_displayed()
|
||||
|
||||
def selector_is_displayed(self, selector):
|
||||
element = self.driver.find_element_by_css_selector(selector)
|
||||
return element.is_displayed()
|
||||
|
||||
def is_displayed(self, selector_template):
|
||||
element = self.driver.find_element(*selector_template.element_locator)
|
||||
return element.is_displayed()
|
||||
|
||||
def assert_selector_absent(self, selector):
|
||||
assert len(self.driver.find_elements_by_css_selector(selector)) == 0
|
||||
|
||||
def find_elements(self, selector_template):
|
||||
return self.driver.find_elements(*selector_template.element_locator)
|
||||
|
||||
def assert_absent(self, selector_template):
|
||||
assert len(self.find_elements(selector_template)) == 0
|
||||
|
||||
def wait_for_xpath(self, xpath, **kwds):
|
||||
element = self._wait_on(
|
||||
ec.presence_of_element_located((By.XPATH, xpath)),
|
||||
@@ -59,6 +74,22 @@ class HasDriver:
|
||||
)
|
||||
return element
|
||||
|
||||
def wait_for_present(self, selector_template, **kwds):
|
||||
element = self._wait_on(
|
||||
ec.presence_of_element_located(selector_template.element_locator),
|
||||
"%s to become present" % selector_template.description,
|
||||
**kwds
|
||||
)
|
||||
return element
|
||||
|
||||
def wait_for_visible(self, selector_template, **kwds):
|
||||
element = self._wait_on(
|
||||
ec.visibility_of_element_located(selector_template.element_locator),
|
||||
"%s to become visible" % selector_template.description,
|
||||
**kwds
|
||||
)
|
||||
return element
|
||||
|
||||
def wait_for_selector_visible(self, selector, **kwds):
|
||||
element = self._wait_on(
|
||||
ec.visibility_of_element_located((By.CSS_SELECTOR, selector)),
|
||||
@@ -75,6 +106,14 @@ class HasDriver:
|
||||
)
|
||||
return element
|
||||
|
||||
def wait_for_clickable(self, selector_template, **kwds):
|
||||
element = self._wait_on(
|
||||
ec.element_to_be_clickable(selector_template.element_locator),
|
||||
"%s to become clickable" % selector_template.description,
|
||||
**kwds
|
||||
)
|
||||
return element
|
||||
|
||||
def wait_for_selector_absent_or_hidden(self, selector, **kwds):
|
||||
element = self._wait_on(
|
||||
ec.invisibility_of_element_located((By.CSS_SELECTOR, selector)),
|
||||
@@ -91,6 +130,14 @@ class HasDriver:
|
||||
)
|
||||
return element
|
||||
|
||||
def wait_for_absent_or_hidden(self, selector_template, **kwds):
|
||||
element = self._wait_on(
|
||||
ec.invisibility_of_element_located(selector_template.element_locator),
|
||||
"%s to become absent or hidden" % selector_template.description,
|
||||
**kwds
|
||||
)
|
||||
return element
|
||||
|
||||
def wait_for_id(self, id, **kwds):
|
||||
return self._wait_on(
|
||||
ec.presence_of_element_located((By.ID, id)),
|
||||
|
||||
@@ -17,7 +17,10 @@ import yaml
|
||||
from galaxy.util.bunch import Bunch
|
||||
|
||||
from . import sizzle
|
||||
from .data import NAVIGATION_DATA
|
||||
from .data import (
|
||||
NAVIGATION, # newer variant of NAVIGATION_DATA
|
||||
NAVIGATION_DATA,
|
||||
)
|
||||
from .has_driver import (
|
||||
exception_indicates_not_clickable,
|
||||
exception_indicates_stale_element,
|
||||
@@ -130,6 +133,10 @@ class NavigatesGalaxy(HasDriver):
|
||||
def navigation_data(self):
|
||||
return NAVIGATION_DATA
|
||||
|
||||
@property
|
||||
def navigation(self):
|
||||
return NAVIGATION
|
||||
|
||||
def wait_length(self, wait_type):
|
||||
return wait_type.default_length * self.timeout_multiplier
|
||||
|
||||
@@ -142,11 +149,11 @@ class NavigatesGalaxy(HasDriver):
|
||||
|
||||
def home(self):
|
||||
self.get()
|
||||
self.wait_for_selector_visible("#masthead")
|
||||
self.wait_for_selector_visible("#current-history-panel")
|
||||
self.wait_for_visible(self.navigation.masthead.selector)
|
||||
self.wait_for_visible(self.navigation.history_panel.selector)
|
||||
|
||||
def switch_to_main_panel(self):
|
||||
self.driver.switch_to.frame(self.navigation_data["selectors"]["frames"]["main"])
|
||||
self.driver.switch_to.frame("galaxy_main")
|
||||
|
||||
@contextlib.contextmanager
|
||||
def main_panel(self):
|
||||
@@ -182,12 +189,8 @@ class NavigatesGalaxy(HasDriver):
|
||||
'galaxysession': self.get_galaxy_session()
|
||||
}
|
||||
|
||||
def history_panel_name_selector(self):
|
||||
name_selector = self.test_data["historyPanel"]["selectors"]["history"]["name"]
|
||||
return name_selector
|
||||
|
||||
def history_panel_name_element(self):
|
||||
return self.wait_for_selector(self.history_panel_name_selector())
|
||||
return self.wait_for_present(self.navigation.history_panel.selectors.name)
|
||||
|
||||
@retry_during_transitions
|
||||
def history_panel_name(self):
|
||||
@@ -248,23 +251,25 @@ class NavigatesGalaxy(HasDriver):
|
||||
self.wait(timeout).until(history_has_hid)
|
||||
contents = self.api_get("histories/%s/contents" % current_history_id)
|
||||
history_item = [d for d in contents if d["hid"] == hid][0]
|
||||
history_item_selector = "#%s-%s" % (history_item["history_content_type"], history_item["id"])
|
||||
|
||||
history_item_selector = self.navigation.history_panel.item.selector(
|
||||
history_content_type=history_item["history_content_type"],
|
||||
id=history_item["id"]
|
||||
)
|
||||
try:
|
||||
self.history_item_wait_for_selector(history_item_selector, allowed_force_refreshes)
|
||||
self.history_item_wait_for(history_item_selector, allowed_force_refreshes)
|
||||
except self.TimeoutException as e:
|
||||
dataset_elements = self.driver.find_elements_by_css_selector("#current-history-panel .list-items div")
|
||||
div_ids = [d.get_attribute('id') for d in dataset_elements]
|
||||
contents_elements = self.find_elements(self.navigation.history_panel.selectors.contents)
|
||||
div_ids = [("#" + d.get_attribute('id')) for d in contents_elements]
|
||||
template = "Failed waiting on history item %d to become visible, visible datasets include [%s]."
|
||||
message = template % (hid, ",".join(div_ids))
|
||||
raise self.prepend_timeout_message(e, message)
|
||||
return history_item_selector
|
||||
|
||||
def history_item_wait_for_selector(self, history_item_selector, allowed_force_refreshes):
|
||||
def history_item_wait_for(self, history_item_selector, allowed_force_refreshes):
|
||||
attempt = 0
|
||||
while True:
|
||||
try:
|
||||
rval = self.wait_for_selector_visible(history_item_selector, wait_type=WAIT_TYPES.JOB_COMPLETION)
|
||||
rval = self.wait_for_visible(history_item_selector, wait_type=WAIT_TYPES.JOB_COMPLETION)
|
||||
break
|
||||
except self.TimeoutException:
|
||||
if attempt >= allowed_force_refreshes:
|
||||
@@ -278,23 +283,26 @@ class NavigatesGalaxy(HasDriver):
|
||||
# Use the search box showing up as a proxy that the history display
|
||||
# has left the "loading" state and is showing a valid set of history contents
|
||||
# (even if empty).
|
||||
self.wait_for_selector_visible("#current-history-panel input.search-query", wait_type=WAIT_TYPES.DATABASE_OPERATION)
|
||||
self.wait_for_visible(self.navigation.history_panel.selectors.search, wait_type=WAIT_TYPES.DATABASE_OPERATION)
|
||||
|
||||
def history_panel_wait_for_hid_hidden(self, hid):
|
||||
current_history_id = self.current_history_id()
|
||||
contents = self.api_get("histories/%s/contents" % current_history_id)
|
||||
history_item = [d for d in contents if d["hid"] == hid][0]
|
||||
history_item_selector = "#%s-%s" % (history_item["history_content_type"], history_item["id"])
|
||||
self.wait_for_selector_absent(history_item_selector, wait_type=WAIT_TYPES.JOB_COMPLETION)
|
||||
history_item_selector = self.navigation.history_panel.item.selector(
|
||||
history_content_type=history_item["history_content_type"],
|
||||
id=history_item["id"]
|
||||
)
|
||||
self.wait_for_absent_or_hidden(history_item_selector, wait_type=WAIT_TYPES.JOB_COMPLETION)
|
||||
return history_item_selector
|
||||
|
||||
def history_panel_wait_for_hid_state(self, hid, state, allowed_force_refreshes=0):
|
||||
history_item_selector = self.history_panel_wait_for_hid_visible(hid, allowed_force_refreshes=allowed_force_refreshes)
|
||||
history_item_selector_state = "%s.state-%s" % (history_item_selector, state)
|
||||
history_item_selector_state = history_item_selector.with_class("state-%s" % state)
|
||||
try:
|
||||
self.history_item_wait_for_selector(history_item_selector_state, allowed_force_refreshes)
|
||||
self.history_item_wait_for(history_item_selector_state, allowed_force_refreshes)
|
||||
except self.TimeoutException as e:
|
||||
history_item = self.driver.find_element_by_css_selector(history_item_selector)
|
||||
history_item = self.wait_for_visible(history_item_selector)
|
||||
current_state = "UNKNOWN"
|
||||
classes = history_item.get_attribute("class").split(" ")
|
||||
for clazz in classes:
|
||||
@@ -348,10 +356,10 @@ class NavigatesGalaxy(HasDriver):
|
||||
}
|
||||
|
||||
self.click_masthead_user()
|
||||
self.click_label(self.navigation_data["labels"]["masthead"]["userMenu"]["login"])
|
||||
self.wait_for_and_click(self.navigation.masthead.labels.login)
|
||||
|
||||
with self.main_panel():
|
||||
form = self.wait_for_selector(self.navigation_data["selectors"]["loginPage"]["form"])
|
||||
form = self.wait_for_visible(self.navigation.login.selectors.form)
|
||||
self.fill(form, login_info)
|
||||
self.snapshot("logging-in")
|
||||
self.click_submit(form)
|
||||
@@ -380,17 +388,16 @@ class NavigatesGalaxy(HasDriver):
|
||||
|
||||
self.home()
|
||||
self.click_masthead_user()
|
||||
self.click_label(self.navigation_data["labels"]["masthead"]["userMenu"]["register"])
|
||||
self.wait_for_and_click(self.navigation.masthead.labels.register)
|
||||
with self.main_panel():
|
||||
register_form_id = self.navigation_data["selectors"]["registrationPage"]["form"]
|
||||
form = self.wait_for_id(register_form_id)
|
||||
form = self.wait_for_visible(self.navigation.registration.selectors.form)
|
||||
self.fill(form, dict(
|
||||
email=email,
|
||||
password=password,
|
||||
username=username,
|
||||
confirm=confirm
|
||||
))
|
||||
self.click_xpath(self.navigation_data["selectors"]["registrationPage"]["submit_xpath"])
|
||||
self.wait_for_and_click(self.navigation.registration.selectors.submit)
|
||||
# Give the browser a bit of time to submit the request.
|
||||
# It would be good to eliminate this sleep, but it can't be because Galaxy
|
||||
# doesn't swap the "User" menu automatically after it registers a user and
|
||||
@@ -409,7 +416,7 @@ class NavigatesGalaxy(HasDriver):
|
||||
# Make sure the user menu was dropped down
|
||||
user_menu = self.wait_for_selector_visible("ul.nav#user .dropdown-menu")
|
||||
try:
|
||||
user_email_element = self.wait_for_xpath_visible(self.navigation_data["selectors"]["masthead"]["userMenu"]["userEmail_xpath"])
|
||||
user_email_element = self.wait_for_visible(self.navigation.masthead.selectors.user_email)
|
||||
except self.TimeoutException as e:
|
||||
menu_items = user_menu.find_elements_by_css_selector("li a")
|
||||
menu_text = [mi.text for mi in menu_items]
|
||||
@@ -425,7 +432,7 @@ class NavigatesGalaxy(HasDriver):
|
||||
|
||||
def wait_for_logged_in(self):
|
||||
try:
|
||||
self.wait_for_selector_visible("a.loggedin-only")
|
||||
self.wait_for_visible(self.navigation.masthead.selectors.logged_in_only)
|
||||
except self.TimeoutException as e:
|
||||
user_info = self.api_get("users/current")
|
||||
if "username" in user_info:
|
||||
@@ -709,13 +716,16 @@ class NavigatesGalaxy(HasDriver):
|
||||
self.wait_for_and_click_selector("button#execute")
|
||||
|
||||
def click_masthead_user(self):
|
||||
self.click_xpath(self.navigation_data["selectors"]["masthead"]["user"])
|
||||
self.wait_for_and_click(self.navigation.masthead.selectors.user)
|
||||
|
||||
def click_masthead_libraries(self):
|
||||
self.wait_for_and_click(self.navigation.masthead.labels.libraries)
|
||||
|
||||
def click_masthead_workflow(self):
|
||||
self.click_xpath(self.navigation_data["selectors"]["masthead"]["workflow"])
|
||||
self.wait_for_and_click(self.navigation.masthead.labels.workflow)
|
||||
|
||||
def click_button_new_workflow(self):
|
||||
self.wait_for_and_click_selector(self.navigation_data["selectors"]["workflows"]["new_button"])
|
||||
self.wait_for_and_click(self.navigation.workflows.selectors.new_button)
|
||||
|
||||
def wait_for_sizzle_selector_clickable(self, selector):
|
||||
element = self._wait_on(
|
||||
@@ -741,16 +751,11 @@ class NavigatesGalaxy(HasDriver):
|
||||
self.click_history_options()
|
||||
|
||||
# Click labelled option
|
||||
menu_selector = self.history_options_menu_selector()
|
||||
self.wait_for_selector_visible(menu_selector)
|
||||
self.wait_for_visible(self.navigation.history_panel.selectors.options_menu)
|
||||
menu_item_sizzle_selector = '#history-options-button-menu > li > a:contains("%s")' % option_label
|
||||
menu_selection_element = self.wait_for_sizzle_selector_clickable(menu_item_sizzle_selector)
|
||||
menu_selection_element.click()
|
||||
|
||||
def history_options_menu_selector(self):
|
||||
menu_selector = self.test_data["historyOptions"]["selectors"]["menu"]
|
||||
return menu_selector
|
||||
|
||||
@retry_during_transitions
|
||||
def histories_click_advanced_search(self):
|
||||
search_selector = '#standard-search .advanced-search-toggle'
|
||||
@@ -777,36 +782,31 @@ class NavigatesGalaxy(HasDriver):
|
||||
self.send_enter(editable_text_input_element)
|
||||
|
||||
def history_panel_click_to_rename(self):
|
||||
self.wait_for_and_click_selector(self.history_panel_name_selector())
|
||||
return self.wait_for_selector(self.history_panel_edit_title_input_selector())
|
||||
|
||||
def history_panel_edit_title_input_selector(self):
|
||||
return self.test_data["historyPanel"]["selectors"]["history"]["nameEditableTextInput"]
|
||||
self.wait_for_and_click(self.navigation.history_panel.selectors.name)
|
||||
return self.wait_for_visible(self.navigation.history_panel.selectors.name_edit_input)
|
||||
|
||||
def history_panel_refresh_click(self):
|
||||
self.wait_for_and_click_selector("#history-refresh-button")
|
||||
|
||||
def history_panel_multi_operations_selector(self):
|
||||
return self.test_data["historyPanel"]["selectors"]["history"]["multiOperationsIcon"]
|
||||
self.wait_for_and_click(self.navigation.history_panel.selectors.refresh_button)
|
||||
|
||||
def history_panel_multi_operations_show(self):
|
||||
operations_selector = self.history_panel_multi_operations_selector()
|
||||
self.wait_for_and_click_selector(operations_selector)
|
||||
return self.wait_for_and_click(self.navigation.history_panel.multi_operations.selectors.show_button)
|
||||
|
||||
def history_panel_muli_operation_select_hid(self, hid):
|
||||
item_selector = self.history_panel_item_selector(hid, wait=True)
|
||||
operation_radio_selector = "%s .selector" % item_selector
|
||||
self.wait_for_and_click_selector(operation_radio_selector)
|
||||
|
||||
def history_panel_multi_operation_action_selector(self):
|
||||
return self.test_data["historyPanel"]["selectors"]["history"]["multiOperationsActionBtn"]
|
||||
|
||||
def history_panel_multi_operation_action_click(self, action):
|
||||
time.sleep(5)
|
||||
self.wait_for_and_click_selector(self.history_panel_multi_operation_action_selector())
|
||||
menu_element = self.wait_for_selector_visible(".list-action-menu.open")
|
||||
action_element = menu_element.find_element_by_link_text(action)
|
||||
action_element.click()
|
||||
# Maybe isn't needed?
|
||||
# self.sleep_for(WAIT_TYPES.UX_RENDER)
|
||||
self.wait_for_and_click(self.navigation.history_panel.multi_operations.selectors.action_button)
|
||||
|
||||
@retry_during_transitions
|
||||
def _click_action_in_menu():
|
||||
menu_element = self.wait_for_visible(self.navigation.history_panel.multi_operations.selectors.action_menu)
|
||||
menu_element.find_element_by_link_text(action.text).click()
|
||||
|
||||
_click_action_in_menu()
|
||||
|
||||
def history_panel_item_click_visualization_menu(self, hid):
|
||||
viz_button_selector = "%s %s" % (self.history_panel_item_selector(hid), ".visualizations-dropdown")
|
||||
@@ -917,7 +917,7 @@ class NavigatesGalaxy(HasDriver):
|
||||
if self.is_logged_in():
|
||||
self.home()
|
||||
self.click_masthead_user()
|
||||
self.click_label(self.navigation_data["labels"]["masthead"]["userMenu"]["logout"])
|
||||
self.wait_for_and_click(self.navigation.masthead.labels.logout)
|
||||
self.click_label('go to the home page')
|
||||
assert not self.is_logged_in()
|
||||
|
||||
@@ -986,6 +986,8 @@ class NavigatesGalaxy(HasDriver):
|
||||
return self.assert_selector_absent_or_hidden(selector)
|
||||
|
||||
def assert_tooltip_text(self, element, expected, sleep=0, click_away=True):
|
||||
if hasattr(expected, "text"):
|
||||
expected = expected.text
|
||||
text = self.get_tooltip_text(element, sleep=sleep, click_away=click_away)
|
||||
assert text == expected, "Tooltip text [%s] was not expected text [%s]." % (text, expected)
|
||||
|
||||
@@ -1038,6 +1040,12 @@ class NavigatesGalaxy(HasDriver):
|
||||
element.click()
|
||||
return element
|
||||
|
||||
@retry_during_transitions
|
||||
def wait_for_and_click(self, selector_template):
|
||||
element = self.wait_for_clickable(selector_template)
|
||||
element.click()
|
||||
return element
|
||||
|
||||
def select2_set_value(self, container_selector, value, with_click=True):
|
||||
# There are two hacky was to select things from the select2 widget -
|
||||
# with_click=True: This simulates the mouse click after the suggestion contains
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# Deprecated navigation constants - prefer navigation.yml.
|
||||
selectors:
|
||||
tooltipBalloon: '.tooltip'
|
||||
editableText: '.editable-text'
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
# top_level: {global: <components>, workflows: <components>, histories: <components>}
|
||||
# components := {<subcomponent_name>: <components>} | <component>
|
||||
# component := {selectors: <selectors_map>, labels: <labels_map>, ids: <id_map>}
|
||||
# selectors_map := {<selector_name>: <selector>}
|
||||
# selector := <str> | {type: <selector_type>, selector: <str>}
|
||||
# selector_type := css|xpath|sizzle|id
|
||||
# labels_map := {<label_name>: <label>}
|
||||
|
||||
_: # global stuff
|
||||
|
||||
selectors:
|
||||
editable_text: '.editable-text'
|
||||
|
||||
masthead:
|
||||
|
||||
selectors:
|
||||
_: '#masthead'
|
||||
|
||||
user:
|
||||
type: xpath
|
||||
selector: '//ul[@id="user"]'
|
||||
|
||||
workflow:
|
||||
type: xpath
|
||||
selector: '//ul[@id="workflow"]'
|
||||
|
||||
user_email:
|
||||
type: xpath
|
||||
selector: '//a[contains(text(), "Logged in as")]'
|
||||
|
||||
logged_in_only: 'a.loggedin-only'
|
||||
|
||||
labels:
|
||||
# top-level menus
|
||||
analyze: 'Analyze Data'
|
||||
workflow: 'Workflow'
|
||||
libraries: 'Shared Data'
|
||||
visualization: 'Visualization'
|
||||
help: 'Help'
|
||||
user: 'User'
|
||||
|
||||
# user menu
|
||||
register: 'Register'
|
||||
login: 'Login'
|
||||
logout: 'Logout'
|
||||
|
||||
history_panel:
|
||||
menu:
|
||||
labels:
|
||||
new: 'Create New'
|
||||
|
||||
item:
|
||||
selectors:
|
||||
_: '#${history_content_type}-${id}'
|
||||
|
||||
title: '${item_selector} .title'
|
||||
hid: '${item_selector} .hid'
|
||||
name: '${item_selector} .name'
|
||||
titleButtonArea: '${item_selector} .primary-actions'
|
||||
summary: '${item_selector} .summary'
|
||||
blurb: '${item_selector} .blurb .value'
|
||||
dbkey: '${item_selector} .dbkey .value'
|
||||
info: '${item_selector} .info .value'
|
||||
body: '${item_selector} .details'
|
||||
|
||||
multi_operations:
|
||||
selectors:
|
||||
show_button: '#current-history-panel .actions .show-selectors-btn'
|
||||
action_button: '#current-history-panel .list-action-menu .list-action-menu-btn'
|
||||
action_menu: '.list-action-menu.open'
|
||||
|
||||
labels:
|
||||
build_pair: "Build Dataset Pair"
|
||||
build_list: "Build Dataset List"
|
||||
build_list_pairs: "Build List of Dataset Pairs"
|
||||
|
||||
selectors:
|
||||
_: '#current-history-panel'
|
||||
search: '#current-history-panel input.search-query'
|
||||
refresh_button: '#history-refresh-button'
|
||||
name: '.title .name'
|
||||
name_edit_input: '.name input'
|
||||
contents: '#current-history-panel .list-items div.history-content'
|
||||
|
||||
empty_message: '.empty-message'
|
||||
|
||||
tag_icon: '.actions .history-tag-btn'
|
||||
tag_area: '.controls .tags-display'
|
||||
annotation_icon: '.actions .history-annotate-btn'
|
||||
annotation_area: '.controls .annotation-display'
|
||||
annotation_editable_text: '.controls .annotation-display .annotation.editable-text'
|
||||
annotation_edit: '.controls .annotation-display .annotation textarea'
|
||||
annotation_done: '.controls .annotation-display .annotation button'
|
||||
|
||||
options_button: '#history-options-button'
|
||||
options_button_icon: '#history-options-button span.fa-cog'
|
||||
options_menu: '#history-options-button-menu'
|
||||
|
||||
text:
|
||||
tooltip_name: 'Click to rename history'
|
||||
|
||||
collection_builders:
|
||||
|
||||
selectors:
|
||||
|
||||
clear_filters: "a.clear-filters-link"
|
||||
forward_datasets: ".forward-column .column-datasets"
|
||||
reverse_datasets: ".reverse-column .column-datasets"
|
||||
|
||||
login:
|
||||
selectors:
|
||||
form: 'form#login'
|
||||
|
||||
registration:
|
||||
selectors:
|
||||
form: '#registration'
|
||||
submit:
|
||||
type: xpath
|
||||
selector: "//input[@value='Submit']"
|
||||
|
||||
workflows:
|
||||
|
||||
selectors:
|
||||
new_button: '#new-workflow'
|
||||
import_button: '#import-workflow'
|
||||
|
||||
published_grids:
|
||||
selectors:
|
||||
search: '#input-free-text-search-filter'
|
||||
@@ -11,19 +11,19 @@ class AnonymousHistoriesTestCase(SeleniumTestCase):
|
||||
self.home()
|
||||
self.assert_initial_history_panel_state_correct()
|
||||
|
||||
tag_icon_selector = self.test_data["historyPanel"]["selectors"]["history"]["tagIcon"]
|
||||
anno_icon_selector = self.test_data["historyPanel"]["selectors"]["history"]["annoIcon"]
|
||||
self.assert_selector_absent(tag_icon_selector)
|
||||
self.assert_selector_absent(anno_icon_selector)
|
||||
tag_icon_selector = self.navigation.history_panel.selectors.tag_icon
|
||||
annotation_icon_selector = self.navigation.history_panel.selectors.annotation_icon
|
||||
self.assert_absent_or_hidden(tag_icon_selector)
|
||||
self.assert_absent_or_hidden(annotation_icon_selector)
|
||||
|
||||
name_selector = self.test_data["historyPanel"]["selectors"]["history"]["name"]
|
||||
name_element = self.wait_for_selector(name_selector)
|
||||
|
||||
# name should NOT be editable when clicked by anon-user
|
||||
editable_text_class = self.test_data["selectors"]["editableText"]
|
||||
assert editable_text_class not in name_element.get_attribute("class")
|
||||
# History has a name but...
|
||||
name_element = self.wait_for_present(self.navigation.history_panel.selectors.name)
|
||||
name_element.click()
|
||||
|
||||
# ... name should NOT be editable when clicked by anon-user
|
||||
editable_text_class = self.navigation._.selectors.editable_text
|
||||
assert editable_text_class.as_css_class not in name_element.get_attribute("class")
|
||||
|
||||
@selenium_test
|
||||
def test_anon_history_upload(self):
|
||||
self.home()
|
||||
@@ -35,8 +35,7 @@ class AnonymousHistoriesTestCase(SeleniumTestCase):
|
||||
assert 'state-ok' in element.get_attribute("class")
|
||||
|
||||
# empty should be NO LONGER be displayed
|
||||
empty_msg_selector = self.test_data["historyPanel"]["selectors"]["history"]["emptyMsg"]
|
||||
self.assert_selector_absent_or_hidden(empty_msg_selector)
|
||||
self.assert_absent_or_hidden(self.navigation.history_panel.selectors.empty_message)
|
||||
|
||||
@selenium_test
|
||||
def test_anon_history_after_registration(self):
|
||||
|
||||
@@ -14,7 +14,7 @@ class CollectionBuildersTestCase(SeleniumTestCase):
|
||||
self.history_panel_wait_for_hid_ok(1)
|
||||
self.history_panel_multi_operations_show()
|
||||
self.history_panel_muli_operation_select_hid(1)
|
||||
self.history_panel_multi_operation_action_click("Build Dataset List")
|
||||
self.history_panel_multi_operation_action_click(self.navigation.history_panel.multi_operations.labels.build_list)
|
||||
|
||||
self.collection_builder_set_name("my cool list")
|
||||
|
||||
@@ -27,7 +27,7 @@ class CollectionBuildersTestCase(SeleniumTestCase):
|
||||
self.history_panel_wait_for_hid_ok(1)
|
||||
self.history_panel_multi_operations_show()
|
||||
self.history_panel_muli_operation_select_hid(1)
|
||||
self.history_panel_multi_operation_action_click("Build Dataset List")
|
||||
self.history_panel_multi_operation_action_click(self.navigation.history_panel.multi_operations.labels.build_list)
|
||||
|
||||
self.collection_builder_hide_originals()
|
||||
self.collection_builder_set_name("my cool list")
|
||||
@@ -46,7 +46,7 @@ class CollectionBuildersTestCase(SeleniumTestCase):
|
||||
self.history_panel_multi_operations_show()
|
||||
self.history_panel_muli_operation_select_hid(1)
|
||||
self.history_panel_muli_operation_select_hid(2)
|
||||
self.history_panel_multi_operation_action_click("Build Dataset Pair")
|
||||
self.history_panel_multi_operation_action_click(self.navigation.history_panel.multi_operations.labels.build_pair)
|
||||
self.collection_builder_set_name("my awesome pair")
|
||||
|
||||
self.collection_builder_create()
|
||||
@@ -61,7 +61,7 @@ class CollectionBuildersTestCase(SeleniumTestCase):
|
||||
self.history_panel_multi_operations_show()
|
||||
self.history_panel_muli_operation_select_hid(1)
|
||||
self.history_panel_muli_operation_select_hid(2)
|
||||
self.history_panel_multi_operation_action_click("Build List of Dataset Pairs")
|
||||
self.history_panel_multi_operation_action_click(self.navigation.history_panel.multi_operations.labels.build_list_pairs)
|
||||
|
||||
self.collection_builder_clear_filters()
|
||||
self.collection_builder_click_paired_item("forward", 0)
|
||||
@@ -80,16 +80,15 @@ class CollectionBuildersTestCase(SeleniumTestCase):
|
||||
self.history_panel_multi_operations_show()
|
||||
self.history_panel_muli_operation_select_hid(1)
|
||||
self.history_panel_muli_operation_select_hid(2)
|
||||
self.history_panel_multi_operation_action_click("Build List of Dataset Pairs")
|
||||
self.history_panel_multi_operation_action_click(self.navigation.history_panel.multi_operations.labels.build_list_pairs)
|
||||
|
||||
clear_filter_link = self.wait_for_selector_visible("a.clear-filters-link")
|
||||
clear_filter_link.click()
|
||||
self.wait_for_and_click(self.navigation.collection_builders.selectors.clear_filters)
|
||||
|
||||
forward_column = self.wait_for_selector_visible(".forward-column .column-datasets")
|
||||
forward_column = self.wait_for_visible(self.navigation.collection_builders.selectors.forward_datasets)
|
||||
first_datset_forward = forward_column.find_elements_by_css_selector("li")[0]
|
||||
first_datset_forward.click()
|
||||
|
||||
reverse_column = self.wait_for_selector_visible(".reverse-column .column-datasets")
|
||||
reverse_column = self.wait_for_visible(self.navigation.collection_builders.selectors.reverse_datasets)
|
||||
second_dataset_reverse = reverse_column.find_elements_by_css_selector("li")[1]
|
||||
second_dataset_reverse.click()
|
||||
|
||||
|
||||
@@ -90,8 +90,6 @@ class CustomBuildsTestcase(SharedStateSeleniumTestCase):
|
||||
def navigate_to_custom_builds_page(self):
|
||||
self.home()
|
||||
self.click_masthead_user() # Open masthead menu
|
||||
label = self.navigation_data['labels']['masthead']['menus']['user']
|
||||
self.click_label(label)
|
||||
self.wait_for_and_click_selector('a[href="/custom_builds"]')
|
||||
|
||||
def setup_shared_state(self):
|
||||
|
||||
@@ -13,13 +13,13 @@ class HistoryOptionsTestCase(SeleniumTestCase):
|
||||
self.wait_for_history()
|
||||
self.click_history_options()
|
||||
|
||||
menu_selector = self.history_options_menu_selector()
|
||||
self.wait_for_selector_visible(menu_selector)
|
||||
menu_selector = self.navigation.history_panel.selectors.options_menu
|
||||
self.wait_for_visible(menu_selector)
|
||||
|
||||
# Click away closes history options
|
||||
self.click_center()
|
||||
|
||||
self.assert_selector_absent_or_hidden(menu_selector)
|
||||
self.wait_for_absent_or_hidden(menu_selector)
|
||||
|
||||
hda = self.latest_history_item()
|
||||
hid = hda["hid"]
|
||||
|
||||
@@ -11,13 +11,14 @@ class HistoryPanelTestCase(SeleniumTestCase):
|
||||
self.register()
|
||||
self.assert_initial_history_panel_state_correct()
|
||||
|
||||
tag_icon_selector = self.test_data["historyPanel"]["selectors"]["history"]["tagIcon"]
|
||||
anno_icon_selector = self.test_data["historyPanel"]["selectors"]["history"]["annoIcon"]
|
||||
self.assert_selector(tag_icon_selector)
|
||||
self.assert_selector(anno_icon_selector)
|
||||
tag_icon_selector = self.navigation.history_panel.selectors.tag_icon
|
||||
annotation_icon_selector = self.navigation.history_panel.selectors.annotation_icon
|
||||
|
||||
self.wait_for_visible(tag_icon_selector)
|
||||
self.wait_for_visible(annotation_icon_selector)
|
||||
|
||||
name_element = self.history_panel_name_element()
|
||||
self.assert_tooltip_text(name_element, self.test_data["historyPanel"]["text"]["history"]["tooltips"]["name"])
|
||||
self.assert_tooltip_text(name_element, self.navigation.history_panel.text.tooltip_name)
|
||||
|
||||
@selenium_test
|
||||
def test_history_panel_rename(self):
|
||||
@@ -34,7 +35,7 @@ class HistoryPanelTestCase(SeleniumTestCase):
|
||||
editable_text_input_element = self.history_panel_click_to_rename()
|
||||
editable_text_input_element.send_keys("New History Name")
|
||||
self.click_center()
|
||||
self.assert_selector_absent(self.history_panel_edit_title_input_selector())
|
||||
self.assert_absent(self.navigation.history_panel.selectors.name_edit_input)
|
||||
assert "New History Name" not in self.history_panel_name()
|
||||
|
||||
@selenium_test
|
||||
@@ -43,7 +44,7 @@ class HistoryPanelTestCase(SeleniumTestCase):
|
||||
editable_text_input_element = self.history_panel_click_to_rename()
|
||||
editable_text_input_element.send_keys("New History Name")
|
||||
self.send_escape(editable_text_input_element)
|
||||
self.assert_selector_absent(self.history_panel_edit_title_input_selector())
|
||||
self.assert_absent(self.navigation.history_panel.selectors.name_edit_input)
|
||||
assert "New History Name" not in self.history_panel_name()
|
||||
|
||||
@selenium_test
|
||||
@@ -51,34 +52,35 @@ class HistoryPanelTestCase(SeleniumTestCase):
|
||||
# TODO: Test actually editing these values.
|
||||
self.register()
|
||||
|
||||
tag_icon_selector = self.test_data["historyPanel"]["selectors"]["history"]["tagIcon"]
|
||||
anno_icon_selector = self.test_data["historyPanel"]["selectors"]["history"]["annoIcon"]
|
||||
tag_area_selector = self.test_data["historyPanel"]["selectors"]["history"]["tagArea"]
|
||||
anno_area_selector = self.test_data["historyPanel"]["selectors"]["history"]["annoArea"]
|
||||
tag_icon_selector = self.navigation.history_panel.selectors.tag_icon
|
||||
annotation_icon_selector = self.navigation.history_panel.selectors.annotation_icon
|
||||
|
||||
tag_icon = self.wait_for_selector_clickable(tag_icon_selector)
|
||||
annon_icon = self.wait_for_selector_clickable(anno_icon_selector)
|
||||
tag_area_selector = self.navigation.history_panel.selectors.tag_area
|
||||
annotation_area_selector = self.navigation.history_panel.selectors.annotation_area
|
||||
|
||||
self.assert_selector_absent_or_hidden(tag_area_selector)
|
||||
self.assert_selector_absent_or_hidden(anno_area_selector)
|
||||
tag_icon = self.wait_for_clickable(tag_icon_selector)
|
||||
annon_icon = self.wait_for_clickable(annotation_icon_selector)
|
||||
|
||||
self.assert_absent_or_hidden(tag_area_selector)
|
||||
self.assert_absent_or_hidden(annotation_area_selector)
|
||||
|
||||
tag_icon.click()
|
||||
|
||||
self.wait_for_selector(tag_area_selector)
|
||||
self.assert_selector_absent_or_hidden(anno_area_selector)
|
||||
self.wait_for_visible(tag_area_selector)
|
||||
self.assert_absent_or_hidden(annotation_area_selector)
|
||||
|
||||
tag_icon.click()
|
||||
self.sleep_for(self.wait_types.UX_TRANSITION)
|
||||
annon_icon.click()
|
||||
|
||||
self.wait_for_selector(anno_area_selector)
|
||||
self.assert_selector_absent_or_hidden(tag_area_selector)
|
||||
self.wait_for_visible(annotation_area_selector)
|
||||
self.assert_absent_or_hidden(tag_area_selector)
|
||||
|
||||
annon_icon.click()
|
||||
self.sleep_for(self.wait_types.UX_TRANSITION)
|
||||
|
||||
self.assert_selector_absent_or_hidden(tag_area_selector)
|
||||
self.assert_selector_absent_or_hidden(anno_area_selector)
|
||||
self.assert_absent_or_hidden(tag_area_selector)
|
||||
self.assert_absent_or_hidden(annotation_area_selector)
|
||||
|
||||
@selenium_test
|
||||
def test_refresh_preserves_state(self):
|
||||
|
||||
@@ -147,24 +147,23 @@ class HistoryGridTestCase(SharedStateSeleniumTestCase):
|
||||
self.sleep_for(self.wait_types.UX_RENDER)
|
||||
|
||||
def set_annotation(self, annotation):
|
||||
anno_icon_selector = self.test_data['historyPanel']['selectors']['history']['annoIcon']
|
||||
anno_area_selector = self.test_data['historyPanel']['selectors']['history']['annoArea']
|
||||
self.ensure_annotation_area_displayed()
|
||||
|
||||
if not self.selector_is_displayed(anno_area_selector):
|
||||
self.wait_for_and_click_selector(anno_icon_selector)
|
||||
self.wait_for_and_click(self.navigation.history_panel.selectors.annotation_editable_text)
|
||||
|
||||
anno_area_selector += ' .annotation'
|
||||
self.wait_for_and_click_selector(anno_area_selector)
|
||||
annon_area_editable = self.wait_for_and_click(self.navigation.history_panel.selectors.annotation_edit)
|
||||
anno_done_button = self.wait_for_clickable(self.navigation.history_panel.selectors.annotation_done)
|
||||
|
||||
area_editable_selector = anno_area_selector + ' textarea'
|
||||
done_button_selector = anno_area_selector + ' button'
|
||||
annon_area_editable = self.wait_for_selector_clickable(area_editable_selector)
|
||||
anno_done_button = self.wait_for_selector_clickable(done_button_selector)
|
||||
|
||||
annon_area_editable.click()
|
||||
annon_area_editable.send_keys(annotation)
|
||||
anno_done_button.click()
|
||||
|
||||
def ensure_annotation_area_displayed(self):
|
||||
annotation_area_selector = self.navigation.history_panel.selectors.annotation_area
|
||||
annotation_icon_selector = self.navigation.history_panel.selectors.annotation_icon
|
||||
|
||||
if not self.is_displayed(annotation_area_selector):
|
||||
self.wait_for_and_click(annotation_icon_selector)
|
||||
|
||||
def setup_shared_state(self):
|
||||
tag1 = self._get_random_name(len=5)
|
||||
tag2 = self._get_random_name(len=5)
|
||||
@@ -210,8 +209,6 @@ class HistoryGridTestCase(SharedStateSeleniumTestCase):
|
||||
|
||||
def navigate_to_published_histories_page(self):
|
||||
self.home()
|
||||
self.click_masthead_user() # Open masthead menu
|
||||
self.click_label(
|
||||
self.navigation_data['labels']['masthead']['menus']['libraries'])
|
||||
self.click_masthead_libraries()
|
||||
selector = 'a[href="/histories/list_published"]'
|
||||
self.wait_for_and_click_selector(selector)
|
||||
|
||||
@@ -275,8 +275,6 @@ class SavedHistoriesTestCase(SharedStateSeleniumTestCase):
|
||||
def navigate_to_saved_histories_page(self):
|
||||
self.home()
|
||||
self.click_masthead_user() # Open masthead menu
|
||||
label = self.navigation_data['labels']['masthead']['menus']['user']
|
||||
self.click_label(label)
|
||||
self.wait_for_and_click_selector('a[href="/histories/list"]')
|
||||
|
||||
def setup_shared_state(self):
|
||||
|
||||
@@ -17,8 +17,8 @@ class UploadsTestCase(SeleniumTestCase, UsesHistoryItemAssertions):
|
||||
assert history_count == 1, "Incorrect number of items in history - expected 1, found %d" % history_count
|
||||
|
||||
hda = history_contents[0]
|
||||
assert hda["name"] == '1.sam'
|
||||
assert hda["extension"] == "sam"
|
||||
assert hda["name"] == '1.sam', hda
|
||||
assert hda["extension"] == "sam", hda
|
||||
|
||||
self.history_panel_click_item_title(hid=1, wait=True)
|
||||
self.assert_item_dbkey_displayed_as(1, "?")
|
||||
|
||||
Reference in New Issue
Block a user