Skip to content

feat: Add withLabel and withAriaLabel filters for form field selection#71

Open
mstahv wants to merge 10 commits into
mainfrom
feat/withLabel
Open

feat: Add withLabel and withAriaLabel filters for form field selection#71
mstahv wants to merge 10 commits into
mainfrom
feat/withLabel

Conversation

@mstahv
Copy link
Copy Markdown
Member

@mstahv mstahv commented May 21, 2026

Description

Addes a more specific and especially more discoverable API to search by label. Additionally, supports separate label elements/components.

Fixes #42

Type of change

  • Bugfix
  • Feature

mstahv and others added 3 commits May 21, 2026 11:27
Common Playwright pattern — selecting form fields by their visible label
or buttons by their assistive-tech label — wasn't expressible directly
in the locator/query API. withCaption() exists but is intentionally
broader (it matches label, text, or placeholder depending on component)
and that ambiguity can surprise users.

Adds four new filters at both the ComponentQuery and Locator layers:

  - withLabel(text)            — exact match on the "label" property
  - withLabelContaining(text)  — substring match on the "label" property
  - withAriaLabel(text)        — exact match on the "aria-label" attribute
  - withAriaLabelContaining(text) — substring match on "aria-label"

Backed by new predicates in ElementConditions (hasLabel, labelContains,
hasAriaLabel, ariaLabelContains) so the same checks are reusable from
withCondition. Unit-tested at the query layer and integration-tested at
the locator layer; LocatorDemoView grows an aria-label on Clear and the
inner PersonForm uses distinct labels so withLabel matches resolve to
single elements.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When a component is labelled by an external <label for="componentId">
element (the HTML pattern, exposed in Flow via NativeLabel.setFor), its
"label" property is empty — so the previous withLabel implementation
missed it. Now also walks the UI for a <label> element whose "for"
attribute matches the component's id, and reads the label's text
content.

Adds a reusable ElementTreeWalker static helper for depth-first Element
traversal — kind of a Flow-level querySelectorAll. ElementConditions
uses it for the new fallback path; other future searches that need to
look at the raw element tree (e.g. by tag or attribute) can reuse it.

ComponentQueryTest covers both exact and substring forms across the
direct-property and for-attribute paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mstahv mstahv requested a review from mcollovati May 21, 2026 12:23
Comment thread shared/src/main/java/com/vaadin/browserless/ElementConditions.java Outdated
mstahv and others added 6 commits May 21, 2026 20:26
Verifies that the <label for="..."> resolution path also fires when the
NativeLabel and its targeted TextField live inside an opened Dialog —
i.e. the walker reaches into the dialog overlay content and finds the
referring label even though it's not a direct child of the UI element.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MasterDetailLayout attaches its detail content via Element.appendVirtualChild
(same mechanism several Vaadin overlays use). The previous walk()
implementation only traversed regular children, so a <label for="id">
inside such virtual content was invisible to the withLabel resolver.

walk() now interleaves virtual children with regular ones, matching the
behavior of the existing Kotlin Element.getVirtualChildren() helper.

Also tightens referringLabelText to skip text-node elements before
calling getTag() — text nodes throw UnsupportedOperationException on
that call, which masked failures before this fix.

Regression test (ComponentQueryTest#withLabel_componentInsideMasterDetailLayout_isFound)
fails on the old walker and passes after the change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mstahv mstahv marked this pull request as ready for review May 22, 2026 05:57
Comment thread junit6/src/test/java/com/vaadin/browserless/ComponentQueryTest.java Outdated
Comment thread junit6/src/test/java/com/vaadin/browserless/ComponentQueryTest.java
Comment thread junit6/src/test/java/com/vaadin/browserless/ComponentQueryTest.java Outdated
Comment thread junit6/src/test/java/com/vaadin/browserless/ComponentQueryTest.java
Comment thread junit6/src/test/java/com/vaadin/browserless/ComponentQueryTest.java Outdated
Comment thread shared/src/main/java/com/vaadin/browserless/ComponentQuery.java
Comment thread shared/src/main/java/com/vaadin/browserless/ComponentQuery.java
Comment thread shared/src/main/java/com/vaadin/browserless/ComponentQuery.java
Comment thread shared/src/main/java/com/vaadin/browserless/ComponentQuery.java
Comment thread shared/src/main/java/com/vaadin/browserless/ElementConditions.java Outdated
Comment thread shared/src/main/java/com/vaadin/browserless/ElementConditions.java Outdated
Comment thread shared/src/main/java/com/vaadin/browserless/ElementConditions.java Outdated
Comment thread shared/src/main/java/com/vaadin/browserless/ElementConditions.java Outdated
ElementConditions: hasLabel, labelContains, hasAriaLabel and
ariaLabelContains now throw IllegalArgumentException on null inputs,
consistent with the existing containsText / hasAttribute style.

ComponentQueryTest:
- Drop the temporary "why don't we set id implicitly" TODO comment;
  if it becomes a real concern, it'll get a Flow ticket instead of
  living as inline noise.
- Expand withLabelContaining_substringMatch with both a non-matching
  label-property component and a non-matching <label for=...>
  component, so the assertion proves filtering rather than just
  matching.
- Expand withAriaLabelContaining_substringMatch with a non-matching
  aria-label component for the same reason.
- Use imports for Dialog and MasterDetailLayout instead of
  fully-qualified names.
- withLabel_null_throws / withAriaLabel_null_throws now assert
  IllegalArgumentException to match the new contract.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

Add support to select by label

2 participants