Skip to content

Commit

Permalink
Initial work on selenium testing for libraries.
Browse files Browse the repository at this point in the history
- Test importing datasets from histories.
- Test basic importing of a dataset from a path.
- Test creating a folder.
- Test create library, renaming a library, filtering libraries by name, and sorting of libraries.
- Test various buttons (e.g. details, help).
  • Loading branch information
jmchilton committed Oct 3, 2017
1 parent 9481b85 commit 3c64167
Show file tree
Hide file tree
Showing 6 changed files with 350 additions and 3 deletions.
88 changes: 88 additions & 0 deletions test/galaxy_selenium/navigates_galaxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ def submit_login(self, email, password=None, assert_valid=True, retries=0):
self.wait_for_and_click(self.navigation.masthead.labels.login)

with self.main_panel():
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")
Expand Down Expand Up @@ -591,6 +592,90 @@ def workflow_editor_click_options(self):
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()
Expand Down Expand Up @@ -724,6 +809,9 @@ def click_masthead_libraries(self):
def click_masthead_workflow(self):
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(self.navigation.workflows.selectors.new_button)

Expand Down
6 changes: 5 additions & 1 deletion test/galaxy_selenium/navigation-data.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ selectors:

user: '//ul[@id="user"]'
workflow: '//ul[@id="workflow"]'
shared_data: '//ul[@id="shared"]'

toolMenu:
container: '.toolMenuContainer'
Expand Down Expand Up @@ -55,7 +56,7 @@ labels:
menus:
analyze: 'Analyze Data'
workflow: 'Workflow'
libraries: 'Shared Data'
shared_data: 'Shared Data'
visualization: 'Visualization'
help: 'Help'
user: 'User'
Expand All @@ -64,6 +65,9 @@ labels:
register: 'Register'
login: 'Login'
logout: 'Logout'

sharedMenu:
libraries: 'Data Libraries'

tools:
upload:
Expand Down
30 changes: 30 additions & 0 deletions test/galaxy_selenium/navigation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,36 @@ workflows:
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'
21 changes: 19 additions & 2 deletions test/selenium_tests/framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
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
Expand All @@ -36,6 +37,8 @@
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)
Expand All @@ -51,7 +54,8 @@

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
Expand Down Expand Up @@ -156,7 +160,7 @@ def write_to_error_directory(self, write_file_func):
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
Expand All @@ -166,6 +170,7 @@ class SeleniumTestCase(FunctionalTestCase, NavigatesGalaxy):
# 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()
Expand All @@ -177,6 +182,9 @@ def setUp(self):
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:
Expand Down Expand Up @@ -290,6 +298,15 @@ def assert_initial_history_panel_state_correct(self):
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)
Expand Down
103 changes: 103 additions & 0 deletions test/selenium_tests/test_library_contents.py
Original file line number Diff line number Diff line change
@@ -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()

0 comments on commit 3c64167

Please sign in to comment.