# Implementation Details of getByRole, getByText, and getByLabel in ego-lite

> Discover ego-lite's getByRole, getByText, and getByLabel implementation. Learn how it uses CDP queries and accessibility tree resolution for efficient element selection.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: internals
- Published: 2026-08-07

---

**ego-lite implements Playwright-style locator methods through a dual-layer façade system that converts high-level calls into Chrome DevTools Protocol (CDP) queries using internal `loc=` selector protocols and accessibility tree resolution.**

The `citrolabs/ego-lite` repository provides a browser automation library that mirrors Playwright's ergonomic locator API while leveraging Chrome DevTools Protocol for element resolution. Understanding how `getByRole`, `getByText`, and `getByLabel` translate high-level JavaScript calls into low-level browser commands reveals the architectural patterns that enable reliable element detection across dynamic web applications.

## High-Level Page Façade Implementation

The **page façade** is defined in [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts) (lines 684-735) via the `createPageFacade` function. This façade exposes the primary locator methods directly on the page object, converting human-readable arguments into internal selector strings.

### getByRole and Role-Based Selection

The `getByRole` method utilizes the **role selector** function (`roleSelector`) to construct strings following the protocol `loc=role:${role}${name}`. When the optional `options.name` parameter is provided, the implementation appends the name filter to the selector string. This format instructs the runtime to query the browser's **accessibility (AX) tree** rather than performing DOM traversal, ensuring semantic element resolution that respects ARIA roles.

### getByText, getByLabel, and Text-Based Selection

Methods including `getByText`, `getByLabel`, `getByPlaceholder`, `getByAltText`, and `getByTitle` share a common implementation through the **text selector** helper (`textSelector`) in [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts) (lines 736-756). These generate selectors using the pattern `loc=${prefix}:${value}`, where `prefix` corresponds to the search type (`"text"`, `"label"`, etc.). When `options.exact` is set to `true`, the value is prefixed with `exact:` to enforce literal string matching during resolution.

## Low-Level Locator Façade and Chaining

For **selector chaining** scenarios such as `page.locator('div').getByRole('button')`, ego-lite provides the `createLocator` function (lines 533-560 in [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts)). This **locator façade** returns an object exposing the same getter methods as the page façade, but with additional context about the current selector scope.

The `scopedSelector` function combines the existing selector with the new one, producing an **internal scope selector** formatted as `internal:scope:{base, child}`. This indirection allows the runtime to track hierarchical relationships between selectors while maintaining a consistent API surface across both simple and complex traversal operations.

## Resolution Mechanics and CDP Integration

When the runtime encounters a role-based selector (containing `loc=role:`), it delegates resolution to `queryRoleBackendNodeIds` in [`package/ego-browser/src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/element-resolver.ts) (lines 17-30). This function forwards the request to the CDP method `queryRoleLocatorBackendNodeIds`, which queries the browser's accessibility tree to return matching backend node IDs.

For text-based selectors, the system invokes `buildQueryAllExpression` to construct a DOM query expression that matches the specified text, label, or attribute content. This dual-path resolution strategy ensures that semantic queries leverage the accessibility tree while content queries execute efficient DOM searches.

## Internal Selector Protocol

All ego-lite locator methods eventually produce strings utilizing the **`loc=` protocol** for element identification. Role-based selectors use the `loc=role:` namespace, while text-based selectors use type-specific prefixes like `loc=text:` or `loc=label:`. Chained selectors utilize the `internal:scope` format to encode parent-child relationships.

This protocol abstraction enables **auto-waiting semantics** and **retry-on-transient-error** logic by providing a uniform interface that the runtime can parse and execute against the CDP layer, regardless of the original high-level method invoked.

## Practical Usage Examples

```javascript
// Locate a button by its accessible role and name
await page.getByRole('button', { name: 'Submit' }).click();

// Find a paragraph that contains exact text
await page.getByText('Welcome to ego-lite', { exact: true }).innerText();

// Locate an input by its associated <label> text
await page.getByLabel('Email').fill('user@example.com');

// Chain selectors: start from a container, then narrow by role
await page.locator('section#login')
          .getByRole('textbox', { name: 'Username' })
          .fill('myUser');

```

## Summary

- **Dual-layer architecture**: `createPageFacade` provides page-level methods while `createLocator` enables chained selector composition through `scopedSelector`.
- **Protocol-based selectors**: Internal `loc=` strings (`loc=role:`, `loc=text:`, `loc=label:`) decouple API methods from CDP implementation details.
- **Accessibility tree integration**: Role-based lookups use `queryRoleBackendNodeIds` and `queryRoleLocatorBackendNodeIds` for semantic element resolution.
- **Text matching flexibility**: The `textSelector` helper supports exact matching via the `exact:` prefix when `options.exact` is enabled.

## Frequently Asked Questions

### How does selector chaining work in ego-lite?

Selector chaining relies on the `createLocator` façade and the `scopedSelector` function to combine parent and child selectors into an `internal:scope:{base, child}` format. This allows expressions like `page.locator('div').getByRole('button')` to resolve correctly by maintaining the hierarchical relationship between the container element and the target role.

### What CDP method does ego-lite use for role-based lookups?

The library uses `queryRoleLocatorBackendNodeIds` via the accessibility tree to resolve selectors containing `loc=role:`. This function is called from `queryRoleBackendNodeIds` in [`package/ego-browser/src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/element-resolver.ts), ensuring that role queries respect ARIA semantics and browser accessibility implementations rather than relying solely on DOM attributes.

### How is exact text matching implemented in getByText?

When the `exact` option is set to `true` in `getByText` or similar methods, the `textSelector` helper prefixes the selector value with `exact:` (e.g., `loc=text:exact:Welcome`). This prefix is later interpreted by the query builder to enforce literal string matching rather than substring containment during element resolution.

### Where are the locator methods defined in the source code?

The methods are defined in [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts), specifically within `createPageFacade` (lines 684-735) for page-level access and `createLocator` (lines 533-560) for chained locator instances. Resolution logic resides in [`package/ego-browser/src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/element-resolver.ts), while low-level element operations are handled in [`package/ego-browser/src/driver/locator.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/driver/locator.ts).