From 20fd86375f814864cafb72a465d956d512ae5877 Mon Sep 17 00:00:00 2001 From: Amin Chirazi <32016576+AminChirazi@users.noreply.github.com> Date: Sat, 1 Aug 2026 13:32:27 +0400 Subject: [PATCH] fix(adapters): Select typed the option name when the option did not exist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A JS exception inside the driver does not reach Rust as an `Err`, so the throw on a missing option looked exactly like "this element is not a is itself a prefix search, so the step landed on whatever option starts with the same letters and reported success. Not a failure and not the option that was asked for: a plausible wrong answer, chosen quietly. The two cases are now different answers rather than the same one. A status value comes back and is inspected: `not_select` keeps the fall-through to typing, which is what it was always for, and a name matching nothing fails naming it. Prefix matching is untouched — value, then exact visible text, then prefix — so `Audit` selecting `Auditor` stays correct. Found while building the multi-option form, whose first version had the same defect and was caught by its own red-path test. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 21 ++++ crates/flowproof-adapters/src/web.rs | 32 ++++- .../tests/select_missing_option_e2e.rs | 110 ++++++++++++++++++ docs/authoring.md | 2 +- 4 files changed, 158 insertions(+), 7 deletions(-) create mode 100644 crates/flowproof-cli/tests/select_missing_option_e2e.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d46a50..2c753f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -105,6 +105,27 @@ together). ### Fixed +- **`Select` typed the option name when the option did not exist.** A JS + exception inside the driver does not reach Rust as an `Err`, so the throw + on a missing option looked exactly like "this element is not a + `` is itself a prefix search, so the step landed on + whatever option starts with the same letters and reported success. Not a + failure and not the option that was asked for: a plausible wrong answer, + chosen quietly. + + The two cases are now different answers rather than the same one. A status + value comes back and is inspected: `not_select` keeps the fall-through to + typing, which is what it was always for, and a name matching nothing fails + naming it. Prefix matching is untouched — it is the documented ladder + (value, then exact visible text, then prefix), and `Audit` selecting + `Auditor` is correct. + + Found while building the multi-option form, whose first version had the + same defect and was caught by its own red-path test. + - **The docs never said what a typed capture does with the text around it.** `authoring.md` showed one form, `Type ${captured.oid} into the …`, and said the value is read fresh on every replay. It did not say that diff --git a/crates/flowproof-adapters/src/web.rs b/crates/flowproof-adapters/src/web.rs index 47c71fa..b799050 100644 --- a/crates/flowproof-adapters/src/web.rs +++ b/crates/flowproof-adapters/src/web.rs @@ -2287,31 +2287,51 @@ impl AppDriver for WebAppDriver { let handled = self.with_element(&locator, &format!("selecting in [{selector}]"), |element| { element.call_js_fn( + // A STATUS STRING, not a throw and not a bare boolean. + // A JS exception does not reach Rust as an `Err` here, + // so throwing on a missing option looked identical to + // "this is not a `" — and fell through to typing the option's name into the +//! dropdown. Typing into a ` + + + + + +
none
+ + +"#; + +fn fixture(dir: &std::path::Path) -> String { + std::fs::create_dir_all(dir).expect("temp dir"); + let page = dir.join("roles.html"); + std::fs::write(&page, PAGE).expect("page written"); + format!("file://{}", page.display()) +} + +/// The red path: an option that matches NOTHING on the ladder. +/// +/// Note what is deliberately absent here. `Audit` is not one of these, +/// because prefix matching is documented behaviour — value, then exact +/// visible text, then prefix — so `Audit` legitimately selects `Auditor`. +/// The bug was never about prefixes; it was that a name matching nothing +/// at all fell through to typing, and typing into a ``: committed via the value setter, fires `input`+`change` (React-safe). `in the` and `… dropdown` also accepted | +| `Select