Implementation:MarketSquare Robotframework browser Selector Finder
| Knowledge Sources | |
|---|---|
| Domains | Browser Automation, UI Tooling |
| Last Updated | 2026-02-12 05:40 GMT |
Overview
Implements the interactive selector recorder UI that is injected into browser pages, enabling users to visually hover over elements and record optimized CSS selectors.
Description
selector-finder.js is a client-side JavaScript module injected into browser pages by the recordSelector gRPC handler. It provides a complete interactive selector recording workflow for test development. The module consists of two major parts:
Part 1: CSS Selector Generator (based on Anton Medvedev's "finder" library, MIT licensed)
The finder function generates unique, minimal CSS selectors for any DOM element. It uses a bottom-up search algorithm that:
- Traverses from the target element up to the document root
- Generates candidate selectors using ID (
#id), attributes ([attr="value"]), class names (.class), tag names, and:nth-child()pseudo-selectors - Produces multiple candidate paths at three specificity limits (All, Two, One)
- Optimizes selectors by removing unnecessary intermediate nodes
- Uses a configurable threshold (default 1000) and max tries (default 10000) to bound the search
- Includes a full
cssescimplementation for safe CSS identifier escaping
Part 2: Browser Library Selector Recorder UI
An interactive floating panel injected into the page that:
- Displays a draggable recorder window with the current selector, countdown timer, and instructions
- Tracks
mousemoveevents to resolve the hovered element into CSS selectors in real-time - Uses
elementSelectorFromPointInFrameto support shadow DOM piercing via>>syntax and cross-frame element selection - After the user hovers over an element for 3 seconds, displays Select/Cancel buttons with a flickering highlight border around the target element
- On selection, shows a dropdown panel with multiple alternative selector variants (different ID/class/attribute combinations) and "Take It", "Highlight", and "Reselect" buttons
- The "Highlight" button calls
window.highlightPWSelectorto visually verify the selector matches the expected element - Communicates with the Node.js layer via exposed functions:
window.setRecordedSelector,window.getRecordedSelectors, andwindow.highlightPWSelector window.subframeSelectorRecorderFindSelectoris exposed for cross-frame selector recording, recursively attaching listeners to child frames- Includes comprehensive CSS styling for the recorder panel, selection frame, buttons, and animations (flicker animation for element highlighting)
Usage
This module is used internally by the Record Selector keyword. It is injected into the active browser page via page.addScriptTag and requires that the browser context is launched with bypassCSP=True if the page has strict Content Security Policies. The recorder only works with visible (non-headless) browsers.
Code Reference
Source Location
- Repository: MarketSquare_Robotframework_browser
- File: node/playwright-wrapper/static/selector-finder.js
- Lines: 1-644
Signature
// CSS selector generator
export function finder(element, options) -> string
// Main-frame selector recorder (returns Promise<string>)
window.selectorRecorderFindSelector = function(label) -> Promise<string>
// Sub-frame selector recorder
window.subframeSelectorRecorderFindSelector = function(frameIndex) -> void
// Internal: resolve element at coordinates including shadow DOM
function elementSelectorFromPointInFrame(x, y) -> [selectors[], rect]
Import
// Injected via page.addScriptTag in evaluation.ts:
await frame.addScriptTag({
type: 'module',
path: path.join(__dirname, '/static/selector-finder.js'),
});
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| element | Node (ELEMENT_NODE) | Yes | The DOM element to generate a CSS selector for (finder function) |
| options.root | Element | No | Root element for selector scope (default: document.body) |
| options.idName | function | No | Filter function for ID attributes (default: () => true) |
| options.className | function | No | Filter function for class names (default: () => true) |
| options.tagName | function | No | Filter function for tag names (default: () => true) |
| options.attr | function | No | Filter function for other attributes (default: () => false) |
| options.seedMinLength | number | No | Minimum path length before checking uniqueness (default: 1) |
| options.optimizedMinLength | number | No | Minimum path length to attempt optimization (default: 2) |
| options.threshold | number | No | Maximum combinations to check (default: 1000) |
| options.maxNumberOfTries | number | No | Maximum optimization iterations (default: 10000) |
| label | string | No | Label text displayed in the recorder UI header |
Outputs
| Name | Type | Description |
|---|---|---|
| selector | string | A unique, optimized CSS selector string for the target element |
| Promise<string> | Promise | The user-selected CSS selector from the recorder UI (resolved on "Take It" click) |
Usage Examples
*** Settings ***
Library Browser
*** Test Cases ***
Record A Selector
New Browser chromium headless=False
New Context bypassCSP=True
New Page https://example.com
${selector}= Record Selector my_element
Log User selected: ${selector}
# Now use the recorded selector in tests
Click ${selector}
// Internal usage of the finder function:
import { finder } from './selector-finder.js';
// Generate a unique CSS selector for an element
const element = document.querySelector('#myElement');
const selector = finder(element, {
root: document.body,
className: (name) => !name.startsWith('dynamic-'),
threshold: 500
});
// selector might be: "#myElement" or ".container > .item:nth-child(3)"