Skip to content

feat: add text locator cli - #151

Closed
cpendery (cpendery) wants to merge 2 commits into
feat/text-expectationsfrom
feat/text-locators-cli
Closed

feat: add text locator cli#151
cpendery (cpendery) wants to merge 2 commits into
feat/text-expectationsfrom
feat/text-locators-cli

Conversation

@cpendery

@cpendery cpendery (cpendery) commented Aug 16, 2026

Copy link
Copy Markdown
Member

No description provided.

Signed-off-by: cpendery <cpendery@vt.edu>
Signed-off-by: cpendery <cpendery@vt.edu>
@cpendery cpendery (cpendery) changed the title feat/text locators cli feat: add text locator cli Aug 16, 2026
@cpendery

Copy link
Copy Markdown
Member Author

Superseded by #149, which now contains the complete text-locator change.

@cpendery
cpendery (cpendery) deleted the feat/text-locators-cli branch August 20, 2026 00:03
cpendery (cpendery) added a commit that referenced this pull request Aug 29, 2026
## Summary

Adds lazy, reusable locators to Rust, Python, and JavaScript. The APIs
are `getByText` / `getByStyle` in JavaScript and `get_by_text` /
`get_by_style` in Python and Rust. Text and contiguous per-row style-run
stages can be chained with `within`, `after`, or `before`. Repeated
parent matches divide relative regions into separate segments.

Locators support literal and regex matching, viewport and
full-scrollback searches, whitespace normalization, and match locations.
Occurrences are selected only by chaining `any`, `unique`, `first`,
`last`, or `nth`. Every read or action resolves against the current
terminal. Clicks target the middle matched cell, and highlights appear
in the live monitor and SVG screenshots. Bare `wait`, `highlight`, and
`expect` accept any match; `location` and `click` require one match; and
`unique().expect()` requires exactly one.

Style expectations use nested `getByStyle` / `get_by_style` stages. In
the default `within` direction, the style stage keeps a parent match
only when all its visible cells satisfy the style.

The CLI exposes `find|expect|click|highlight text "T"` with the same
selector and optional style flags. This removes `wait text`, redundant
`--no-strict`, and the one-shot `findText` / `find_text`, `waitText` /
`wait_text`, and `expectText` / `expect_text` APIs.

N-API and PyO3 receive typed locator stage arrays and action parameters
without locator JSON serialization. Dense regex match offsets are
converted in one forward pass. Occurrences are selected before match
cells are materialized unless a style filter must run first.
Full-scrollback locations use 32-bit row coordinates, preserving rows
beyond 65,535.

Tests cover `any` / `unique` / `first` / `last` / `nth` expectations,
negation with zero, one, or multiple matches, exact counts, and partial
style filtering in all three libraries. The package version is the sole
daemon compatibility version.

Text matching and actions use the generic locator engine. The four
cleanup revisions remove 3,081 lines and add 1,978, for a net reduction
of 1,103 lines.

## Examples

```js
const save = terminal
  .getByText("Settings")
  .getByText("Save", {
    whitespace: "normalize",
    direction: "after",
  })
  .unique();

await save.wait();
await save.expect();
await save.click();

await terminal
  .getByText("Warning")
  .getByStyle({ bold: true })
  .unique()
  .expect();
```

```python
from tui_test import TextStyle

save = (
    terminal
    .get_by_text("Settings")
    .get_by_text(
        "Save",
        whitespace="normalize",
        direction="after",
    )
    .unique()
)

await save.wait()
await save.expect()
await save.click()

await (
    terminal
    .get_by_text("Warning")
    .get_by_style(TextStyle(bold=True))
    .unique()
    .expect()
)
```

```rust
use tui_test::LocatorDirection;

let save = terminal
    .get_by_text("Settings")
    .get_by_text_relative("Save", LocatorDirection::After)
    .unique();

save.wait()?;
save.expect()?;
save.click()?;
```

```sh
tui-test find text "Add to cart" --fg green
tui-test expect text "Add to cart" --fg green --timeout 5000
tui-test expect text "Add to cart" --match unique --timeout 5000
tui-test click text "Add to cart" --fg green --timeout 5000
tui-test highlight text "Add to cart" --fg green
```

## Test plan

- `cargo fmt --all -- --check`
- `cargo clippy --workspace --all-targets --all-features -- -D warnings`
- `cargo clippy -p tui-test-rs --all-targets --no-default-features -- -D
warnings`
- `cargo build`
- `cargo test --workspace -- --test-threads=1`
- `python -m unittest discover -s bindings/python/tests -v`
- `python bindings/python/scripts/generate_stubs.py --check`
- `npm run test:node --prefix bindings/js`

Replaces #150, #151, #152, and #154.

---------

Signed-off-by: cpendery <cpendery@vt.edu>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant