How to Use getByRole, getByText, and getByLabel in ego-lite Locators

Ego-lite exposes Playwright-style accessibility helpers through its locator() façade, enabling you to target elements by ARIA role, visible text, or associated labels using strictly-typed, auto-waiting selectors.

The citrolabs/ego-lite library provides a lightweight runtime that mimics the Playwright API for browser automation. When working with ego-lite locators, you interact with a page object that returns a strict locator instance—one that automatically waits for elements and supports chaining accessibility-focused queries without requiring external browser drivers.

How ego-lite Locators Expose Accessibility Helpers

Every page object in ego-lite provides a locator() method that returns a chainable locator instance. According to the source in src/helpers.ts, this object is generated by createLocator() and exposes the three core accessibility methods: getByRole(), getByText(), and getByLabel(). Each helper constructs a scoped selector string (prefixed with loc=) that the runtime resolves in the browser context.

The selector strings follow a strict grammar defined in src/helpers.ts (lines 534–544):

  • loc=role:button[name="Submit"]
  • loc=text:"Exact text"
  • loc=label:"Email"

These strings are parsed by the query engine in src/locator-query.ts and evaluated against the DOM or Accessibility tree.

Querying Elements by ARIA Role with getByRole

The getByRole(role, options?) helper locates elements that expose a specific ARIA role—such as button, link, or textbox—and optionally filters by accessible name.

Implementation in the Source Code

In src/helpers.ts, the method forwards to roleSelector(), which constructs the loc=role: selector. The heavy lifting occurs in src/locator-query.ts (lines 345–401), where a regex ^role:([A-Za-z0-9_-]+)(?:\[name=(.+)\])?$ extracts the role and name parameters. The engine then invokes roleElementsExpression() to build a DOM query that first checks an element’s explicit role attribute, falling back to implicit ARIA roles derived from the element type (e.g., <button> implicitly has role button).

// Click a submit button by its ARIA role and accessible name
await page.locator('body')
          .getByRole('button', { name: 'Submit' })
          .click();

// Scope to a specific section before querying
await page.locator('section#settings')
          .getByRole('checkbox', { name: 'Enable notifications' })
          .check();

Finding Visible Text with getByText

Use getByText(text, options?) to target elements whose visible or inner text matches a supplied string. By default, the matching is partial (exact: false).

Text Matching Pipeline

Per the implementation in src/helpers.ts, this helper calls textSelector("text", …), which generates a loc=text: query. The runtime resolves this via the generic text matcher in src/locator-query.ts. When exact: true is passed, the selector enforces a literal match; otherwise, it accepts substrings.

// Partial match (default behavior)
const saveBtn = page.locator('body').getByText('Save');
await expect(saveBtn).toBeVisible();

// Exact match
await page.locator('main')
          .getByText('Delete', { exact: true })
          .click();

Targeting Form Controls via getByLabel

The getByLabel(text, options?) method targets form controls—such as inputs and selects—by their associated <label> element or aria-label attribute.

Label Resolution Logic

This helper is implemented via textSelector("label", …) in src/helpers.ts, producing a loc=label: prefixed query. The resolution pipeline (referenced in src/element-resolver.ts, lines 251–317) maps this prefix to the text-matching engine but restricts the search to elements with label associations or explicit aria-label properties.

// Fill an input field targeted by its label text
await page.locator('#login')
          .getByLabel('Email')
          .fill('alice@example.com');

Selector Composition and Execution Flow

Understanding how ego-lite compiles these helpers into executable queries clarifies their behavior:

  1. Façade Creation: Calling page.locator(selector) returns a locator object defined in src/helpers.ts.
  2. Scope Chaining: Each helper invokes scopedSelector() to append the accessibility constraint to the base selector.
  3. Parsing: src/locator-query.ts strips the loc= prefix and parses role or text parameters using dedicated regexes.
  4. Execution: The generated expression—such as the role-matching logic in roleElementsExpression()—evaluates in the browser context, returning backend node IDs that the runtime wraps into a fresh locator for further actions like click() or fill().

Because these helpers are pure JavaScript functions within the ego-lite runtime, they require no additional configuration or Playwright installation; simply import the page object from the global ego runtime and begin chaining.

Summary

  • ** ego-lite locators** provide Playwright-compatible accessibility methods through a strict, auto-waiting API.
  • getByRole constructs loc=role: selectors parsed by src/locator-query.ts to match explicit or implicit ARIA roles.
  • getByText leverages textSelector("text", …) in src/helpers.ts to perform partial or exact visible-text matching.
  • getByLabel uses textSelector("label", …) to resolve form controls by their label text or aria-label.
  • All three helpers support chained scoping and return fresh locator instances for composable automation scripts.

Frequently Asked Questions

Does ego-lite require Playwright to be installed?

No. While the API mirrors Playwright's locator signatures—defined in src/format.ts (lines 101–120) for documentation parity—ego-lite operates as a standalone runtime. The methods execute within the library's own browser context without external dependencies.

How does getByRole handle implicit versus explicit ARIA roles?

The roleElementsExpression() function in src/locator-query.ts (lines 345–401) first inspects an element's explicit role attribute. If absent, it falls back to the implicit role derived from the HTML tag (for example, <button> maps to the button role), ensuring comprehensive accessibility coverage.

Can I chain multiple ego-lite locators together?

Yes. Every call to getByRole, getByText, or getByLabel returns a new locator instance scoped to the previous selection. This allows you to narrow searches progressively, such as page.locator('nav').getByRole('link', { name: 'Home' }), which restricts the query to links within the navigation landmark only.

What is the difference between exact and partial matching in getByText?

By default, getByText performs partial matching (exact: false), accepting any element containing the substring. Passing { exact: true } constrains the selector to elements whose text content matches the string literally. This behavior is encoded in the textSelector() utility in src/helpers.ts and reflected in the generated loc=text: query string.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →