mod common;
use std::collections::HashMap;
use ui_automata::mock::{MockDesktop, MockElement, mock_desktop_from_yaml};
use ui_automata::yaml::WorkflowFile;
use ui_automata::{AutomataError, Executor};
fn desktop_with_text(text: &str) -> MockDesktop {
MockDesktop::new(vec![MockElement::parent(
"window",
"App",
vec![MockElement::leaf_text("edit", "field", text)],
)])
}
fn run_workflow(
yaml: &str,
params: HashMap<String, String>,
desktop: MockDesktop,
) -> Result<(HashMap<String, String>, ui_automata::Output), AutomataError> {
let wf = WorkflowFile::load_from_str(yaml, ¶ms).expect("YAML parse failed");
let mut executor = Executor::new(desktop);
wf.run(&mut executor, None, None)
.map(|state| (state.locals, state.output))
}
fn no_params() -> HashMap<String, String> {
HashMap::new()
}
#[test]
fn eval_add_constant_to_extracted_value() {
let desktop = desktop_with_text("10");
let yaml = r#"
name: test
anchors:
app: { type: Root, selector: "[name=App]" }
phases:
- name: compute
mount: [app]
steps:
- intent: extract the number
action:
type: Extract
key: raw
scope: app
selector: ">> [role=edit]"
expect: { type: Always }
- intent: add 5
action:
type: Eval
key: result
expr: "raw + 5"
expect: { type: Always }
"#;
let (locals, _) = run_workflow(yaml, no_params(), desktop).unwrap();
assert_eq!(locals.get("result").map(String::as_str), Some("15"));
}
#[test]
fn eval_string_concat_with_extracted_value() {
let desktop = desktop_with_text("world");
let yaml = r#"
name: test
anchors:
app: { type: Root, selector: "[name=App]" }
phases:
- name: compute
mount: [app]
steps:
- intent: extract word
action:
type: Extract
key: word
scope: app
selector: ">> [role=edit]"
expect: { type: Always }
- intent: prepend greeting
action:
type: Eval
key: greeting
expr: "'hello ' + word"
expect: { type: Always }
"#;
let (locals, _) = run_workflow(yaml, no_params(), desktop).unwrap();
assert_eq!(
locals.get("greeting").map(String::as_str),
Some("hello world")
);
}
#[test]
fn eval_uses_param_as_operand() {
let desktop = desktop_with_text("6");
let yaml = r#"
name: test
params:
- name: factor
default: "1"
anchors:
app: { type: Root, selector: "[name=App]" }
phases:
- name: compute
mount: [app]
steps:
- intent: extract number
action:
type: Extract
key: raw
scope: app
selector: ">> [role=edit]"
expect: { type: Always }
- intent: multiply by param
action:
type: Eval
key: result
expr: "raw * param.factor"
expect: { type: Always }
"#;
let mut params = HashMap::new();
params.insert("factor".into(), "7".into());
let (locals, _) = run_workflow(yaml, params, desktop).unwrap();
assert_eq!(locals.get("result").map(String::as_str), Some("42"));
}
#[test]
fn eval_result_accessible_in_subsequent_step() {
let desktop = mock_desktop_from_yaml(
r#"
role: window
name: App
children:
- role: edit
name: field
text: "3"
- role: text
name: target
text: ""
"#,
);
let yaml = r#"
name: test
anchors:
app: { type: Root, selector: "[name=App]" }
phases:
- name: compute
mount: [app]
steps:
- intent: extract base
action:
type: Extract
key: base
scope: app
selector: ">> [role=edit]"
expect: { type: Always }
- intent: compute square
action:
type: Eval
key: squared
expr: "base * base"
expect: { type: Always }
- intent: verify computed value is reachable via output substitution
action:
type: NoOp
expect:
type: ElementHasText
scope: app
selector: ">> [role=edit]"
pattern:
exact: "{output.base}"
"#;
let (locals, _) = run_workflow(yaml, no_params(), desktop).unwrap();
assert_eq!(locals.get("squared").map(String::as_str), Some("9"));
}
#[test]
fn eval_split_lines_on_extracted_multiline() {
let desktop = desktop_with_text("line0\nline1\nresult");
let yaml = r#"
name: test
anchors:
app: { type: Root, selector: "[name=App]" }
phases:
- name: compute
mount: [app]
steps:
- intent: extract text block
action:
type: Extract
key: block
scope: app
selector: ">> [role=edit]"
expect: { type: Always }
- intent: pick last line
action:
type: Eval
key: last_line
expr: "split_lines(block, -1)"
expect: { type: Always }
"#;
let (locals, _) = run_workflow(yaml, no_params(), desktop).unwrap();
assert_eq!(locals.get("last_line").map(String::as_str), Some("result"));
}
#[test]
fn eval_round_extracted_decimal() {
let desktop = desktop_with_text("3.75");
let yaml = r#"
name: test
anchors:
app: { type: Root, selector: "[name=App]" }
phases:
- name: compute
mount: [app]
steps:
- intent: extract decimal
action:
type: Extract
key: raw
scope: app
selector: ">> [role=edit]"
expect: { type: Always }
- intent: round it
action:
type: Eval
key: rounded
expr: "round(raw)"
expect: { type: Always }
"#;
let (locals, _) = run_workflow(yaml, no_params(), desktop).unwrap();
assert_eq!(locals.get("rounded").map(String::as_str), Some("4"));
}
#[test]
fn eval_chained_computations() {
let desktop = desktop_with_text("5");
let yaml = r#"
name: test
anchors:
app: { type: Root, selector: "[name=App]" }
phases:
- name: compute
mount: [app]
steps:
- intent: extract base
action:
type: Extract
key: base
scope: app
selector: ">> [role=edit]"
expect: { type: Always }
- intent: double it
action:
type: Eval
key: doubled
expr: "base * 2"
expect: { type: Always }
- intent: add 1 to doubled
action:
type: Eval
key: final
expr: "doubled + 1"
expect: { type: Always }
"#;
let (locals, _) = run_workflow(yaml, no_params(), desktop).unwrap();
assert_eq!(locals.get("doubled").map(String::as_str), Some("10"));
assert_eq!(locals.get("final").map(String::as_str), Some("11"));
}
#[test]
fn eval_sum_1_to_100_with_flow_control_loop() {
let desktop = MockDesktop::new(vec![]);
let yaml = r#"
name: sum_loop
phases:
- name: init
steps:
- intent: initialise counter
action:
type: Eval
key: i
expr: "1"
expect:
type: Always
- intent: initialise total
action:
type: Eval
key: total
expr: "0"
expect:
type: Always
- name: check
flow_control:
condition:
type: EvalCondition
expr: "i > 100"
go_to: done
- name: loop_body
steps:
- intent: accumulate
action:
type: Eval
key: total
expr: "total + i"
expect:
type: Always
- intent: increment counter
action:
type: Eval
key: i
expr: "i + 1"
expect:
type: Always
- name: loop_back
flow_control:
condition:
type: Always
go_to: check
- name: done
steps:
- intent: record result into output buffer
action:
type: Eval
key: total
expr: "total"
output: result
expect:
type: Always
"#;
let (locals, output) = run_workflow(yaml, no_params(), desktop).unwrap();
assert_eq!(locals.get("total").map(String::as_str), Some("5050"));
assert_eq!(
output.get("result").first().map(String::as_str),
Some("5050")
);
}