Skip to content

DizzyMii/Resilient-Locator-Extractor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Resilient Locator Extractor

A CLI tool and Chrome extension that parses HTML, identifies target elements, and outputs ranked locators optimized for test automation stability.

Why This Tool?

Test automation often fails due to brittle selectors. This tool analyzes HTML elements and generates locators based on a resilience hierarchy—prioritizing attributes that developers intentionally add for testing over fragile structural paths.

Installation

git clone <repo-url>
cd Resilient-Locator-Extractor
npm install
npm run build

Quick Start

# From HTML string
node dist/index.js --html "<button data-testid='submit-btn'>Submit</button>" --selector "button"

# From file
node dist/index.js --file ./snippet.html --selector "button.login"

CLI Options

Option Description Required
--html Raw HTML string to parse One of --html or --file
--file Path to HTML file One of --html or --file
--selector CSS selector to identify target element Yes
--help Show usage information No

Chrome Extension

The Chrome extension provides a visual UI for extracting locators directly from any webpage.

Installation

  1. Generate PNG icons (required for Chrome):

    # Open in browser
    open extension/icons/generate-icons.html
    
    # Right-click each canvas → Save image as:
    # - icon16.png
    # - icon48.png  
    # - icon128.png
    # Save to extension/icons/
  2. Load in Chrome:

    • Open chrome://extensions/
    • Enable "Developer mode" (toggle in top-right)
    • Click "Load unpacked"
    • Select the extension/ folder

Usage

  1. Navigate to any webpage
  2. Right-click on any element
  3. Select "Get Resilient Locators" from context menu
  4. Click the extension icon to view ranked locators
  5. Click any locator to copy to clipboard

Extension Features

Feature Description
Right-click extraction Instantly analyze any element
Visual tier badges Color-coded by stability
One-click copy Copy any locator to clipboard
No setup required Works on any webpage

Extension Structure

extension/
├── manifest.json       # Extension configuration
├── background.js       # Service worker (context menu + scoring)
├── content.js          # Element extraction from pages
├── popup/              # Results display UI
│   ├── popup.html
│   ├── popup.css
│   └── popup.js
├── icons/              # Extension icons
└── lib/                # Shared scoring logic
    ├── scorer.js
    ├── types.js
    └── utils.js

Output Format

✓ Found target element: <button>

Ranked Locators:
1. [Tier 1] [data-testid="submit-btn"]
   Explicit test attribute: data-testid
2. [Tier 2] [aria-label="Submit form"]
   Accessibility attribute: aria-label
3. [Tier 3] //button[text()='Submit']
   Text content: "Submit"
4. [Tier 4] #login-btn
   Static ID: login-btn
5. [Tier 5] div > form > button:nth-child(2)
   Structural path (least stable)

Locator Tiers (Resilience Hierarchy)

Tier Priority Attribute Why It's Stable
1 Highest data-testid, data-cy, data-qa Explicitly added for automation—rarely changes
2 High aria-label, title, role Tied to accessibility compliance
3 Medium Text content Semantic text like "Submit" or "Log In"
4 Low id Static IDs only—dynamic IDs filtered out
5 Lowest Structural path Fallback: CSS path based on DOM position

Dynamic ID Detection

Tier 4 filters out IDs that appear dynamically generated:

  • IDs with 6+ consecutive digits (e.g., btn-123456789)
  • Common in React, Angular, Vue apps

Examples

Example 1: Button with Test ID

node dist/index.js --html "<button data-testid='login-btn' class='btn primary'>Login</button>" --selector "button"

Output:

✓ Found target element: <button>

Ranked Locators:
1. [Tier 1] [data-testid=login-btn]
   Explicit test attribute: data-testid
2. [Tier 3] //button[text()='Login']
   Text content: "Login"
3. [Tier 5] button.btn
   Class-based selector

Example 2: Form Input with ARIA Label

node dist/index.js --html "<input type='email' aria-label='Email address' name='email'>" --selector "input"

Output:

✓ Found target element: <input>

Ranked Locators:
1. [Tier 2] [aria-label="Email address"]
   Accessibility attribute: aria-label
2. [Tier 5] html:nth-child(1) > body:nth-child(2) > input:nth-child(1)
   Structural path (least stable)

Example 3: Dynamic ID Filtered Out

node dist/index.js --html "<button id='submit-847293847'>Click</button>" --selector "button"

Output:

✓ Found target element: <button>

Ranked Locators:
1. [Tier 3] //button[text()='Click']
   Text content: "Click"
2. [Tier 5] html:nth-child(1) > body:nth-child(2) > button:nth-child(1)
   Structural path (least stable)

Note: Tier 4 (ID selector) is skipped because submit-847293847 contains 9 consecutive digits.

Example 4: From File

Create login.html:

<!DOCTYPE html>
<html>
<body>
  <form id="login-form">
    <input type="email" aria-label="Email address" name="email">
    <input type="password" title="Password" name="password">
    <button type="submit" data-cy="login-submit" class="btn-primary">Log In</button>
  </form>
</body>
</html>

Run:

node dist/index.js --file login.html --selector "button"

Output:

✓ Found target element: <button>

Ranked Locators:
1. [Tier 1] [data-cy=login-submit]
   Explicit test attribute: data-cy
2. [Tier 3] //button[text()='Log In']
   Text content: "Log In"
3. [Tier 5] button.btn-primary
   Class-based selector
4. [Tier 5] html:nth-child(1) > body:nth-child(2) > form:nth-child(1) > button:nth-child(3)
   Structural path (least stable)

Integration with Test Frameworks

Copy the highest-tier locator into your automation code:

Playwright

// Tier 1 (recommended)
await page.locator('[data-testid=submit-btn]').click();

// Tier 3 (XPath)
await page.locator('xpath=//button[text()="Submit"]').click();

Cypress

// Tier 1
cy.get('[data-testid=submit-btn]').click();

// Tier 2
cy.get('[aria-label="Submit form"]').click();

Selenium (Python)

from selenium.webdriver.common.by import By

# Tier 1 (CSS)
driver.find_element(By.CSS_SELECTOR, '[data-testid=submit-btn]').click()

# Tier 3 (XPath)
driver.find_element(By.XPATH, '//button[text()="Submit"]').click()

Puppeteer

// Tier 1
await page.click('[data-testid=submit-btn]');

// Tier 2
await page.click('[aria-label="Submit form"]');

Project Structure

src/
├── index.ts           # CLI entry point, argument parsing
├── parser.ts          # HTML parsing with jsdom
├── scorer.ts          # Heuristic scoring engine (5 tiers)
├── locator-generator.ts # Main extraction logic
├── types.ts           # TypeScript interfaces
└── utils.ts           # Helpers (ID validation, escaping)

Scope

Supported:

  • Static HTML parsing
  • CSS selector targeting
  • Ranked locator output (CSS + XPath)
  • Dynamic ID detection

Not Supported (MVP):

  • Shadow DOM traversal
  • Cross-origin iframes
  • Client-side JavaScript rendering
  • Dynamic content loaded via AJAX

Development

# Build
npm run build

# Run locally (without build)
npx ts-node src/index.ts --html "<button>Test</button>" --selector "button"

# Run compiled
node dist/index.js --html "<button>Test</button>" --selector "button"

Tech Stack

  • Language: TypeScript
  • Runtime: Node.js
  • Virtual DOM: jsdom

License

MIT

About

CLI tool and Chrome extension for extracting ranked, resilient locators optimized for test automation stability. Generate robust selectors that survive DOM changes.

Topics

Resources

Stars

Watchers

Forks

Contributors