Merge branch 'dev' into ftp_uploader

This commit is contained in:
guerler
2017-10-04 08:25:30 -04:00
34 changed files with 1412 additions and 271 deletions
@@ -75,6 +75,9 @@ var HistoryView = _super.extend(
// control contents/behavior based on where (and in what context) the panel is being used
/** where should pages from links be displayed? (default to new tab/window) */
this.linkTarget = attributes.linkTarget || '_blank';
/** timeout id for detailed fetch of collection counts, etc... */
this.detailedFetchTimeoutId = null;
},
/** create and return a collection for when none is initially passed */
@@ -89,9 +92,18 @@ var HistoryView = _super.extend(
if( this.model ){
this.model.clearUpdateTimeout();
}
this._clearDetailedFetchTimeout();
return this;
},
/** clear the timeout and the cached timeout id */
_clearDetailedFetchTimeout : function(){
if( this.detailedFetchTimeoutId ){
clearTimeout( this.detailedFetchTimeoutId );
this.detailedFetchTimeoutId = null;
}
},
/** create any event listeners for the panel
* @fires: rendered:initial on the first render
* @fires: empty-history when switching to a history with no contents or creating a new history
@@ -105,7 +117,8 @@ var HistoryView = _super.extend(
'loading-done' : function(){
var self = this;
// after the initial load, decorate with more time consuming fields (like HDCA element_counts)
_.delay( function(){
self.detailedFetchTimeoutId = _.delay( function(){
self.detailedFetchTimeoutId = null;
self.model.contents.fetchCollectionCounts();
}, self.FETCH_COLLECTION_COUNTS_DELAY );
},
+2
View File
@@ -234,6 +234,7 @@
<datatype extension="sf3" type="galaxy.datatypes.proteomics:Sf3" display_in_upload="true" />
<datatype extension="cps" type="galaxy.datatypes.binary:Binary" subclass="true" display_in_upload="true" />
<datatype extension="ct" type="galaxy.datatypes.tabular:ConnectivityTable" display_in_upload="true"/>
<datatype extension="postgresql" type="galaxy.datatypes.binary:PostgresqlArchive" subclass="True" display_in_upload="True"/>
<datatype extension="searchgui_archive" type="galaxy.datatypes.binary:SearchGuiArchive" display_in_upload="true"/>
<datatype extension="fast5.tar" type="galaxy.datatypes.binary:Fast5Archive" display_in_upload="true"/>
<datatype extension="fast5.tar.gz" type="galaxy.datatypes.binary:Fast5ArchiveGz" display_in_upload="true"/>
@@ -679,6 +680,7 @@
<sniffer type="galaxy.datatypes.binary:Fast5ArchiveGz" />
<sniffer type="galaxy.datatypes.binary:Fast5ArchiveBz2" />
<sniffer type="galaxy.datatypes.binary:Fast5Archive" />
<sniffer type="galaxy.datatypes.binary:PostgresqlArchive"/>
<sniffer type="galaxy.datatypes.triples:Rdf"/>
<sniffer type="galaxy.datatypes.blast:BlastXml"/>
<sniffer type="galaxy.datatypes.xml:Phyloxml"/>
+53
View File
@@ -1566,6 +1566,59 @@ Binary.register_sniffable_binary_format("oxli.graphlabels", "oxligl",
OxliGraphLabels)
class PostgresqlArchive(CompressedArchive):
"""
Class describing a Postgresql database packed into a tar archive
>>> from galaxy.datatypes.sniff import get_test_fname
>>> fname = get_test_fname( 'postgresql_fake.tar.bz2' )
>>> PostgresqlArchive().sniff( fname )
True
>>> fname = get_test_fname( 'test.fast5.tar' )
>>> PostgresqlArchive().sniff( fname )
False
"""
MetadataElement(name="version", default=None, param=MetadataParameter, desc="PostgreSQL database version",
readonly=True, visible=True, no_value=None)
file_ext = "postgresql"
def set_meta(self, dataset, overwrite=True, **kwd):
super(PostgresqlArchive, self).set_meta(dataset, overwrite=overwrite, **kwd)
try:
if dataset and tarfile.is_tarfile(dataset.file_name):
with tarfile.open(dataset.file_name, 'r') as temptar:
pg_version_file = temptar.extractfile('postgresql/db/PG_VERSION')
dataset.metadata.version = pg_version_file.read().strip()
except Exception as e:
log.warning('%s, set_meta Exception: %s', self, e)
def sniff(self, filename):
if filename and tarfile.is_tarfile(filename):
try:
with tarfile.open(filename, 'r') as temptar:
return 'postgresql/db/PG_VERSION' in temptar.getnames()
except Exception as e:
log.warning('%s, sniff Exception: %s', self, e)
return False
def set_peek(self, dataset, is_multi_byte=False):
if not dataset.dataset.purged:
dataset.peek = "PostgreSQL Archive (%s)" % (nice_size(dataset.get_size()))
dataset.blurb = "PostgreSQL version %s" % (dataset.metadata.version or 'unknown')
else:
dataset.peek = 'file does not exist'
dataset.blurb = 'file purged from disk'
def display_peek(self, dataset):
try:
return dataset.peek
except:
return "PostgreSQL Archive (%s)" % (nice_size(dataset.get_size()))
Binary.register_sniffable_binary_format("postgresql_archiv", "postgresql", PostgresqlArchive)
class Fast5Archive(CompressedArchive):
"""
Class describing a FAST5 archive
Binary file not shown.
+2 -2
View File
@@ -87,8 +87,8 @@ def set_datatypes_registry(d_registry):
class HasTags(object):
dict_collection_visible_keys = ('tags')
dict_element_visible_keys = ('tags')
dict_collection_visible_keys = ('tags',)
dict_element_visible_keys = ('tags',)
def to_dict(self, *args, **kwargs):
rval = super(HasTags, self).to_dict(*args, **kwargs)
+5 -1
View File
@@ -1056,7 +1056,11 @@ class BaseGalaxyToolBox(AbstractToolBox):
def __init__(self, config_filenames, tool_root_dir, app):
super(BaseGalaxyToolBox, self).__init__(config_filenames, tool_root_dir, app)
self._init_dependency_manager()
old_toolbox = getattr(app, 'toolbox', None)
if old_toolbox:
self.dependency_manager = old_toolbox.dependency_manager
else:
self._init_dependency_manager()
@property
def sa_session(self):
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+152
View File
@@ -0,0 +1,152 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"# Welcome to the interactive Galaxy IPython Notebook."
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"You can access your data via the dataset number. For example, ``handle = open(get(42), 'r')``.\n",
"To save data, write your data to a file, and then call ``put('filename.txt')``. The dataset will then be available in your galaxy history.\n",
"Notebooks can be saved to Galaxy by clicking the large green button at the top right of the IPython interface.<br>\n",
"More help and informations can be found on the project [website](https://github.com/bgruening/galaxy-ipython)."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"collapsed": false,
"deletable": true,
"editable": true
},
"outputs": [
{
"data": {
"text/plain": [
"'/import/1'"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"get(1)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"chr6_hla_hap2 0 150\r\n"
]
}
],
"source": [
"!grep chr6 /import/1"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"!echo \"Hello World\" > /tmp/cow"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"put(\"/tmp/cow\")"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1 [2] 1.txt.gg ipython_galaxy_notebook.ipynb\r\n"
]
}
],
"source": [
"!ls /import"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"put(\"/import/ipython_galaxy_notebook.ipynb\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.12"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
+155 -2
View File
@@ -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)
+67 -12
View File
@@ -28,73 +28,128 @@ 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 wait_for_xpath(self, xpath):
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)),
"XPATH selector [%s] to become present" % xpath,
**kwds
)
return element
def wait_for_xpath_visible(self, xpath):
def wait_for_xpath_visible(self, xpath, **kwds):
element = self._wait_on(
ec.visibility_of_element_located((By.XPATH, xpath)),
"XPATH selector [%s] to become visible" % xpath,
**kwds
)
return element
def wait_for_selector(self, selector):
def wait_for_selector(self, selector, **kwds):
element = self._wait_on(
ec.presence_of_element_located((By.CSS_SELECTOR, selector)),
"CSS selector [%s] to become present" % selector,
**kwds
)
return element
def wait_for_selector_visible(self, selector):
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)),
"CSS selector [%s] to become visible" % selector,
**kwds
)
return element
def wait_for_selector_clickable(self, selector):
def wait_for_selector_clickable(self, selector, **kwds):
element = self._wait_on(
ec.element_to_be_clickable((By.CSS_SELECTOR, selector)),
"CSS selector [%s] to become clickable" % selector,
**kwds
)
return element
def wait_for_selector_absent_or_hidden(self, selector):
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)),
"CSS selector [%s] to become absent or hidden" % selector,
**kwds
)
return element
def wait_for_selector_absent(self, selector):
def wait_for_selector_absent(self, selector, **kwds):
element = self._wait_on(
lambda driver: len(driver.find_elements_by_css_selector(selector)) == 0,
"CSS selector [%s] to become absent" % selector,
**kwds
)
return element
def wait_for_id(self, id):
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)),
"presence of DOM ID [%s]" % id,
**kwds
)
def _wait_on(self, condition, on_str=None):
def _wait_on(self, condition, on_str=None, **kwds):
if on_str is None:
on_str = str(condition)
message = "Timeout waiting on %s." % on_str
wait = self.wait()
wait = self.wait(**kwds)
return wait.until(condition, message)
def action_chains(self):
@@ -106,9 +161,9 @@ class HasDriver:
def send_escape(self, element):
element.send_keys(Keys.ESCAPE)
def wait(self, timeout=UNSPECIFIED_TIMEOUT):
def wait(self, timeout=UNSPECIFIED_TIMEOUT, **kwds):
if timeout is UNSPECIFIED_TIMEOUT:
timeout = self.default_timeout
timeout = self.timeout_for(**kwds)
return WebDriverWait(self.driver, timeout)
def click_xpath(self, xpath):
+280 -74
View File
@@ -4,6 +4,7 @@ Implementer must provide a self.build_url method to target Galaxy.
"""
from __future__ import print_function
import collections
import contextlib
import random
import string
@@ -13,8 +14,13 @@ from functools import partial, wraps
import requests
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,
@@ -28,6 +34,28 @@ DEFAULT_PASSWORD = '123456'
RETRY_DURING_TRANSITIONS_SLEEP_DEFAULT = .1
RETRY_DURING_TRANSITIONS_ATTEMPTS_DEFAULT = 10
WaitType = collections.namedtuple("WaitType", ["name", "default_length"])
# Default wait times should make sense for a development server under low
# load. Wait times for production servers can be scaled up with a multiplier.
WAIT_TYPES = Bunch(
# Rendering a form and registering callbacks, etc...
UX_RENDER=WaitType("ux_render", 1),
# Fade in, fade out, etc...
UX_TRANSITION=WaitType("ux_transition", 5),
# Toastr popup and dismissal, etc...
UX_POPUP=WaitType("ux_popup", 10),
# Creating a new history and loading it into the panel.
DATABASE_OPERATION=WaitType("database_operation", 10),
# Wait time for jobs to complete in default environment.
JOB_COMPLETION=WaitType("job_completion", 30),
# Wait time for a GIE to spawn.
GIE_SPAWN=WaitType("gie_spawn", 30),
)
# Choose a moderate wait type for operations that don't specify a type.
DEFAULT_WAIT_TYPE = WAIT_TYPES.DATABASE_OPERATION
class NullTourCallback(object):
@@ -35,7 +63,7 @@ class NullTourCallback(object):
pass
def excepion_seems_to_indicate_transition(e):
def exception_seems_to_indicate_transition(e):
"""True if exception seems to indicate the page state is transitioning.
Galaxy features many different transition effects that change the page state over time.
@@ -52,7 +80,7 @@ def excepion_seems_to_indicate_transition(e):
return exception_indicates_stale_element(e) or exception_indicates_not_clickable(e)
def retry_call_during_transitions(f, attempts=RETRY_DURING_TRANSITIONS_ATTEMPTS_DEFAULT, sleep=RETRY_DURING_TRANSITIONS_SLEEP_DEFAULT, exception_check=excepion_seems_to_indicate_transition):
def retry_call_during_transitions(f, attempts=RETRY_DURING_TRANSITIONS_ATTEMPTS_DEFAULT, sleep=RETRY_DURING_TRANSITIONS_SLEEP_DEFAULT, exception_check=exception_seems_to_indicate_transition):
previous_attempts = 0
while True:
try:
@@ -68,7 +96,7 @@ def retry_call_during_transitions(f, attempts=RETRY_DURING_TRANSITIONS_ATTEMPTS_
previous_attempts += 1
def retry_during_transitions(f, attempts=RETRY_DURING_TRANSITIONS_ATTEMPTS_DEFAULT, sleep=RETRY_DURING_TRANSITIONS_SLEEP_DEFAULT, exception_check=excepion_seems_to_indicate_transition):
def retry_during_transitions(f, attempts=RETRY_DURING_TRANSITIONS_ATTEMPTS_DEFAULT, sleep=RETRY_DURING_TRANSITIONS_SLEEP_DEFAULT, exception_check=exception_seems_to_indicate_transition):
@wraps(f)
def _retry(*args, **kwds):
@@ -78,8 +106,24 @@ def retry_during_transitions(f, attempts=RETRY_DURING_TRANSITIONS_ATTEMPTS_DEFAU
class NavigatesGalaxy(HasDriver):
"""Class with helpers methods for driving components of the Galaxy interface.
In most cases, methods for interacting with Galaxy components that appear in
multiple tests or applications should be refactored into this class for now.
Keep in mind that this class is used outside the context of ``TestCase``s as
well - so some methods more explicitly related to test data or assertion checking
may make more sense in SeleniumTestCase for instance.
Some day this class will likely be split up into smaller mixins for particular
components of Galaxy, but until that day the best practice is to prefix methods
for driving or querying the interface with the name of the component or page
the method operates on. These serve as psedu-namespaces until we decompose this
class. For instance, the method for clicking an option in the workflow editor is
workflow_editor_click_option instead of click_workflow_editor_option.
"""
default_password = DEFAULT_PASSWORD
wait_types = WAIT_TYPES
def get(self, url=""):
full_url = self.build_url(url)
@@ -89,13 +133,27 @@ 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
def sleep_for(self, wait_type):
time.sleep(self.wait_length(wait_type))
def timeout_for(self, **kwds):
wait_type = kwds.get("wait_type", DEFAULT_WAIT_TYPE)
return self.wait_length(wait_type)
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):
@@ -113,6 +171,14 @@ class NavigatesGalaxy(HasDriver):
else:
return response.json()
def api_delete(self, endpoint, raw=False):
full_url = self.build_url("api/" + endpoint, for_selenium=False)
response = requests.get(full_url, cookies=self.selenium_to_requests_cookies())
if raw:
return response
else:
return response.json()
def get_galaxy_session(self):
for cookie in self.driver.get_cookies():
if cookie["name"] == "galaxysession":
@@ -123,12 +189,12 @@ 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):
return self.history_panel_name_element().text
def current_history(self):
history = self.api_get("histories")[0]
@@ -147,7 +213,7 @@ class NavigatesGalaxy(HasDriver):
assert len(history_contents) > 0
return history_contents[-1]
def wait_for_history(self, timeout=30, assert_ok=True):
def wait_for_history(self, assert_ok=True):
def history_becomes_terminal(driver):
current_history_id = self.current_history_id()
state = self.api_get("histories/%s" % current_history_id)["state"]
@@ -156,41 +222,54 @@ class NavigatesGalaxy(HasDriver):
else:
return None
final_state = self.wait(timeout).until(history_becomes_terminal)
timeout = self.timeout_for(wait_type=WAIT_TYPES.JOB_COMPLETION)
final_state = self.wait(timeout=timeout).until(history_becomes_terminal)
if assert_ok:
assert final_state == "ok", final_state
return final_state
def history_panel_wait_for_hid_ok(self, hid, timeout=60, allowed_force_refreshes=0):
self.history_panel_wait_for_hid_state(hid, 'ok', timeout=timeout, allowed_force_refreshes=allowed_force_refreshes)
def history_panel_create_new_with_name(self, name):
self.history_panel_create_new()
self.history_panel_rename(name)
def history_panel_wait_for_hid_visible(self, hid, timeout=60, allowed_force_refreshes=0):
def history_panel_create_new(self):
"""Click create new and pause a bit for the history to begin to refresh."""
self.click_history_option('Create New')
self.sleep_for(WAIT_TYPES.UX_RENDER)
def history_panel_wait_for_hid_ok(self, hid, allowed_force_refreshes=0):
self.history_panel_wait_for_hid_state(hid, 'ok', allowed_force_refreshes=allowed_force_refreshes)
def history_panel_wait_for_hid_visible(self, hid, allowed_force_refreshes=0):
current_history_id = self.current_history_id()
def history_has_hid(driver):
contents = self.api_get("histories/%s/contents" % current_history_id)
return any([d for d in contents if d["hid"] == hid])
timeout = self.timeout_for(wait_type=WAIT_TYPES.JOB_COMPLETION)
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)
rval = self.wait_for_visible(history_item_selector, wait_type=WAIT_TYPES.JOB_COMPLETION)
break
except self.TimeoutException:
if attempt >= allowed_force_refreshes:
@@ -204,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")
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, timeout=60):
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)
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, timeout=60, allowed_force_refreshes=0):
history_item_selector = self.history_panel_wait_for_hid_visible(hid, timeout=timeout, allowed_force_refreshes=allowed_force_refreshes)
history_item_selector_state = "%s.state-%s" % (history_item_selector, state)
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 = 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:
@@ -274,10 +356,11 @@ 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"])
self.sleep_for(WAIT_TYPES.UX_RENDER)
form = self.wait_for_visible(self.navigation.login.selectors.form)
self.fill(form, login_info)
self.snapshot("logging-in")
self.click_submit(form)
@@ -306,17 +389,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
@@ -335,7 +417,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]
@@ -351,7 +433,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:
@@ -490,7 +572,7 @@ class NavigatesGalaxy(HasDriver):
menu_element = self.workflow_editor_options_menu_element()
option_elements = menu_element.find_elements_by_css_selector("a")
assert len(option_elements) > 0, "Failed to find workflow editor options"
time.sleep(1)
self.sleep_for(WAIT_TYPES.UX_RENDER)
found_option = False
for option_element in option_elements:
if option_label in option_element.text:
@@ -510,6 +592,90 @@ class NavigatesGalaxy(HasDriver):
def workflow_editor_options_menu_element(self):
return self.wait_for_selector_visible("#workflow-options-button-menu")
def libraries_open(self):
self.home()
self.click_masthead_shared_data()
self.click_label(self.navigation_data["labels"]["masthead"]["sharedMenu"]["libraries"])
self.wait_for_selector('.library_style_container')
@retry_during_transitions
def libraries_index_table_elements(self):
container = self.wait_for_selector_visible(".library_container")
elements = container.find_elements_by_css_selector("#library_list_body")
if not elements:
return []
else:
assert len(elements) == 1
element = elements[0]
return element.find_elements_by_css_selector("tr") # [style='display: table-row']
def libraries_index_click_create_new(self):
self.wait_for_and_click_selector("#create_new_library_btn")
def libraries_index_create(self, name):
self.libraries_index_click_create_new()
name_text_box = self.wait_for_selector_clickable("input[name='Name']")
name_text_box.send_keys(name)
self.wait_for_and_click_selector("#button-0")
def libraries_index_click_search(self):
self.sleep_for(WAIT_TYPES.UX_RENDER)
search_element = self.wait_for_selector_clickable("input.library-search-input")
search_element.click()
return search_element
def libraries_index_sort_selector(self):
return ".sort-libraries-link"
def libraries_index_sort_click(self):
sort_element = self.wait_for_selector_clickable(self.libraries_index_sort_selector())
sort_element.click()
return sort_element
def libraries_index_search_for(self, text):
self.wait_for_overlays_cleared()
search_box = self.libraries_index_click_search()
search_box.clear()
search_box.send_keys(text)
value = search_box.get_attribute("value")
assert value == text, value
self.driver.execute_script("$(arguments[0]).keyup();", search_box)
def libraries_folder_create(self, name):
create_folder_button = self.wait_for_selector_clickable("#toolbtn_create_folder")
create_folder_button.click()
name_text_box = self.wait_for_selector_clickable("input[name='Name']")
name_text_box.send_keys(name)
create_button = self.wait_for_selector_clickable("#button-0")
create_button.click()
def libraries_click_dataset_import(self):
self.wait_for_and_click(self.navigation.libraries.folder.selectors.add_items_button)
def libraries_dataset_import_from_history(self):
self.libraries_click_dataset_import()
self.wait_for_visible(self.navigation.libraries.folder.selectors.add_items_menu)
self.wait_for_and_click(self.navigation.libraries.folder.labels.from_history)
def libraries_dataset_import_from_path(self):
self.libraries_click_dataset_import()
self.wait_for_visible(self.navigation.libraries.folder.selectors.add_items_menu)
self.wait_for_and_click(self.navigation.libraries.folder.labels.from_path)
def libraries_table_elements(self):
tbody_element = self.wait_for_selector_visible("#folder_list_body")
return tbody_element.find_elements_by_css_selector("tr")[1:]
def wait_for_overlays_cleared(self):
"""Wait for modals and Toast notifications to disappear."""
self.wait_for_selector_absent_or_hidden(".ui-modal")
self.wait_for_selector_absent_or_hidden(".toast")
def workflow_index_open(self):
self.home()
self.click_masthead_workflow()
@@ -546,6 +712,13 @@ class NavigatesGalaxy(HasDriver):
alert.send_keys(new_name)
alert.accept()
@retry_during_transitions
def workflow_index_name(self, workflow_index=0):
"""Get workflow name for workflow_index'th row."""
row_element = self.workflow_index_table_row(workflow_index=workflow_index)
workflow_button = row_element.find_element_by_css_selector(".menubutton")
return workflow_button.text
def workflow_index_click_option(self, option_title, workflow_index=0):
@retry_during_transitions
@@ -628,13 +801,19 @@ 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_masthead_shared_data(self):
self.click_xpath(self.navigation_data["selectors"]["masthead"]["shared_data"])
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(
@@ -660,16 +839,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'
@@ -696,36 +870,55 @@ 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")
self.wait_for_and_click_selector(viz_button_selector)
self.wait_for_selector_visible("%s %s" % (viz_button_selector, ".dropdown-menu"))
def history_panel_item_available_visualizations_elements(self, hid):
# Precondition: viz menu has been opened with history_panel_item_click_visualization_menu
viz_menu_selectors = "%s %s" % (self.history_panel_item_selector(hid), "a.visualization-link")
return self.driver.find_elements_by_css_selector(viz_menu_selectors)
def history_panel_item_available_visualizations(self, hid):
# Precondition: viz menu has been opened with history_panel_item_click_visualization_menu
return [e.text for e in self.history_panel_item_available_visualizations_elements(hid)]
def history_panel_item_click_visualization(self, hid, visualization_name):
# Precondition: viz menu has been opened with history_panel_item_click_visualization_menu
elements = self.history_panel_item_available_visualizations_elements(hid)
for element in elements:
if element.text == visualization_name:
element.click()
return element
assert False, "No visualization [%s] found." % visualization_name
def history_panel_item_selector(self, hid, wait=False):
current_history_id = self.current_history_id()
@@ -780,9 +973,14 @@ class NavigatesGalaxy(HasDriver):
else:
self.wait_for_selector_visible(details_selector)
def click_hda_title(self, hda_id, wait=False):
# TODO: Replace with calls to history_panel_click_item_title.
return self.history_panel_click_item_title(hda_id=hda_id, wait=wait)
def history_panel_ensure_showing_item_details(self, hid):
if not self.history_panel_item_showing_details(hid):
self.history_panel_click_item_title(hid=hid, wait=True)
def history_panel_item_showing_details(self, hid):
item_selector = self.history_panel_item_selector(hid)
details_selector = "%s .details" % item_selector
return self.selector_is_displayed(details_selector)
def collection_builder_set_name(self, name):
name_element = self.wait_for_selector_visible("input.collection-name")
@@ -807,7 +1005,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()
@@ -876,6 +1074,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)
@@ -928,6 +1128,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
+6 -2
View File
@@ -1,3 +1,4 @@
# Deprecated navigation constants - prefer navigation.yml.
selectors:
tooltipBalloon: '.tooltip'
editableText: '.editable-text'
@@ -15,12 +16,12 @@ selectors:
main: 'galaxy_main'
masthead:
id: '#masthead'
adminLink: '#masthead a[href="/admin/index"]'
userMenu:
userEmail_xpath: '//a[contains(text(), "Logged in as")]'
user: '//ul[@id="user"]'
workflow: '//ul[@id="workflow"]'
shared_data: '//ul[@id="shared"]'
toolMenu:
container: '.toolMenuContainer'
@@ -55,7 +56,7 @@ labels:
menus:
analyze: 'Analyze Data'
workflow: 'Workflow'
libraries: 'Shared Data'
shared_data: 'Shared Data'
visualization: 'Visualization'
help: 'Help'
user: 'User'
@@ -64,6 +65,9 @@ labels:
register: 'Register'
login: 'Login'
logout: 'Logout'
sharedMenu:
libraries: 'Data Libraries'
tools:
upload:
+159
View File
@@ -0,0 +1,159 @@
# 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'
libraries:
folder:
selectors:
# There needs to a different class or ID on the dataset button, this isn't
# a good selector for that button since .add-library-items also applies to
# folder (but it is a button.add-library-items instead).
add_items_button: 'div.add-library-items'
add_items_menu: 'div.add-library-items .dropdown-menu'
add_items_options: 'div.add-library-items .dropdown-menu li a'
# TODO: Most of these are very good selectors but the same DOM elements
# are reused without adding specific classes, IDs, or roles to anything.
import_modal: '.modal'
import_datasets_ok_button: '.modal-footer .buttons #button-0'
import_datasets_cancel_button: '.modal-footer .buttons #button-1'
import_progress_bar: '.progress-bar-import'
import_history_content: '#selected_history_content'
import_history_contents_items: '#selected_history_content ul li'
import_from_path_textarea: '#import_paths'
labels:
from_history: 'from History'
from_path: 'from Path'
dataset:
selectors:
table: '.dataset_table'
table_rows: '.dataset_table table tbody tr'
published_grids:
selectors:
search: '#input-free-text-search-filter'
+124 -44
View File
@@ -5,7 +5,6 @@ from __future__ import print_function
import datetime
import json
import os
import time
import traceback
import unittest
from functools import partial, wraps
@@ -18,6 +17,7 @@ except ImportError:
from six.moves.urllib.parse import urljoin
from base import populators # noqa: I100
from base.api import UsesApiTestCaseMixin # noqa: I100
from base.driver_util import classproperty, DEFAULT_WEB_HOST, get_ip_address # noqa: I100
from base.testcase import FunctionalTestCase # noqa: I100
from base.workflows_format_2 import ( # noqa: I100
@@ -30,14 +30,17 @@ from galaxy_selenium import ( # noqa: I100
from galaxy_selenium.navigates_galaxy import NavigatesGalaxy, retry_during_transitions # noqa: I100
from galaxy.util import asbool
DEFAULT_WAIT_TIMEOUT = 60
DEFAULT_TIMEOUT_MULTIPLIER = 1
DEFAULT_TEST_ERRORS_DIRECTORY = os.path.abspath("database/test_errors")
DEFAULT_SELENIUM_BROWSER = "auto"
DEFAULT_SELENIUM_REMOTE = False
DEFAULT_SELENIUM_REMOTE_PORT = "4444"
DEFAULT_SELENIUM_REMOTE_HOST = "127.0.0.1"
DEFAULT_SELENIUM_HEADLESS = "auto"
DEFAULT_ADMIN_USER = "test@bx.psu.edu"
DEFAULT_ADMIN_PASSWORD = "testpass"
TIMEOUT_MULTIPLIER = float(os.environ.get("GALAXY_TEST_TIMEOUT_MULTIPLIER", DEFAULT_TIMEOUT_MULTIPLIER))
GALAXY_TEST_ERRORS_DIRECTORY = os.environ.get("GALAXY_TEST_ERRORS_DIRECTORY", DEFAULT_TEST_ERRORS_DIRECTORY)
# Test browser can be ["CHROME", "FIREFOX", "OPERA", "PHANTOMJS"]
GALAXY_TEST_SELENIUM_BROWSER = os.environ.get("GALAXY_TEST_SELENIUM_BROWSER", DEFAULT_SELENIUM_BROWSER)
@@ -49,9 +52,10 @@ GALAXY_TEST_EXTERNAL_FROM_SELENIUM = os.environ.get("GALAXY_TEST_EXTERNAL_FROM_S
# Auto-retry selenium tests this many times.
GALAXY_TEST_SELENIUM_RETRIES = int(os.environ.get("GALAXY_TEST_SELENIUM_RETRIES", "0"))
# Test case data
DEFAULT_PASSWORD = '123456'
GALAXY_TEST_SELENIUM_USER_EMAIL = os.environ.get("GALAXY_TEST_SELENIUM_USER_EMAIL", None)
GALAXY_TEST_SELENIUM_USER_PASSWORD = os.environ.get("GALAXY_TEST_SELENIUM_USER_PASSWORD", None)
GALAXY_TEST_SELENIUM_ADMIN_USER_EMAIL = os.environ.get("GALAXY_TEST_SELENIUM_ADMIN_USER_EMAIL", DEFAULT_ADMIN_USER)
GALAXY_TEST_SELENIUM_ADMIN_USER_PASSWORD = os.environ.get("GALAXY_TEST_SELENIUM_ADMIN_USER_PASSWORD", DEFAULT_ADMIN_PASSWORD)
try:
from nose.tools import nottest
@@ -60,6 +64,62 @@ except ImportError:
return x
def managed_history(f):
"""Ensure a Selenium test has a distinct, named history.
Cleanup the history after the job is complete as well unless
GALAXY_TEST_NO_CLEANUP is set in the environment.
"""
@wraps(f)
def func_wrapper(self, *args, **kwds):
self.home()
history_name = f.__name__ + datetime.datetime.now().strftime("%Y%m%d%H%M%s")
self.history_panel_create_new_with_name(history_name)
try:
f(self, *args, **kwds)
finally:
if "GALAXY_TEST_NO_CLEANUP" not in os.environ:
current_history_id = self.current_history_id()
self.api_delete("histories/%s" % current_history_id)
return func_wrapper
def dump_test_information(self, name_prefix):
if GALAXY_TEST_ERRORS_DIRECTORY and GALAXY_TEST_ERRORS_DIRECTORY != "0":
if not os.path.exists(GALAXY_TEST_ERRORS_DIRECTORY):
os.makedirs(GALAXY_TEST_ERRORS_DIRECTORY)
result_name = name_prefix + datetime.datetime.now().strftime("%Y%m%d%H%M%s")
target_directory = os.path.join(GALAXY_TEST_ERRORS_DIRECTORY, result_name)
def write_file(name, content, raw=False):
with open(os.path.join(target_directory, name), "wb") as buf:
buf.write(content.encode("utf-8") if not raw else content)
os.makedirs(target_directory)
self.driver.save_screenshot(os.path.join(target_directory, "last.png"))
write_file("page_source.txt", self.driver.page_source)
write_file("DOM.txt", self.driver.execute_script("return document.documentElement.outerHTML"))
write_file("stacktrace.txt", traceback.format_exc())
for snapshot in getattr(self, "snapshots", []):
snapshot.write_to_error_directory(write_file)
for log_type in ["browser", "driver"]:
try:
write_file("%s.log.json" % log_type, json.dumps(self.driver.get_log(log_type)))
except Exception:
continue
iframes = self.driver.find_elements_by_css_selector("iframe")
for iframe in iframes:
pass
# TODO: Dump content out for debugging in the future.
# iframe_id = iframe.get_attribute("id")
# if iframe_id:
# write_file("iframe_%s" % iframe_id, "My content")
@nottest
def selenium_test(f):
@@ -72,38 +132,7 @@ def selenium_test(f):
try:
return f(self, *args, **kwds)
except Exception:
if GALAXY_TEST_ERRORS_DIRECTORY and GALAXY_TEST_ERRORS_DIRECTORY != "0":
if not os.path.exists(GALAXY_TEST_ERRORS_DIRECTORY):
os.makedirs(GALAXY_TEST_ERRORS_DIRECTORY)
result_name = f.__name__ + datetime.datetime.now().strftime("%Y%m%d%H%M%s")
target_directory = os.path.join(GALAXY_TEST_ERRORS_DIRECTORY, result_name)
def write_file(name, content, raw=False):
with open(os.path.join(target_directory, name), "wb") as buf:
buf.write(content.encode("utf-8") if not raw else content)
os.makedirs(target_directory)
self.driver.save_screenshot(os.path.join(target_directory, "last.png"))
write_file("page_source.txt", self.driver.page_source)
write_file("DOM.txt", self.driver.execute_script("return document.documentElement.outerHTML"))
write_file("stacktrace.txt", traceback.format_exc())
for snapshot in getattr(self, "snapshots", []):
snapshot.write_to_error_directory(write_file)
for log_type in ["browser", "driver"]:
try:
write_file("%s.log.json" % log_type, json.dumps(self.driver.get_log(log_type)))
except Exception:
continue
iframes = self.driver.find_elements_by_css_selector("iframe")
for iframe in iframes:
pass
# TODO: Dump content out for debugging in the future.
# iframe_id = iframe.get_attribute("id")
# if iframe_id:
# write_file("iframe_%s" % iframe_id, "My content")
dump_test_information(self, f.__name__)
if retry_attempts < GALAXY_TEST_SELENIUM_RETRIES:
retry_attempts += 1
else:
@@ -131,10 +160,17 @@ class TestSnapshot(object):
write_file_func("%s-stack.txt" % prefix, str(self.stack))
class SeleniumTestCase(FunctionalTestCase, NavigatesGalaxy):
class SeleniumTestCase(FunctionalTestCase, NavigatesGalaxy, UsesApiTestCaseMixin):
# If run one-off via nosetests, the next line ensures test
# tools and datatypes are used instead of configured tools.
framework_tool_and_types = True
# Override this in subclasses to ensure a user is logged in
# before each test. If GALAXY_TEST_SELENIUM_USER_EMAIL and
# GALAXY_TEST_SELENIUM_USER_PASSWORD are set these values
# will be used to login.
ensure_registered = False
requires_admin = False
def setUp(self):
super(SeleniumTestCase, self).setUp()
@@ -146,6 +182,21 @@ class SeleniumTestCase(FunctionalTestCase, NavigatesGalaxy):
self.target_url_from_selenium = self.url
self.snapshots = []
self.setup_driver_and_session()
if self.requires_admin and GALAXY_TEST_SELENIUM_ADMIN_USER_EMAIL == DEFAULT_ADMIN_USER:
self._setup_interactor()
self._setup_user(GALAXY_TEST_SELENIUM_ADMIN_USER_EMAIL)
try:
self.setup_with_driver()
except Exception:
dump_test_information(self, self.__class__.__name__ + "_setup")
raise
def setup_with_driver(self):
"""Override point that allows setting up data using self.driver and Selenium connection.
Using this instead of overriding will ensure debug data such as screenshots and stack traces
are dumped if there are problems with the setup.
"""
def tearDown(self):
exception = None
@@ -177,6 +228,18 @@ class SeleniumTestCase(FunctionalTestCase, NavigatesGalaxy):
self.driver.set_window_size(1280, 900)
if self.ensure_registered:
self.login()
def login(self):
if GALAXY_TEST_SELENIUM_USER_EMAIL:
assert GALAXY_TEST_SELENIUM_USER_PASSWORD, "If GALAXY_TEST_SELENIUM_USER_EMAIL is set, a password must be set also with GALAXY_TEST_SELENIUM_USER_PASSWORD"
self.home()
self.submit_login(
email=GALAXY_TEST_SELENIUM_USER_EMAIL,
password=GALAXY_TEST_SELENIUM_USER_PASSWORD,
assert_valid=True,
)
else:
self.register()
def tear_down_driver(self):
@@ -199,8 +262,8 @@ class SeleniumTestCase(FunctionalTestCase, NavigatesGalaxy):
return default_web_host_for_selenium_tests()
@property
def default_timeout(self):
return DEFAULT_WAIT_TIMEOUT
def timeout_multiplier(self):
return TIMEOUT_MULTIPLIER
def build_url(self, url, for_selenium=True):
if for_selenium:
@@ -235,10 +298,28 @@ class SeleniumTestCase(FunctionalTestCase, NavigatesGalaxy):
assert empty_msg_element.is_displayed()
assert empty_msg_str in empty_msg_element.text
def admin_login(self):
self.home()
self.submit_login(
GALAXY_TEST_SELENIUM_ADMIN_USER_EMAIL,
GALAXY_TEST_SELENIUM_ADMIN_USER_PASSWORD
)
with self.main_panel():
self.assert_no_error_message()
@property
def workflow_populator(self):
return SeleniumSessionWorkflowPopulator(self)
def ensure_visualization_available(self, hid, visualization_name):
"""Skip or fail a test if visualization for file doesn't appear.
Precondition: viz menu has been opened with history_panel_item_click_visualization_menu.
"""
visualization_names = self.history_panel_item_available_visualizations(hid)
if visualization_name not in visualization_names:
raise unittest.SkipTest("Skipping test, visualization [%s] doesn't appear to be configured." % visualization_name)
class SharedStateSeleniumTestCase(SeleniumTestCase):
"""This describes a class Selenium tests that setup class state for all tests.
@@ -253,8 +334,7 @@ class SharedStateSeleniumTestCase(SeleniumTestCase):
shared_state_initialized = False
shared_state_in_error = False
def setUp(self):
super(SharedStateSeleniumTestCase, self).setUp()
def setup_with_driver(self):
if not self.__class__.shared_state_initialized:
try:
self.setup_shared_state()
@@ -315,7 +395,7 @@ class UsesHistoryItemAssertions:
def _assert_item_button(self, buttons_area, expected_button, button_def):
selector = button_def["selector"]
# Let old tooltip expire, etc...
time.sleep(1)
self.sleep_for(self.wait_types.UX_TRANSITION)
button_item = self.wait_for_selector_visible("%s %s" % (buttons_area, selector))
expected_tooltip = button_def.get("tooltip")
self.assert_tooltip_text(button_item, expected_tooltip)
+11 -12
View File
@@ -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()
+1 -5
View File
@@ -1,5 +1,3 @@
import time
from .framework import (
retry_assertion_during_transitions,
selenium_test,
@@ -81,7 +79,7 @@ class CustomBuildsTestcase(SharedStateSeleniumTestCase):
delete_button.click()
def get_custom_builds(self):
time.sleep(1)
self.sleep_for(self.wait_types.UX_RENDER)
builds = []
grid = self.wait_for_selector('table.grid > tbody')
for row in grid.find_elements_by_tag_name('tr'):
@@ -92,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):
+8 -7
View File
@@ -13,20 +13,21 @@ 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_id = self.latest_history_item()["id"]
self.click_hda_title(hda_id, wait=True)
hda = self.latest_history_item()
hid = hda["hid"]
self.history_panel_click_item_title(hid=hid, wait=True)
hda_body_selector = self.hda_body_selector(hda_id)
hda_body_selector = self.hda_body_selector(hda["id"])
self.wait_for_selector_visible(hda_body_selector)
self.click_hda_title(hda_id, wait=True)
self.history_panel_click_item_title(hid=hid, wait=True)
self.assert_selector_absent_or_hidden_after_transitions(hda_body_selector)
+41 -44
View File
@@ -1,5 +1,3 @@
import time
from .framework import (
selenium_test,
SeleniumTestCase
@@ -13,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):
@@ -28,8 +27,7 @@ class HistoryPanelTestCase(SeleniumTestCase):
editable_text_input_element.send_keys("New History Name")
self.send_enter(editable_text_input_element)
name_element = self.history_panel_name_element()
assert "New History Name" in name_element.text
assert "New History Name" in self.history_panel_name()
@selenium_test
def test_history_rename_cancel_with_click(self):
@@ -37,9 +35,8 @@ 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())
name_element = self.history_panel_name_element()
assert "New History Name" not in name_element.text
self.assert_absent(self.navigation.history_panel.selectors.name_edit_input)
assert "New History Name" not in self.history_panel_name()
@selenium_test
def test_history_rename_cancel_with_escape(self):
@@ -47,43 +44,43 @@ 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())
name_element = self.history_panel_name_element()
assert "New History Name" not in name_element.text
self.assert_absent(self.navigation.history_panel.selectors.name_edit_input)
assert "New History Name" not in self.history_panel_name()
@selenium_test
def test_history_tags_and_annotations_buttons(self):
# 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()
time.sleep(.5)
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()
time.sleep(.5)
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):
@@ -91,20 +88,20 @@ class HistoryPanelTestCase(SeleniumTestCase):
self.perform_upload(self.get_filename("1.txt"))
self.wait_for_history()
hda_id = self.latest_history_item()["id"]
hda_body_selector = self.hda_body_selector(hda_id)
self.assert_selector_absent_or_hidden(hda_body_selector)
self.click_hda_title(hda_id, wait=True)
self.wait_for_selector_visible(hda_body_selector)
# Open the details, verify they are open and do a refresh.
self.history_panel_ensure_showing_item_details(hid=1)
self.history_panel_item_body_selector(1, wait=True)
self.history_panel_refresh_click()
title_selector = self.hda_div_selector(hda_id)
self.wait_for_selector_visible(hda_body_selector)
# After the refresh, verify the details are still open.
self.sleep_for(self.wait_types.UX_TRANSITION)
self.wait_for_selector_clickable(self.history_panel_item_selector(hid=1))
assert self.history_panel_item_showing_details(hid=1)
self.click_hda_title(hda_id, wait=True)
# Close the detailed display, refresh, and ensure they are still closed.
self.history_panel_click_item_title(hid=1, wait=True)
assert not self.history_panel_item_showing_details(hid=1)
self.history_panel_refresh_click()
self.wait_for_selector(title_selector)
self.assert_selector_absent_or_hidden(hda_body_selector)
self.sleep_for(self.wait_types.UX_TRANSITION)
self.wait_for_selector_clickable(self.history_panel_item_selector(hid=1))
assert not self.history_panel_item_showing_details(hid=1)
+46
View File
@@ -0,0 +1,46 @@
from .framework import (
managed_history,
selenium_test,
SeleniumTestCase,
)
JUPYTER_VISUALIZATION_NAME = "Jupyter"
class JupyterTestCase(SeleniumTestCase):
ensure_registered = True
@selenium_test
@managed_history
def test_jupyter_session(self):
self.perform_upload(self.get_filename("1.txt"))
self.perform_upload(self.get_filename("selenium-test-notebook.ipynb"))
self.wait_for_history()
self.history_panel_ensure_showing_item_details(2)
self.history_panel_item_click_visualization_menu(2)
self.ensure_visualization_available(2, JUPYTER_VISUALIZATION_NAME)
self.history_panel_item_click_visualization(2, JUPYTER_VISUALIZATION_NAME)
with self.main_panel():
viz_iframe = self.wait_for_selector("body iframe", wait_type=self.wait_types.GIE_SPAWN)
try:
self.driver.switch_to.frame(viz_iframe)
self.wait_for_selector("ul.nav")
lis = self.driver.find_elements_by_css_selector("ul.nav li.dropdown")
cell_li = None
for li in lis:
if li.text == "Cell":
cell_li = li
break
cell_li.click()
self.wait_for_and_click_selector("ul#cell_menu li#run_all_cells")
finally:
self.driver.switch_to.default_content()
# TODO: wait on something in the notebook instead of sleeping so long.
self.sleep_for(wait_type=self.wait_types.JOB_COMPLETION)
self.history_panel_refresh_click()
# Re-running notebook should publish two new things to the history.
self.history_panel_wait_for_hid_ok(3)
self.history_panel_wait_for_hid_ok(4)
@@ -0,0 +1,103 @@
from .framework import (
retry_assertion_during_transitions,
selenium_test,
SeleniumTestCase,
)
class LibraryContentsTestCase(SeleniumTestCase):
requires_admin = True
@selenium_test
def test_create_folder(self):
self._navigate_to_new_library()
self._assert_num_displayed_items_is(0)
self.libraries_folder_create("folder1")
self._assert_num_displayed_items_is(1)
@selenium_test
def test_import_dataset_from_history(self):
self.admin_login()
self.perform_upload(self.get_filename("1.txt"))
self.wait_for_history()
self._navigate_to_new_library(login=False)
self._assert_num_displayed_items_is(0)
self.sleep_for(self.wait_types.UX_RENDER)
self.libraries_dataset_import_from_history()
# Click the cancel button, make sure modal is hidden.
self.wait_for_visible(self.navigation.libraries.folder.selectors.import_modal)
self.wait_for_and_click(self.navigation.libraries.folder.selectors.import_datasets_cancel_button)
self.wait_for_absent_or_hidden(self.navigation.libraries.folder.selectors.import_modal)
self.libraries_dataset_import_from_history()
self.wait_for_visible(self.navigation.libraries.folder.selectors.import_history_content)
history_elements = self.find_elements(self.navigation.libraries.folder.selectors.import_history_contents_items)
assert "1.txt" in history_elements[0].text
history_elements[0].find_element_by_css_selector("input").click()
# Add
self.sleep_for(self.wait_types.UX_RENDER)
self.wait_for_and_click(self.navigation.libraries.folder.selectors.import_datasets_ok_button)
# Let the progress bar disappear...
self.wait_for_absent_or_hidden(self.navigation.libraries.folder.selectors.import_progress_bar)
self._assert_num_displayed_items_is(1)
@selenium_test
def test_import_dataset_from_path(self):
self._navigate_to_new_library()
self._assert_num_displayed_items_is(0)
self.sleep_for(self.wait_types.UX_RENDER)
# Click the cancel button, make sure modal is hidden.
self.libraries_dataset_import_from_path()
self.wait_for_visible(self.navigation.libraries.folder.selectors.import_modal)
self.wait_for_and_click(self.navigation.libraries.folder.selectors.import_datasets_cancel_button)
self.wait_for_absent_or_hidden(self.navigation.libraries.folder.selectors.import_modal)
# Try again... this time actually select some paths.
self.libraries_dataset_import_from_path()
textarea = self.wait_for_and_click(self.navigation.libraries.folder.selectors.import_from_path_textarea)
textarea.send_keys("test-data/1.txt")
self.sleep_for(self.wait_types.UX_RENDER)
self.wait_for_and_click(self.navigation.libraries.folder.selectors.import_datasets_ok_button)
# Let the progress bar disappear...
self.wait_for_absent_or_hidden(self.navigation.libraries.folder.selectors.import_progress_bar)
self._assert_num_displayed_items_is(1)
self.click_label("1.txt")
self.wait_for_visible(self.navigation.libraries.dataset.selectors.table)
elements = self.find_elements(self.navigation.libraries.dataset.selectors.table_rows)
table_as_dict = {}
for element in elements:
key = element.find_element_by_tag_name("th").text
value = element.find_element_by_tag_name("td").text
table_as_dict[key] = value
assert table_as_dict["Name"] == "1.txt", table_as_dict
assert table_as_dict["Genome build"] == "?", table_as_dict
@selenium_test
def test_show_details(self):
self._navigate_to_new_library()
self.sleep_for(self.wait_types.UX_RENDER)
self.wait_for_selector_clickable(".toolbtn-show-locinfo").click()
self.sleep_for(self.wait_types.UX_RENDER)
self.wait_for_selector_clickable(".ui-modal #button-0").click()
self.wait_for_overlays_cleared()
@retry_assertion_during_transitions
def _assert_num_displayed_items_is(self, n):
self.assertEqual(n, self._num_displayed_items())
def _num_displayed_items(self):
return len(self.libraries_table_elements())
def _navigate_to_new_library(self, login=True):
if login:
self.admin_login()
self.libraries_open()
self.name = self._get_random_name(prefix="testcontents")
self.libraries_index_create(self.name)
self.wait_for_overlays_cleared()
self.libraries_index_search_for(self.name)
self.libraries_index_table_elements()[0].find_element_by_css_selector("td a").click()
+105
View File
@@ -0,0 +1,105 @@
from .framework import (
retry_assertion_during_transitions,
selenium_test,
SeleniumTestCase,
)
class LibraryLandingTestCase(SeleniumTestCase):
requires_admin = True
def setup_with_driver(self):
self.admin_login()
self.libraries_open()
@selenium_test
def test_create_new_close(self):
num_displayed_libraries = self._num_displayed_libraries()
self.libraries_index_click_create_new()
self.wait_for_selector_visible(".ui-modal")
close_button = self.wait_for_selector_clickable("#button-1")
close_button.click()
self.wait_for_overlays_cleared()
assert self._num_displayed_libraries() == num_displayed_libraries
@selenium_test
def test_create_new(self):
name = self._get_random_name(prefix="testcreatenew")
self.libraries_index_create(name)
# Must be showing at least one library now.
self._assert_at_least_one_library_displayed()
self._search_for_only_with_name(name)
@selenium_test
def test_rename(self):
name = self._get_random_name(prefix="testprerename")
self.libraries_index_create(name)
self._search_for_only_with_name(name)
edit_button = self.wait_for_selector_clickable(".edit_library_btn")
edit_button.click()
new_name = self._get_random_name(prefix="testpostrename")
name_box = self.wait_for_selector_clickable(".input_library_name")
name_box.send_keys(new_name)
save_button = self.wait_for_selector_clickable(".save_library_btn")
save_button.click()
self._search_for_only_with_name(new_name)
@selenium_test
def test_help(self):
help_button = self.wait_for_selector_clickable(".help-button")
help_link = help_button.find_element_by_css_selector("a")
self.assertEqual(help_link.get_attribute("href"), "https://galaxyproject.org/data-libraries/screen/list-of-libraries/")
@selenium_test
def test_sorting(self):
# Throw in another library to ensure filtering is working...
other = self._get_random_name(prefix="notthesame")
self.libraries_index_create(other)
self.wait_for_overlays_cleared()
namebase = self._get_random_name(prefix="testsort")
self.libraries_index_create(namebase + " b")
self.wait_for_overlays_cleared()
self.libraries_index_create(namebase + " a")
self.wait_for_overlays_cleared()
self.libraries_index_create(namebase + " c")
self.libraries_index_search_for(namebase)
self._assert_num_displayed_libraries_is(3)
self._assert_names_are([namebase + " a", namebase + " b", namebase + " c"])
self.libraries_index_sort_click()
# TODO: Usability bug in libraries - should need to resort - it should
# sort the entities on the screen.
self.libraries_index_search_for(namebase)
self._assert_names_are([namebase + " c", namebase + " b", namebase + " a"])
def _search_for_only_with_name(self, name):
self.libraries_index_search_for(name)
self._assert_num_displayed_libraries_is(1)
@retry_assertion_during_transitions
def _assert_names_are(self, expected_names):
names = [e.find_element_by_css_selector("td a").text for e in self.libraries_index_table_elements()]
self.assertEqual(names, expected_names)
@retry_assertion_during_transitions
def _assert_at_least_one_library_displayed(self):
assert self._num_displayed_libraries() > 0
@retry_assertion_during_transitions
def _assert_num_displayed_libraries_is(self, n):
self.assertEqual(n, self._num_displayed_libraries())
def _num_displayed_libraries(self):
return len(self.libraries_index_table_elements())
@@ -0,0 +1,32 @@
from galaxy_selenium.navigates_galaxy import (
exception_indicates_not_clickable,
exception_seems_to_indicate_transition,
)
from .framework import (
selenium_test,
SeleniumTestCase,
)
class NavigatesGalaxySeleniumTestCase(SeleniumTestCase):
"""Test the Selenium test framework itself.
Unlike the others test cases in this module, this test case tests the
test framework itself (for when that makes sense, mostly corner cases).
"""
@selenium_test
def test_click_error(self):
self.home()
self.upload_start_click()
# Open the details, verify they are open and do a refresh.
exception = None
try:
refresh_button = self.wait_for_selector("#history-refresh-button")
refresh_button.click()
except Exception as e:
exception = e
assert exception is not None
assert exception_indicates_not_clickable(exception)
assert exception_seems_to_indicate_transition(exception)
@@ -1,5 +1,3 @@
import time
from .framework import (
retry_assertion_during_transitions,
selenium_test,
@@ -98,7 +96,7 @@ class HistoryGridTestCase(SharedStateSeleniumTestCase):
self.assert_grid_histories_are([self.history1_name, self.history3_name], False)
def get_histories(self, sleep=False):
time.sleep(1.5)
self.sleep_for(self.wait_types.UX_RENDER)
names = []
grid = self.wait_for_selector('#grid-table-body')
for row in grid.find_elements_by_tag_name('tr'):
@@ -146,27 +144,26 @@ class HistoryGridTestCase(SharedStateSeleniumTestCase):
close_link_selector = 'a[filter_key="%s"][filter_val="%s"]' % \
(filter_key, filter_value)
self.wait_for_and_click_selector(close_link_selector)
time.sleep(.5)
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)
@@ -202,8 +199,7 @@ class HistoryGridTestCase(SharedStateSeleniumTestCase):
def create_history(self, name):
self.home()
self.click_history_option('Create New')
self.history_panel_rename(name)
self.history_panel_create_new_with_name(name)
def publish_current_history(self):
self.click_history_option('Share or Publish')
@@ -213,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)
+3 -4
View File
@@ -1,5 +1,4 @@
from .framework import (
DEFAULT_PASSWORD,
selenium_test,
SeleniumTestCase
)
@@ -33,7 +32,7 @@ class RegistrationTestCase(SeleniumTestCase):
def test_reregister_email_fails(self):
self.home()
email = self._get_random_email()
password = DEFAULT_PASSWORD
password = self.default_password
confirm = password
username = email.split("@")[0]
self.register(email, password, username, confirm)
@@ -47,7 +46,7 @@ class RegistrationTestCase(SeleniumTestCase):
self.home()
email1 = self._get_random_email()
email2 = self._get_random_email()
password = DEFAULT_PASSWORD
password = self.default_password
confirm = password
username = email1.split("@")[0]
self.register(email1, password, username, confirm)
@@ -60,7 +59,7 @@ class RegistrationTestCase(SeleniumTestCase):
def test_bad_emails(self):
bad_emails = ['bob', 'bob@', 'bob@idontwanttocleanup', 'bob.cantmakeme']
good_email = self._get_random_email()
password = DEFAULT_PASSWORD
password = self.default_password
confirm = password
username = good_email.split("@")[0]
+8 -14
View File
@@ -1,5 +1,3 @@
import time
from .framework import (
retry_assertion_during_transitions,
retry_during_transitions,
@@ -21,12 +19,11 @@ class SavedHistoriesTestCase(SharedStateSeleniumTestCase):
self._login()
self.navigate_to_saved_histories_page()
self.click_popup_option(self.history2_name, 'Switch')
time.sleep(.5)
self.sleep_for(self.wait_types.UX_RENDER)
@retry_assertion_during_transitions
def assert_history_name_switched():
history_name = self.history_panel_name_element()
self.assertEqual(history_name.text, self.history2_name)
self.assertEqual(self.history_panel_name(), self.history2_name)
assert_history_name_switched()
@@ -51,7 +48,7 @@ class SavedHistoriesTestCase(SharedStateSeleniumTestCase):
self.histories_click_advanced_search()
self.select_filter('sharing', 'published')
time.sleep(1)
self.sleep_for(self.wait_types.UX_RENDER)
self.assert_histories_in_grid([self.history2_name])
@@ -85,7 +82,7 @@ class SavedHistoriesTestCase(SharedStateSeleniumTestCase):
self.histories_click_advanced_search()
self.select_filter('deleted', 'True')
time.sleep(1)
self.sleep_for(self.wait_types.UX_RENDER)
# Restore the history
self.click_popup_option(self.history2_name, 'Undelete')
@@ -130,7 +127,7 @@ class SavedHistoriesTestCase(SharedStateSeleniumTestCase):
self.histories_click_advanced_search()
self.select_filter('deleted', 'True')
time.sleep(1)
self.sleep_for(self.wait_types.UX_RENDER)
# Restore multiple histories
self.check_histories([self.history2_name, self.history3_name])
@@ -254,7 +251,7 @@ class SavedHistoriesTestCase(SharedStateSeleniumTestCase):
@retry_during_transitions
def get_histories(self):
time.sleep(.5)
self.sleep_for(self.wait_types.UX_RENDER)
names = []
grid = self.wait_for_selector('#grid-table-body')
for row in grid.find_elements_by_tag_name('tr'):
@@ -273,13 +270,11 @@ class SavedHistoriesTestCase(SharedStateSeleniumTestCase):
close_button_selector = 'a[filter_key="%s"][filter_val="%s"]' % \
(filter_key, filter_value)
self.wait_for_and_click_selector(close_button_selector)
time.sleep(.5)
self.sleep_for(self.wait_types.UX_RENDER)
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):
@@ -300,8 +295,7 @@ class SavedHistoriesTestCase(SharedStateSeleniumTestCase):
def create_history(self, name):
self.home()
self.click_history_option('Create New')
self.history_panel_rename(name)
self.history_panel_create_new_with_name(name)
def select_filter(self, filter_key, filter_value):
filter_selector = 'a[filter_key="%s"][filter_val="%s"]' % \
+2 -2
View File
@@ -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, "?")
+2 -4
View File
@@ -1,5 +1,3 @@
import time
from ._workflow_fixtures import (
WORKFLOW_SIMPLE_CAT_TWICE,
WORKFLOW_WITH_INVALID_STATE,
@@ -27,7 +25,7 @@ class WorkflowEditorTestCase(SeleniumTestCase):
def test_data_input(self):
self.workflow_create_new()
menu = self.wait_for_selector(".toolMenu")
time.sleep(1)
self.sleep_for(self.wait_types.UX_RENDER)
inputs_section = menu.find_element_by_css_selector("#title___workflow__inputs__")
inputs_link = inputs_section.find_element_by_css_selector("a span")
inputs_link.click()
@@ -43,7 +41,7 @@ class WorkflowEditorTestCase(SeleniumTestCase):
workflow_populator.upload_yaml_workflow(WORKFLOW_SIMPLE_CAT_TWICE)
self.workflow_index_open()
self.workflow_index_click_option("Edit")
time.sleep(1)
self.sleep_for(self.wait_types.UX_RENDER)
self.workflow_editor_click_option("Save As")
@selenium_test
@@ -38,9 +38,8 @@ class WorkflowManagementTestCase(SeleniumTestCase):
@retry_assertion_during_transitions
def check_name():
row_element = self.workflow_index_table_row()
renamed_workflow_button = row_element.find_element_by_css_selector(".menubutton")
assert 'CoolNewName' in renamed_workflow_button.text, renamed_workflow_button.text
name = self.workflow_index_name()
assert 'CoolNewName' == name, name
check_name()
+1 -3
View File
@@ -1,5 +1,3 @@
import time
from ._workflow_fixtures import (
WORKFLOW_SIMPLE_CAT_TWICE,
WORKFLOW_WITH_OLD_TOOL_VERSION,
@@ -38,6 +36,6 @@ class WorkflowRunTestCase(SeleniumTestCase, UsesHistoryItemAssertions):
self.workflow_index_open()
self.workflow_index_click_option("Run")
time.sleep(.5)
self.sleep_for(self.wait_types.UX_TRANSITION)
# Check that this tool form contains a warning about different versions.
self.assert_warning_message(contains="different versions")