Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
073c8a7
Resolve a conditional boolean test value the way the tool loader does
guerler Sep 2, 2026
8a74f62
Validate the two conditional boolean tools that now build a request
guerler Sep 2, 2026
2962273
Run the tool form harness against the tool request submission path
guerler Aug 26, 2026
9470328
Allow tool form tests to stop at submission and skip job execution
guerler Aug 28, 2026
31a956c
Drive every framework tool test through the form without running the job
guerler Aug 29, 2026
99ec608
Run the tool form harness against the tool request submission path
guerler Aug 26, 2026
19430e7
Check the form's submitted tool state against the test case
guerler Aug 30, 2026
fb98eaa
List the form tests that cannot be driven yet, with the reason for each
guerler Aug 30, 2026
be858c4
Enable the tool request path for local runs of the form harness
guerler Aug 30, 2026
57d7c4c
Cover the tool form state comparison with unit tests
guerler Aug 30, 2026
34a3426
Compare repeat instances element by element rather than as text
guerler Aug 30, 2026
a17091e
Detect the tool request path from the recorded request, not browser t…
guerler Aug 30, 2026
ff235d1
Sort the imports the two new modules added
guerler Aug 30, 2026
8d9e2db
Hold data parameters to the dataset the test case staged
guerler Aug 30, 2026
6a7b7fd
Type the harness helpers mypy could not resolve
guerler Aug 30, 2026
9f40327
Let a rejected submission stand for tests that declare invalid input
guerler Aug 30, 2026
7cc69b8
Insert repeat instances after the conditional that reveals them
guerler Aug 30, 2026
3eea29e
Fill repeats, composites and colours the way the form expects them
guerler Aug 30, 2026
54e590b
Open only the sections the form left closed
guerler Aug 30, 2026
1958893
Clear a select before setting it and accept either the value or its l…
guerler Aug 30, 2026
7f0f855
Pick the select option whose label matches exactly
guerler Aug 30, 2026
3449f89
Set data parameters before the ones that read options from them
guerler Aug 30, 2026
b7f5df0
Stage each copy of a repeated file and accept any of them
guerler Aug 30, 2026
8b44e54
Carry dbkey and metadata into staging so validators see them
guerler Aug 30, 2026
017a06f
Record why a mismatched datatype leaves the form with nothing to offer
guerler Aug 30, 2026
bd7d4fb
Refresh the known failure reasons against what now happens
guerler Aug 30, 2026
f5f9a14
Fail loudly when a select has no option for the declared value
guerler Aug 30, 2026
0341b52
Upload a test input declared as a directory as one
guerler Aug 30, 2026
52b096d
Say what the two nested conditional cases actually do
guerler Aug 30, 2026
188bb36
Move the comparison tests out of a packaged directory
guerler Aug 30, 2026
8cd4224
Fill a repeat's datasets once its instances exist
guerler Aug 30, 2026
f4cfadc
Treat equal numbers and equal multi-values as matching
guerler Aug 31, 2026
e230429
Set a conditional selector before the parameters it re-renders
guerler Aug 31, 2026
e3c6a40
Record that an unset optional data column blocks async submission
guerler Aug 31, 2026
ef6d21d
Give a multiple select a list default when one option is selected
guerler Aug 31, 2026
3473b79
Note the accessibility violation behind the last optional select case
guerler Aug 31, 2026
55446b6
Set a declared false on an optional boolean explicitly
guerler Aug 31, 2026
5f5eac9
Discover framework tools loaded from a tool_dir
guerler Aug 31, 2026
4463d50
Shard the tool form harness job four ways
guerler Aug 31, 2026
665abe5
Record the known failures in the tool_dir fixtures
guerler Aug 31, 2026
9b47813
Drop the failure the core drill down fix resolves
guerler Aug 31, 2026
bb6a028
Record the accessibility baseline failure on the legacy select case
guerler Aug 31, 2026
2480943
Drop the drill down failures the core fix resolves
guerler Aug 31, 2026
aeff434
Drive the select a parameter with many options renders
guerler Aug 31, 2026
c9a0547
Drop the accessibility failures the toggle fix resolves
guerler Aug 31, 2026
f855457
Set collections before the parameters that read from them
guerler Aug 31, 2026
32d58ff
Match the comparison tests to the reported values
guerler Aug 31, 2026
f9db63a
Record the failures the profile gate and CI surfaced
guerler Sep 2, 2026
ee06335
Set collections after the conditional that reveals them
guerler Sep 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .github/workflows/tool_form_harness.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ on:
env:
GALAXY_CONFIG_GALAXY_URL_PREFIX: '/galaxypf'
GALAXY_TEST_E2E_TOOL_TESTS: '1'
GALAXY_CONFIG_OVERRIDE_ENABLE_TOOL_REQUESTS: 'true'
GALAXY_CONFIG_OVERRIDE_ENABLE_CELERY_TASKS: 'true'
GALAXY_TEST_DBURI: 'postgresql+psycopg://postgres:postgres@localhost:5432/galaxy?client_encoding=utf8'
GALAXY_TEST_RAISE_EXCEPTION_ON_HISTORYLESS_HDA: '1'
GALAXY_TEST_SELENIUM_RETRIES: 1
Expand All @@ -50,6 +52,7 @@ jobs:
fail-fast: false
matrix:
python-version: ['3.10']
chunk: [0, 1, 2, 3]
services:
postgres:
image: postgres:18
Expand Down Expand Up @@ -89,7 +92,7 @@ jobs:
key: galaxy-static-${{ needs.build-client.outputs.commit-id }}
path: 'galaxy root/static'
- name: Run tests
run: ./run_tests.sh --coverage -playwright lib/galaxy_test/selenium/test_tool_form_harness.py
run: ./run_tests.sh --coverage -playwright lib/galaxy_test/selenium/test_tool_form_harness.py -- --num-shards=4 --shard-id=${{ matrix.chunk }}
working-directory: 'galaxy root'
- uses: codecov/codecov-action@v7.0.0
with:
Expand All @@ -98,10 +101,10 @@ jobs:
- uses: actions/upload-artifact@v7
if: failure()
with:
name: Tool form harness test results (${{ matrix.python-version }})
name: Tool form harness test results (${{ matrix.python-version }}, ${{ matrix.chunk }})
path: 'galaxy root/run_playwright_tests.html'
- uses: actions/upload-artifact@v7
if: failure()
with:
name: Tool form harness debug info (${{ matrix.python-version }})
name: Tool form harness debug info (${{ matrix.python-version }}, ${{ matrix.chunk }})
path: 'galaxy root/database/test_errors'
1 change: 1 addition & 0 deletions client/src/utils/navigation/navigation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,7 @@ tool_form:
parameter_checkbox: 'div.ui-form-element[id="form-element-${parameter}"] .ui-switch'
parameter_checkbox_input: 'div.ui-form-element[id="form-element-${parameter}"] input[type="checkbox"]'
parameter_select: 'div.ui-form-element[id="form-element-${parameter}"] .multiselect'
parameter_select_many: 'div.ui-form-element[id="form-element-${parameter}"] .form-select-many'
parameter_input: 'div.ui-form-element[id="form-element-${parameter}"] .ui-input'
parameter_textarea: 'div.ui-form-element[id="form-element-${parameter}"] textarea'
parameter_data_input_single: 'div.ui-form-element[id="form-element-${parameter}"] button[title="Single dataset"]'
Expand Down
10 changes: 10 additions & 0 deletions lib/galaxy/selenium/navigates_galaxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
NamedTuple,
TYPE_CHECKING,
)
from urllib.parse import quote

import yaml
from selenium.webdriver.common.by import By
Expand Down Expand Up @@ -1938,6 +1939,15 @@ def tool_open(self, tool_id, outer=False):
self.scroll_into_view(tool_element)
tool_link.wait_for_and_click()

def tool_open_by_url(self, tool_id: str):
"""Open a tool form by URL rather than through tool panel search.

Shed and user-defined tools are not always reachable from the panel, and
the panel click silently leaves the browser on the current page.
"""
self.get(f"?tool_id={quote(tool_id, safe='')}&version=latest")
self.components.tool_form.execute.wait_for_visible()

def datasource_tool_open(self, tool_id):
tool_link = self.components.tool_panel.data_source_tool_link(tool_id=tool_id)
tool_element = tool_link.wait_for_present()
Expand Down
11 changes: 9 additions & 2 deletions lib/galaxy/tool_util/parameters/case.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ def _merge_into_state(
handled_inputs.update(_merge_into_state(test_parameter, context, conditional_state, state_path))
# If the discriminator was omitted, record the when _select_which_when chose so the state
# validates against that branch rather than the phantom __absent__ branch.
if test_parameter.name not in conditional_state and when.discriminator is not None:
if when.discriminator is not None and conditional_state.get(test_parameter.name) is None:
conditional_state[test_parameter.name] = when.discriminator
# Mark this conditional's discriminator consumed so a nested conditional's loose fallbacks
# do not re-match it.
Expand Down Expand Up @@ -545,7 +545,14 @@ def _select_which_when(
matched_name = test_input["name"] if test_input else None
explicit_test_value = test_input["value"] if test_input else None
if is_boolean and isinstance(explicit_test_value, str):
explicit_test_value = asbool(explicit_test_value)
truevalue = getattr(test_parameter, "truevalue", None)
falsevalue = getattr(test_parameter, "falsevalue", None)
if truevalue is not None and explicit_test_value == truevalue:
explicit_test_value = True
elif falsevalue is not None and explicit_test_value == falsevalue:
explicit_test_value = False
else:
explicit_test_value = asbool(explicit_test_value)
test_value = validate_explicit_conditional_test_value(test_parameter_name, explicit_test_value)
if test_value is None:
# Discriminator omitted: infer the active when from the params the test provides (like
Expand Down
4 changes: 4 additions & 0 deletions lib/galaxy/tool_util/parameters/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,10 @@ def _from_input_source_galaxy(input_source: InputSource, profile: float) -> Tool
BooleanParameterModel | SelectParameterModel,
_from_input_source_galaxy(test_param_input_source, profile),
)
if isinstance(test_parameter, BooleanParameterModel) and test_parameter.optional:
test_parameter.optional = False
if test_parameter.value is None:
test_parameter.value = False
whens = []
default_test_value = cond_test_parameter_default_value(test_parameter)
for value, case_inputs_sources in input_source.parse_when_input_sources():
Expand Down
Loading
Loading