Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 7 additions & 7 deletions client/src/components/Workflow/Run/WorkflowRunFormSimple.vue
Original file line number Diff line number Diff line change
Expand Up @@ -161,17 +161,18 @@ const formInputs = computed(() => {
if (props.requestState) {
if (props.isRerun) {
const requestStateKeys = Object.keys(props.requestState);
const stateKey = String(rerunStateIndex);

let value;
if (props.requestState[rerunStateIndex]) {
if (stateKey in props.requestState) {
// request state has the step_label as key
value = props.requestState[rerunStateIndex];
} else if (requestStateKeys[i] !== undefined && requestStateKeys[i] === "") {
value = props.requestState[stateKey];
} else if (requestStateKeys[i] === "") {
// request state has "" as key on the `i` position
value = Object.values(props.requestState)[i];
}

if (value) {
if (value !== undefined) {
if (stepType === "data_input" || stepType === "data_collection_input") {
// Note: This is different from workflow landings because `WorkflowInvocationRequestModel`
// does not provide an object with `values` property.
Expand All @@ -182,9 +183,8 @@ const formInputs = computed(() => {
stepAsInput.value = value;
}
}
} else if (props.requestState[stepLabel]) {
const value = props.requestState[stepLabel];
stepAsInput.value = value;
} else if (String(stepLabel) in props.requestState) {
stepAsInput.value = props.requestState[String(stepLabel)];
}
}

Expand Down
1 change: 1 addition & 0 deletions client/src/utils/navigation/navigation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,7 @@ tool_form:
parameter_div: 'div.ui-form-element[id="form-element-${parameter}"]'
parameter_error: 'div.ui-form-element[id="form-element-${parameter}"] .ui-form-error-text'
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_input: 'div.ui-form-element[id="form-element-${parameter}"] .ui-input'
parameter_textarea: 'div.ui-form-element[id="form-element-${parameter}"] textarea'
Expand Down
10 changes: 10 additions & 0 deletions lib/galaxy/selenium/playwright_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,16 @@ def is_enabled(self) -> bool:
"""
return self._element.is_enabled()

def is_selected(self) -> bool:
"""
Check if a checkbox, radio button, or option is selected.

Playwright's is_checked() only handles checkbox/radio inputs and raises
for <option> elements, so fall back to evaluating the element's
``checked``/``selected`` property to mirror Selenium's behavior.
"""
return bool(self._element.evaluate("(el) => !!(el.checked || el.selected)"))

def submit(self) -> None:
"""
Submit a form element.
Expand Down
4 changes: 4 additions & 0 deletions lib/galaxy/selenium/web_element_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ def is_enabled(self) -> bool:
"""Check if the element is enabled (not disabled)."""
...

def is_selected(self) -> bool:
"""Check if a checkbox, radio, or option element is selected."""
...

def submit(self) -> None:
"""Submit a form element."""
...
Expand Down
83 changes: 83 additions & 0 deletions lib/galaxy_test/selenium/test_workflow_rerun.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,19 @@
UsesHistoryItemAssertions,
)

WORKFLOW_BOOLEAN_PARAMETER_DEFAULT_TRUE = """
class: GalaxyWorkflow
inputs:
bool_param:
type: boolean
default: true
steps:
echo_bool:
tool_id: gx_boolean
in:
parameter: bool_param
"""


class TestWorkflowRun(SeleniumTestCase, UsesHistoryItemAssertions, RunsWorkflows):
ensure_registered = True
Expand Down Expand Up @@ -65,6 +78,76 @@ def test_workflow_rerun(self):
form_element.changed_value_badge.wait_for_visible()
self.screenshot("workflow_rerun_changed_input")

@selenium_test
@managed_history
def test_workflow_rerun_preserves_false_boolean_parameter(self):
"""Regression test for boolean workflow parameters reverting to default on rerun.

Setting a boolean ``parameter_input`` to ``false`` (overriding a
``default: true``), running the workflow, and re-running the invocation
previously caused the form to silently fall back to the default value
because of a falsy-value check in WorkflowRunFormSimple.vue. The form
should instead show the value the user actually used.
"""
invocations = self.components.invocations

self.workflow_run_open_workflow(WORKFLOW_BOOLEAN_PARAMETER_DEFAULT_TRUE)

# Override the boolean default of true to false before submitting.
# The boolean is the workflow's only input, hence step_index 0.
self._toggle_workflow_run_boolean(parameter="0", target=False)
self.screenshot("workflow_run_boolean_false_before_submit")

self.workflow_run_submit()
self.sleep_for(self.wait_types.UX_TRANSITION)
self.workflow_run_wait_for_ok(hid=1)

# Submitting lands us on the new invocation page; the rerun button is
# right there on the invocation state details. We're still in the
# invocation's history, so no "switch history" confirmation appears.
invocations.state_details.wait_for_visible()
invocations.workflow_rerun_button.wait_for_and_click()
self.sleep_for(self.wait_types.UX_TRANSITION)

# The boolean should be preserved as false on the rerun form rather than
# silently reset to the workflow's default of true.
checkbox = self.components.tool_form.parameter_checkbox_input(parameter="0").wait_for_present()
assert (
not checkbox.is_selected()
), "Boolean parameter input was reset to its default 'true' instead of preserving the previous run's 'false' value"

# Without changes, the boolean should not be flagged as a changed input.
form_element = self.components.workflow_run.form_element._(index=0)
form_element.changed_value_badge.assert_absent()
self.screenshot("workflow_rerun_boolean_preserved_false")

# Toggling the value back to true should flag it as changed.
self._toggle_workflow_run_boolean(parameter="0", target=True)
form_element.changed_value_badge.wait_for_visible()
self.screenshot("workflow_rerun_boolean_changed_input")

# Submit the rerun and verify the new invocation actually recorded the
# value shown in the form (true) on its Inputs tab.
self.workflow_run_submit()
self.sleep_for(self.wait_types.UX_TRANSITION)
self.workflow_run_wait_for_ok(hid=2)
invocations.state_details.wait_for_visible()
invocations.invocation_tab(label="Inputs").wait_for_and_click()
self._assert_input_table_has_parameter("bool_param", "true")
self.screenshot("workflow_rerun_boolean_inputs_tab")

@retry_assertion_during_transitions
def _assert_input_table_has_parameter(self, label: str, value: str):
table = self.wait_for_selector('[data-description="input table"]')
text = table.text
assert label in text, f"Expected input label '{label}' in inputs table, got: {text!r}"
assert value in text, f"Expected input value '{value}' in inputs table, got: {text!r}"

def _toggle_workflow_run_boolean(self, parameter: str, target: bool):
checkbox = self.components.tool_form.parameter_checkbox_input(parameter=parameter).wait_for_present()
if checkbox.is_selected() != target:
self.execute_script("arguments[0].click();", checkbox)

@retry_assertion_during_transitions
def _assert_history_name_is(self, expected_name=None):
name = self.history_panel_name()
Expand Down
Loading