diff --git a/client/src/components/History/CurrentHistory/HistoryCounter.vue b/client/src/components/History/CurrentHistory/HistoryCounter.vue
index 0c41424f55f0..bc948db4d131 100644
--- a/client/src/components/History/CurrentHistory/HistoryCounter.vue
+++ b/client/src/components/History/CurrentHistory/HistoryCounter.vue
@@ -69,7 +69,7 @@
:title="reloadButtonTitle"
:variant="reloadButtonVariant"
size="sm"
- class="rounded-0 text-decoration-none"
+ class="rounded-0 text-decoration-none history-refresh-button"
@click="reloadContents()">
diff --git a/client/src/components/User/DiskUsage/Quota/QuotaUsageBar.vue b/client/src/components/User/DiskUsage/Quota/QuotaUsageBar.vue
index ce3dcca4dc81..a1319b2474d2 100644
--- a/client/src/components/User/DiskUsage/Quota/QuotaUsageBar.vue
+++ b/client/src/components/User/DiskUsage/Quota/QuotaUsageBar.vue
@@ -60,11 +60,15 @@ defineExpose({
{{ storageSourceText }}
-
+
{{ quotaUsage.niceTotalDiskUsage }}
of {{ quotaUsage.niceQuota }} used
-
+
{{ quotaUsage.quotaPercent }}{{ percentOfDiskQuotaUsedText }}
str:
@property
@abc.abstractmethod
def component_locator(self) -> LocatorT:
- """Return a (by, selector) Selenium elment locator tuple for this selector."""
+ """Return a (by, selector) Selenium element locator tuple for this selector."""
@property
def selenium_locator(self) -> Tuple[str, str]:
diff --git a/lib/galaxy/selenium/has_driver.py b/lib/galaxy/selenium/has_driver.py
index 1d122111b048..2dcb4fd954fd 100644
--- a/lib/galaxy/selenium/has_driver.py
+++ b/lib/galaxy/selenium/has_driver.py
@@ -11,7 +11,10 @@
Union,
)
-from selenium.common.exceptions import TimeoutException as SeleniumTimeoutException
+from selenium.common.exceptions import (
+ NoSuchElementException,
+ TimeoutException as SeleniumTimeoutException,
+)
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
@@ -76,8 +79,15 @@ def assert_selector_absent(self, selector: str):
def find_elements(self, selector_template: Target) -> List[WebElement]:
return self.driver.find_elements(*selector_template.element_locator)
- def assert_absent(self, selector_template: Target):
- assert len(self.find_elements(selector_template)) == 0
+ def assert_absent(self, selector_template: Target) -> None:
+ elements = self.find_elements(selector_template)
+ if len(elements) != 0:
+ description = selector_template.description
+ any_displayed = False
+ for element in elements:
+ any_displayed = any_displayed or element.is_displayed()
+ msg = f"Expected DOM elements [{elements}] to be empty for selector target {description} - any actually displayed? [{any_displayed}]"
+ raise AssertionError(msg)
def element_absent(self, selector_template: Target) -> bool:
return len(self.find_elements(selector_template)) == 0
@@ -239,7 +249,10 @@ def click_selector(self, selector: str):
def fill(self, form: WebElement, info: dict):
for key, value in info.items():
- input_element = form.find_element(By.NAME, key)
+ try:
+ input_element = form.find_element(By.NAME, key)
+ except NoSuchElementException:
+ input_element = form.find_element(By.ID, key)
input_element.send_keys(value)
def click_submit(self, form: WebElement):
diff --git a/lib/galaxy/selenium/navigates_galaxy.py b/lib/galaxy/selenium/navigates_galaxy.py
index fb451c3fe36c..1b3d51274234 100644
--- a/lib/galaxy/selenium/navigates_galaxy.py
+++ b/lib/galaxy/selenium/navigates_galaxy.py
@@ -393,6 +393,11 @@ def history_panel_wait_for_hid_ok(self, hid, allowed_force_refreshes=0):
def history_panel_wait_for_hid_deferred(self, hid, allowed_force_refreshes=0):
return self.history_panel_wait_for_hid_state(hid, "deferred", allowed_force_refreshes=allowed_force_refreshes)
+ def wait_for_hid_ok_and_open_details(self, hid):
+ self.history_panel_wait_for_hid_ok(hid, allowed_force_refreshes=1)
+ self.history_panel_click_item_title(hid=hid)
+ self.history_panel_item_view_dataset_details(hid)
+
def history_panel_item_component(self, history_item=None, hid=None, multi_history_panel=False):
assert hid
return self.content_item_by_attributes(hid=hid, multi_history_panel=multi_history_panel)
@@ -1078,6 +1083,41 @@ def navigate_to_pages(self):
def admin_open(self):
self.components.masthead.admin.wait_for_and_click()
+ def create_quota(
+ self,
+ name: Optional[str] = None,
+ description: Optional[str] = None,
+ amount: Optional[str] = None,
+ quota_source_label: Optional[str] = None,
+ user: Optional[str] = None,
+ ):
+ admin_component = self.components.admin
+
+ self.admin_open()
+ quota_link = admin_component.index.quotas
+ quota_link.wait_for_and_click()
+ quota_component = admin_component.quota
+
+ quota_component.add_new.wait_for_and_click()
+ form = quota_component.add_form.wait_for_visible()
+
+ name = name or self._get_random_name()
+ description = description or f"quota description for {name}"
+ amount = amount or ""
+ self.fill(
+ form,
+ {
+ "name": name,
+ "description": description,
+ "amount": amount,
+ },
+ )
+ if quota_source_label:
+ self.select2_set_value("#quota_source_label", quota_source_label)
+ if user:
+ self.select2_set_value("#in_users", user)
+ quota_component.add_form_submit.wait_for_and_click()
+
def select_dataset_from_lib_import_modal(self, filenames):
for name in filenames:
self.components.libraries.folder.select_import_dir_item(name=name).wait_for_and_click()
@@ -1376,6 +1416,22 @@ def datasource_tool_open(self, tool_id):
self.driver.execute_script("arguments[0].scrollIntoView(true);", tool_element)
tool_link.wait_for_and_click()
+ def run_environment_test_tool(self, inttest_value="42", select_storage: Optional[str] = None):
+ self.home()
+ self.tool_open("environment_variables")
+ if select_storage:
+ self.components.tool_form.storage_options.wait_for_and_click()
+ self.select_storage(select_storage)
+ self.tool_set_value("inttest", inttest_value)
+ self.tool_form_execute()
+
+ def select_storage(self, storage_id: str) -> None:
+ selection_component = self.components.preferences.object_store_selection
+ selection_component.option_buttons.wait_for_present()
+ button = selection_component.option_button(object_store_id=storage_id)
+ button.wait_for_and_click()
+ selection_component.option_buttons.wait_for_absent_or_hidden()
+
def create_page_and_edit(self, name=None, slug=None, screenshot_name=None):
name = self.create_page(name=name, slug=slug, screenshot_name=screenshot_name)
self.click_grid_popup_option(name, "Edit content")
@@ -1595,7 +1651,7 @@ def history_multi_view_display_collection_contents(self, collection_hid, collect
def history_panel_item_view_dataset_details(self, hid):
item = self.history_panel_item_component(hid=hid)
- item.dataset_operations_dropdown.wait_for_and_click()
+ item.dataset_operations.wait_for_visible()
item.info_button.wait_for_and_click()
self.components.dataset_details._.wait_for_visible()
diff --git a/lib/galaxy_test/selenium/test_history_dataset_state.py b/lib/galaxy_test/selenium/test_history_dataset_state.py
index eae0342fc4f2..ffa842a66b62 100644
--- a/lib/galaxy_test/selenium/test_history_dataset_state.py
+++ b/lib/galaxy_test/selenium/test_history_dataset_state.py
@@ -126,15 +126,13 @@ def _assert_action_buttons(self, hid, expected_buttons=None):
def _assert_downloadable(self, hid, is_downloadable=True):
item = self.history_panel_item_component(hid=hid)
- item.dataset_operations_dropdown.wait_for_and_click()
+ item.dataset_operations.wait_for_visible()
item.info_button.wait_for_visible()
if is_downloadable:
assert item.download_button.is_displayed
else:
item.download_button.assert_absent_or_hidden()
-
- # close menu...
- item.dataset_operations_dropdown.wait_for_and_click()
+ item.dataset_operations.wait_for_visible()
self.sleep_for(self.wait_types.UX_RENDER)
def _assert_buttons(self, hid, expected_buttons):
diff --git a/test/integration_selenium/test_objectstore_selection.py b/test/integration_selenium/test_objectstore_selection.py
index cbc3b2ac4d64..85be2f937430 100644
--- a/test/integration_selenium/test_objectstore_selection.py
+++ b/test/integration_selenium/test_objectstore_selection.py
@@ -1,8 +1,5 @@
import string
-from typing import (
- Optional,
- TYPE_CHECKING,
-)
+from typing import TYPE_CHECKING
from galaxy_test.driver.integration_util import ConfiguresObjectStores
from galaxy_test.selenium.framework import managed_history
@@ -96,10 +93,8 @@ def handle_galaxy_config_kwds(cls, config):
@selenium_test
@managed_history
def test_0_tools_to_default(self):
- self._run_environment_test_tool()
- self.history_panel_wait_for_hid_ok(1)
- self.history_panel_click_item_title(hid=1)
- self.history_panel_item_view_dataset_details(1)
+ self.run_environment_test_tool()
+ self.wait_for_hid_ok_and_open_details(1)
details = self.components.object_store_details
text = details.stored_by_name.wait_for_text()
assert "High Performance Storage" in text
@@ -109,11 +104,8 @@ def test_0_tools_to_default(self):
@selenium_test
@managed_history
def test_1_tools_override_run(self):
- self._run_environment_test_tool(select_storage="second")
-
- self.history_panel_wait_for_hid_ok(1)
- self.history_panel_click_item_title(hid=1)
- self.history_panel_item_view_dataset_details(1)
+ self.run_environment_test_tool(select_storage="second")
+ self.wait_for_hid_ok_and_open_details(1)
details = self.components.object_store_details
text = details.stored_by_name.wait_for_text()
assert "Second Tier Storage" in text
@@ -125,11 +117,9 @@ def test_2_user_override(self):
self.navigate_to_user_preferences()
preferences = self.components.preferences
preferences.preferred_storage.wait_for_and_click()
- self._select_storage("second")
- self._run_environment_test_tool()
- self.history_panel_wait_for_hid_ok(1)
- self.history_panel_click_item_title(hid=1)
- self.history_panel_item_view_dataset_details(1)
+ self.select_storage("second")
+ self.run_environment_test_tool()
+ self.wait_for_hid_ok_and_open_details(1)
details = self.components.object_store_details
text = details.stored_by_name.wait_for_text()
assert "Second Tier Storage" in text
@@ -145,30 +135,75 @@ def test_3_user_un_override(self):
self.navigate_to_user_preferences()
preferences = self.components.preferences
preferences.preferred_storage.wait_for_and_click()
- self._select_storage("__null__")
+ self.select_storage("__null__")
- self._run_environment_test_tool()
- self.history_panel_wait_for_hid_ok(1)
- self.history_panel_click_item_title(hid=1)
- self.history_panel_item_view_dataset_details(1)
+ self.run_environment_test_tool()
+ self.wait_for_hid_ok_and_open_details(1)
details = self.components.object_store_details
text = details.stored_by_name.wait_for_text()
assert "High Performance Storage" in text
details.badge_of_type(type="faster").wait_for_present()
details.badge_of_type(type="more_stable").wait_for_present()
- def _run_environment_test_tool(self, inttest_value="42", select_storage: Optional[str] = None):
- self.home()
- self.tool_open("environment_variables")
- if select_storage:
- self.components.tool_form.storage_options.wait_for_and_click()
- self._select_storage(select_storage)
- self.tool_set_value("inttest", inttest_value)
- self.tool_form_execute()
-
- def _select_storage(self, storage_id: str) -> None:
- selection_component = self.components.preferences.object_store_selection
- selection_component.option_buttons.wait_for_present()
- button = selection_component.option_button(object_store_id=storage_id)
- button.wait_for_and_click()
- selection_component.option_buttons.wait_for_absent_or_hidden()
+
+class TestMultipleQuotasSeleniumIntegration(SeleniumIntegrationTestCase, ConfiguresObjectStores):
+ dataset_populator: "SeleniumSessionDatasetPopulator"
+ run_as_admin = True
+
+ @classmethod
+ def handle_galaxy_config_kwds(cls, config):
+ cls._configure_object_store(MSI_EXAMPLE_OBJECT_STORE_CONFIG_TEMPLATE, config)
+ config["enable_quotas"] = True
+
+ @selenium_test
+ def test_multiple_quota_sources_for_user(self):
+ expected_bytes = 17
+
+ # create a user to create a quota for...
+ user_email = self._get_random_email("quota_user")
+ self.register(user_email)
+ self.logout()
+
+ # give them a quota on second_tier of 10 times the
+ # size of the output of the test tool.
+
+ self.admin_login()
+ quota_name = self._get_random_name(prefix="secondquota")
+ self.create_quota(
+ name=quota_name,
+ amount=f"{expected_bytes * 10} B",
+ quota_source_label="second_tier",
+ user=user_email,
+ )
+ admin_component = self.components.admin
+ quota_component = admin_component.quota
+ quota_component.items.wait_for_element_count_of_at_least(1)
+ self.logout()
+
+ # run the tool twice - once in the default object store without
+ # quota configured and once in second_tier storage with a quota
+ # configured.
+ self.submit_login(user_email)
+ self.run_environment_test_tool()
+ self.sleep_for(self.wait_types.UX_TRANSITION)
+ self.run_environment_test_tool(select_storage="second")
+
+ # Assert no quota information on an object store without
+ # quota configured and check the usage and percent on the
+ # the dataset stored in an object store with a configured
+ # quota.
+
+ details = self.components.object_store_details
+ self.wait_for_hid_ok_and_open_details(1)
+ details.usage_percent.assert_absent()
+
+ self.wait_for_hid_ok_and_open_details(2)
+
+ usage_summary_el = details.usage_details.wait_for_visible()
+ assert usage_summary_el.get_attribute("quota-source-label") == "second_tier"
+
+ bytes_el = details.usage_bytes.wait_for_visible()
+ assert bytes_el.get_attribute("data-quota-usage") == f"{expected_bytes}"
+
+ percent_el = details.usage_percent.wait_for_visible()
+ assert percent_el.get_attribute("data-quota-percent") == "10"