Automated Browser Testing Without Selectors: Is It Possible?
Yes, and the useful question is what replaces the selector. Three mechanisms exist, each with a distinct failure mode, and only one removes the identifier dependency entirely.
Yuvan Sundrani · 18 min read
autosana.ai

TL:DR
- A selector performs one job: it converts a human description of a control into something a machine resolves on the page, and anything replacing it must perform that same conversion.
- Three mechanisms exist, being accessibility semantics, visual recognition of the rendered page, and runtime intent resolution by an agent.
- Accessibility-based approaches still rely on an identifier someone authored, so "selector-free" describes where the identifier lives rather than whether one exists.
- Only intent resolution removes the dependency, and the exchange is determinism, since an agent decides at runtime where a locator merely resolves.
- Autosana resolves targets from on-screen context at run time, so renames and refactors leave flows intact, while verification matters precisely because the agent decides.
What job does a selector actually perform?
Worth establishing before discussing replacements, because the replacements get judged against this job.
A selector converts a human intention into a machine-resolvable reference. The engineer thinks "the submit button on the checkout form" and writes #checkout-submit or //button[@data-test='submit']. At runtime the browser resolves that string against the document and returns an element.
Three properties make it work. The string is stable across runs, unique on the page, and authored in advance by someone who inspected the markup. Discussions of XPath alternatives testing usually begin here, since XPath is simply the most brittle expression of that third property.
Every weakness traces to the third property. The string gets authored against the markup as it existed on one day, so any change to the markup invalidates it, and markup changes constantly for reasons unrelated to behavior. Renaming, component extraction, a design system rollout, and a framework migration.
A tester on r/QualityAssurance described the familiar consequence, where the suite reports failures the morning after a frontend merge that touched nothing a user would notice, and the team spends the day reconciling strings rather than investigating behavior.
So the question "can we test without selectors" really asks whether the conversion can happen through something other than a pre-authored string.
What are the three mechanisms that replace a selector?
| Mechanism | How It Resolves a Target | Authored in Advance? | Fails When |
|---|---|---|---|
| Accessibility semantics | Role, accessible name, state from the a11y tree | Yes, the name and role | The name changes or semantics are absent |
| Visual recognition | Matching rendered pixels or layout against a reference | Partly, a reference image or region | Styling shifts, resolution changes, theme switches |
| Intent resolution | An agent reads the screen and decides at runtime | No, only the goal | The goal becomes ambiguous or the agent misjudges |
The column that settles the evaluation is the third one. Two of the three still need something authored, so they relocate the dependency into a different artefact rather than eliminating it.
Accessibility semantics
The most widely recommended replacement, and the strongest of the two identifier-based options.
Rather than #checkout-submit, the test asks for a control with role button and accessible name Place order. Testing libraries in the React and Playwright ecosystems promote this style precisely because it binds to something users perceive.
Two genuine advantages follow. The binding survives structural refactors, since moving a button into a new component leaves its role and name untouched. And it creates pressure toward real accessibility, because a test asking for an accessible name fails loudly where that name is absent, which is the rare case where the maintainable choice is also the ethical one.
The limit is the one people skip. The accessible name is still an authored string. Where the product team rewrites "Place order" to "Complete purchase", the test breaks exactly as a selector would. The binding moved from markup structure to user-facing copy, and user-facing copy changes for conversion reasons on a schedule nobody controls.
It also depends on semantics existing. A control built from a div with a click handler and no role contributes nothing to the accessibility tree, so the mechanism has nothing to resolve against.
Visual recognition
Matching what the page renders rather than what the document declares.
Its strength is reaching things the document hides, including canvas-rendered interfaces, embedded media, and frameworks that paint rather than compose DOM nodes. Where the accessibility tree is thin, pixels remain available.
Its weakness is sensitivity to presentation. A theme switch, a font substitution, a different viewport, a changed resolution, or ordinary visual-design work all alter the reference without altering behaviour, which produces failures that look like defects and resolve as styling.
A practitioner on r/softwaretesting made the broader point that coverage for any annotation-dependent scheme tracks whoever last had time rather than which flows matter, and reference images behave similarly, since they decay silently while the suite continues reporting green.
Intent resolution
The third mechanism takes a different position entirely. Rather than resolving a reference, the test states a goal and an agent decides which interaction advances it, reading the screen and its context at runtime.
Nothing gets authored in advance beyond the goal, which is why this is the only one of the three that genuinely removes the identifier dependency. A rename, a component extraction, a design-system migration and a copy rewrite all leave the goal intact, because the goal describes what a user accomplishes.
The exchange is determinism. A locator resolves or fails, which is binary and repeatable. An agent decides, and decisions vary. Ambiguous interfaces, similar-looking controls and thin context all permit a wrong path, which introduces a failure mode the other two mechanisms lack.
What changes about failure modes?
This is the practical heart of the question, since teams adopt a mechanism and then live with its failures.
Selector-based failures are loud and cheap to diagnose. The element went missing, the message names it, and the repair is mechanical. The cost sits in volume rather than in difficulty.
Accessibility-based failures are loud and slightly more informative, since a missing accessible name points at a real accessibility gap alongside the test failure. The repair is still mechanical.
Visual failures are loud and expensive to triage, because the difference between a defect and a rendering change requires a human to look, and the looking happens for every failure rather than for the interesting ones.
Intent-resolution failures are quieter and need verification. An agent that chooses a wrong path may still reach a screen that superficially satisfies the goal, which argues for a closed-loop check inspecting application state and network activity rather than trusting the visible outcome alone. That verification layer is the cost of removing the identifier.
A developer on r/QualityAssurance framed the general evaluation question usefully, observing that comparing tools productively requires first agreeing which layer each one is competent at, and the same applies to these three mechanisms since they answer different questions about resolution.
One checkout button, four changes, three mechanisms
Abstraction hides the differences, so trace a single control through four ordinary frontend changes.
The control begins as <button id="checkout-submit" class="btn-primary">Place order</button>.
Change one, a class rename from btn-primary to btn-cta, during a design-system rollout. A CSS-class selector breaks. An id selector survives. Accessibility semantics survive, since role and name stayed constant. Visual recognition survives where the styling stayed similar, and breaks where the new class changed appearance. Intent resolution survives.
Change two, component extraction, moving the button into a shared <CheckoutActions> component that alters its position in the tree. An XPath referencing a path breaks immediately. An id selector survives where the id travelled with the component. Accessibility semantics survive. Visual recognition survives unless layout shifted. Intent resolution survives.
Change three, a copy rewrite from "Place order" to "Complete purchase," shipped by the growth team for conversion reasons. Selectors survive, since neither id nor class referenced the text. Accessibility semantics broke, because the accessible name was the binding. Visual recognition breaks where the reference captured the label. Intent resolution survives, since the goal concerned placing an order rather than clicking a word.
Change four, a framework migration rendering the control to canvas rather than to DOM nodes. Selectors break entirely, having nothing to resolve. Accessibility semantics break unless the new implementation declares them deliberately. Visual recognition survives, since pixels remain. Intent resolution survives.
| Change | Selector | A11y Semantics | Visual | Intent |
|---|---|---|---|---|
| Class rename | Breaks | Survives | Mostly survives | Survives |
| Component extraction | XPath breaks | Survives | Mostly survives | Survives |
| Copy rewrite | Survives | Breaks | Breaks | Survives |
| Canvas migration | Breaks | Breaks | Survives | Survives |
Row three is the one teams rarely anticipate, since accessibility-based testing gets recommended as the durable choice and it genuinely is durable against structural change while staying fragile against copy change. Growth teams rewrite button labels frequently, so that fragility arrives more often than the structural kind it protects against.
How should a team adopt a selector-free approach?
Incrementally, and the sequence mirrors any binding change.
Start where repair volume is highest. Pull the tests repaired most often last quarter and establish what each binds to. Those tests carry the strongest case and produce the clearest before-and-after comparison.
Run both for a fortnight. The existing test and its selector-free replacement execute in parallel, and agreement across that window establishes trust on evidence rather than on argument. Disagreement is equally informative, since it usually reveals that the original was asserting something narrower than anyone remembered.
Keep the deterministic layer beneath. Component tests continue naming elements, because the implementation is their subject. Adopting selector-free at the end-to-end layer leaves that layer untouched, which also limits the blast radius of the change.
Measure repair events rather than pass rate. Pass rate will look similar across both approaches, since both pass when the product works. The difference appears in how often someone edits a test while product behaviour stayed constant, and that figure is the one justifying the transition.
A QA engineer on r/QualityAssurance observed that coverage decisions in complex systems tend to follow whatever was built most recently rather than whatever carries the most risk, and binding decisions drift the same way, with each test adopting whatever convention its author preferred that sprint. An adoption programme is partly an opportunity to make that convention explicit.
One scheduling note. Run the comparison during ordinary feature work rather than during a redesign, since a redesign breaks the selector-bound originals across the board and removes the comparison at the moment it would be most useful.
Where do selectors remain the right answer?
Three cases, stated plainly, because selector-free arguments oversell routinely.
Component and unit testing. A test asserting that a specific component renders a specific element should name that element, since the subject of the test is the implementation. Intent resolution at this layer would obscure the thing being verified.
High-volume deterministic interaction. Thousands of predictable steps inside a tight window against one runtime run faster and more repeatably through a resolved locator than through runtime decisions.
Strict compliance assertions. Where an auditor needs proof that a specific control carried a specific label at a specific time, a deterministic binary resolution produces better evidence than a judgement.
The honest position is that selector-free belongs at the end-to-end layer, where the test is a claim about the product, and selectors belong beneath it, where tests are claims about implementation.
How do you evaluate a selector-free claim?
Three questions separate the categories quickly.
What happens when a button is renamed? Where the vendor's answer involves updating anything, the mechanism is identifier-based, which is acceptable and worth knowing.
What happens when there is no accessible name and no test identifier? This separates accessibility-based tooling from visual and intent-based tooling directly, since the first has nothing to resolve against.
How does the tool know it succeeded? A mechanism that decides needs a verification story beyond the visible screen. Where that story is absent, the tool will report success on paths that reached a plausible-looking wrong place.
A practitioner on r/rpa described the adjacent dynamic in process automation, where the people who understand the process and the people who can automate it are usually different, and most of the value sits in closing that gap. Selector-free approaches widen authorship for the same reason, which is a genuine benefit worth separating from the maintenance argument.
A fourth question deserves asking for completeness. What does a failure look like? Request a recorded failure from the vendor rather than a recorded success, since the triage experience determines the running cost far more than the authoring experience does.
How does Autosana approach this?
Autosana is an AI-native E2E testing agent for iOS, Android and web, operating through the third mechanism.
Flows state intent, and the agent resolves targets at run time from on-screen context, combining what the page renders with its semantic structure rather than depending on either alone. That combination matters, since accessibility-only resolution breaks where semantics are absent while pixel-only resolution breaks where styling shifts, and reading both gives the agent more to reason from.
Because nothing gets authored in advance beyond the goal, renames, component extraction, design-system migrations and copy rewrites leave flows intact. The maintenance category that dominates mature web suites stops arriving.
Results return through the GitHub integration with session replay attached, which matters more for this mechanism than for the others, since a decision-making system needs its decisions reviewable. Runs execute on hosted real devices for mobile surfaces, and flows are invocable over MCP. The documentation covers flow authoring and execution.
The limits apply as stated above. Per-interaction overhead exceeds a resolved locator, the agent can misjudge an ambiguous interface, verification matters precisely because decisions vary, and component-level testing belongs beneath this layer in code that names elements deliberately.
Where to start
Take your three most-repaired web tests and establish what each one actually binds to. Most teams find a mixture, with some tests naming CSS classes, some naming test identifiers and some naming visible copy, which explains why repair events arrive from unrelated directions.
Then run the rename test on whichever tool you are evaluating. Change a button label in a staging build and observe what breaks. That single experiment distinguishes the three mechanisms faster than any feature comparison, and it takes an afternoon.
FAQ
Can you really automate browser testing without selectors?
Yes, through three mechanisms: accessibility semantics, visual recognition of the rendered page, and runtime intent resolution. Only the third removes the dependency on something authored in advance, while the first two relocate it.
Is testing by accessible name the same as selector-free?
No, because the accessible name is still an authored string. The binding moves from markup structure to user-facing copy, which changes for conversion reasons, so a label rewrite breaks the test exactly as a selector would.
What breaks a visual test automation approach?
Presentation changes rather than behaviour changes. Theme switches, font substitutions, viewport differences, resolution changes and ordinary design work all alter the reference, producing failures that look like defects and resolve as styling.
Does intent-based testing remove maintenance entirely?
It removes identifier maintenance, which dominates mature suites. Deciding what deserves testing, supplying representative data and verifying that a result is genuinely correct remain, and verification matters more because the agent decides rather than resolves.
When should you keep using CSS selectors or XPath?
For component and unit tests where the implementation is the subject, for high-volume deterministic interaction inside tight windows, and for compliance assertions needing binary repeatable evidence rather than a judgement.
How do you tell whether a tool is genuinely selector-free?
Ask what happens when a button gets renamed, what happens when no accessible name and no test identifier exist, and how the tool establishes that it succeeded. The three answers separate the mechanisms cleanly.
Why does intent-based testing need verification?
Because a system that decides can reach a screen that superficially satisfies the goal through a wrong path. A closed-loop check inspecting application state and network activity catches what the visible outcome conceals.
Does Autosana use accessibility semantics or visual recognition?
Both, combined. Accessibility-only resolution breaks where semantics are absent and pixel-only resolution breaks where styling shifts, so reading the rendered page alongside its semantic structure gives the agent more to reason from.
.png)