agentleFS
anhtesteranhtester/codex-testing-kit/.agents/skills/generate-locator/SKILL.md

generate-locator

Skill28 starsChanged 4 months agoMITView on GitHub

Sinh locator ổn định cho UI element. Hỗ trợ Playwright, Selenium, Appium.

  • Reads credentials

The file

Files
SKILL.md

$generate-locator — Sinh Locator Ổn Định Cho UI Automation

User cung cấp element cần tìm locator (mô tả, screenshot, URL, hoặc HTML snippet). AI inspect DOM/UI hierarchy thực tế, sinh locator ổn định theo priority chuẩn, verify uniqueness, trả về kết quả.

BẮT BUỘC (MANDATORY): Trước khi bắt đầu, PHẢI nạp và đọc kỹ:

  • Skill: .agents/skills/smart-locator-agent/SKILL.md — Quy trình sinh locator
  • Skill: .agents/skills/ui-debug-agent/SKILL.md — Quy trình inspect DOM
  • Rule: .agents/rules/locator_strategy.md — Bản đồ ưu tiên locator
  • Rule: .agents/rules/<framework>_rules.md — Quy tắc riêng framework đang dùng

Input cần từ User

InputBắt buộcMô tả
Mô tả element✅VD: "nút Login", "dropdown chọn Country", "ô nhập Email"
URL trang chứa element✅Để AI navigate và inspect DOM thực tế
Framework✅playwright, selenium, hoặc appium
HTML snippet❌Nếu User đã có sẵn DOM context — AI dùng để phân tích nhanh
Page class đích❌File Page class mà locator sẽ được thêm vào
Login yêu cầu❌Nếu trang yêu cầu đăng nhập — User cho biết cách login

Lưu ý về Login: Nếu trang yêu cầu đăng nhập, User PHẢI chỉ rõ cách login (fixture, script, URL login...). AI KHÔNG ĐƯỢC tự đọc .env hay đoán credentials.


Các bước thực hiện

Phase 1: Phân tích yêu cầu

  1. Hiểu element cần tìm — Xác định rõ:
    • Loại element: button, input, link, dropdown, dialog, table row, checkbox, radio...
    • Context: Nằm trong page chính, dialog/modal, sidebar, table, iframe?
    • Thao tác: click, fill, select, hover, verify text, verify visibility?
  2. Xác định framework và đọc rules tương ứng:

    FrameworkRule file
    Playwright.agents/rules/playwright_rules.md
    Selenium.agents/rules/selenium_rules.md
    Appium.agents/rules/appium_rules.md
  3. Kiểm tra Page class hiện tại (nếu User chỉ định):
    • Đọc file Page class → biết locator đã có sẵn
    • Tránh trùng lặp hoặc xung đột naming

Phase 2: Inspect DOM / UI Hierarchy thực tế

⚠️ NGUYÊN TẮC BẤT DI BẤT DỊCH: KHÔNG BAO GIỜ ĐOÁN LOCATOR. PHẢI INSPECT THỰC TẾ.

2A. Web (Playwright MCP)

  1. Navigate đến trang chứa element:
    browser_navigate(url=<target_url>)
  2. Resize viewport (BẮT BUỘC):
    browser_resize(width=1920, height=1080)
  3. Capture DOM:
    browser_snapshot()
  4. Phân tích element trong snapshot:
    • Tìm ref element trong DOM tree
    • Ghi lại tất cả attributes có giá trị: role, aria-label, aria-labelledby, data-testid, data-test, data-qa, id, name, placeholder, type, href, text content
    • Ghi lại parent context (dialog? table? sidebar? iframe?)
  5. Nếu element bị ẩn (dropdown menu, modal, tooltip...):
    • Thực hiện action mở element: browser_click(ref=<trigger>)
    • Capture lại: browser_snapshot()

2B. Mobile (Appium)

  1. Dùng Appium Inspector hoặc page_source để lấy UI hierarchy
  2. Ghi lại attributes: accessibility-id, resource-id, content-desc, text, class, bounds
  3. Nếu element nằm trong scroll view → scroll đến element trước khi inspect

Phase 3: Sinh locator theo Priority

  1. Áp dụng bản đồ ưu tiên (Master Priority Map):

    #LoạiKhi nào dùng
    1Accessibility / AriaElement có role, aria-label rõ ràng
    2Test attributeElement có data-testid, data-test, data-qa
    3ID / nameElement có id hoặc name ổn định (không auto-generated)
    4Framework semanticPlaywright: getByRole, getByLabel...
    5CSS SelectorDùng attribute cụ thể, tránh class động
    6XPathLựa chọn cuối cùng — chỉ dùng XPath tương đối
  2. Sinh locator theo framework:

    Playwright (TypeScript/JavaScript):

    // Priority 1: Role-based
    page.getByRole('button', { name: 'Submit' })
    
    // Priority 2: Test ID
    page.getByTestId('submit-btn')
    
    // Priority 3: Label / Placeholder
    page.getByLabel('Email')
    page.getByPlaceholder('Enter your password')
    
    // Priority 4: Text
    page.getByText('Submit')
    
    // Priority 5: CSS
    page.locator('#submit-button')
    page.locator('[data-testid="submit-btn"]')
    
    // Priority 6: XPath (cuối cùng)
    page.locator('//button[@type="submit"]')

    Playwright (Python):

    # Priority 1: Role-based
    page.get_by_role("button", name="Submit")
    
    # Priority 2: Test ID
    page.get_by_test_id("submit-btn")
    
    # Priority 3: Label / Placeholder
    page.get_by_label("Email")
    page.get_by_placeholder("Enter your password")
    
    # Priority 4: Text
    page.get_by_text("Submit")
    
    # Priority 5: CSS
    page.locator("#submit-button")
    
    # Priority 6: XPath (cuối cùng)
    page.locator("//button[@type='submit']")

    Selenium (Java):

    // Priority 1: ID
    driver.findElement(By.id("submit-button"));
    
    // Priority 2: Test attribute
    driver.findElement(By.cssSelector("[data-testid='submit-btn']"));
    
    // Priority 3: Name
    driver.findElement(By.name("submit"));
    
    // Priority 4: CSS Selector
    driver.findElement(By.cssSelector("button.btn-primary[type='submit']"));
    
    // Priority 5: XPath (cuối cùng)
    driver.findElement(By.xpath("//button[@type='submit']"));

    Appium (Java):

    // Priority 1: Accessibility ID
    driver.findElement(AppiumBy.accessibilityId("login_button"));
    
    // Priority 2: Resource ID (Android)
    driver.findElement(AppiumBy.id("com.app:id/login_button"));
    
    // Priority 3: iOS Predicate
    driver.findElement(AppiumBy.iOSNsPredicateString("label == 'Login'"));
    
    // Priority 4: Class Chain (iOS)
    driver.findElement(AppiumBy.iOSClassChain("**/XCUIElementTypeButton[`label == 'Login'`]"));
    
    // Priority 5: XPath (cuối cùng)
    driver.findElement(AppiumBy.xpath("//android.widget.Button[@text='Login']"));

Phase 4: Validate locator

  1. Verify uniqueness — PHẢI match đúng 1 element:

    Web (Playwright MCP):

    browser_evaluate(function="() => document.querySelectorAll('<css_selector>').length")

    Selenium:

    List<WebElement> matches = driver.findElements(By.<strategy>("<locator>"));
    assert matches.size() == 1;

    Appium:

    List<WebElement> matches = driver.findElements(AppiumBy.<strategy>("<locator>"));
    assert matches.size() == 1;
  2. Verify visibility — Element phải tương tác được:
    • Không bị overlay bởi element khác
    • Không ở trạng thái hidden, display:none, visibility:hidden
    • Không nằm ngoài viewport (cần scroll)
  3. Verify stability — Kiểm tra checklist:
    • Không dùng dynamic CSS class (VD: css-1n2xyz, sc-bdnxRM)
    • Không dùng XPath tuyệt đối (VD: //html/body/div[1]/div[2]/button)
    • Không dùng auto-generated ID (VD: ember123, react-select-2-input)
    • Không dùng nth-child / nth-of-type khi có lựa chọn tốt hơn
    • Sống sót qua page reload
    • Ổn định trên nhiều trạng thái trang (loading, loaded, có data, không data)

Phase 5: Trả kết quả

  1. Output Format — BẮT BUỘC cung cấp đầy đủ 3 phần:
## Locator Result: [Mô tả element]

**Framework:** [Playwright / Selenium / Appium]

### 🎯 Primary Locator (Recommended)
```<language>
// Locator code — copy-paste ready
  • Loại: [Role-based / Test ID / CSS / ...]
  • Unique: ✅ Match 1 element
  • Stability: ✅ Không dùng dynamic class / absolute xpath

🔄 Fallback Locator

// Locator thay thế khi primary hỏng
  • Loại: [CSS / XPath / ...]
  • Khi nào dùng: Khi primary locator bị break do DOM thay đổi

💡 Reasoning

  • Giải thích tại sao chọn Primary locator này
  • Tại sao loại bỏ các candidate khác
  • Rủi ro tiềm ẩn (nếu có)

📋 Usage Example (nếu User yêu cầu)

// Ví dụ sử dụng locator trong test code

15. **(Tùy chọn) Nếu User yêu cầu thêm vào Page class:**
    - Thêm locator đúng vị trí trong Page class
    - Đặt tên theo naming convention của project
    - Tạo method sử dụng locator nếu cần

---

## Common Patterns (Tham khảo)

### Scoping locator trong Dialog / Modal:
```typescript
// Playwright — scope vào dialog trước
const dialog = page.getByRole('dialog');
dialog.getByRole('button', { name: 'Confirm' }).click();
// Selenium — scope vào dialog
WebElement dialog = driver.findElement(By.cssSelector("[role='dialog']"));
dialog.findElement(By.cssSelector("button[data-testid='confirm']")).click();

Dynamic text matching:

// Playwright — exact vs partial
page.getByText('Submit', { exact: true })     // exact match
page.getByText(/submit/i)                      // regex, case-insensitive
# Playwright Python — normalize-space XPath
page.locator(f"//a[normalize-space()='{text}']")

Table row action:

// Playwright — filter row rồi interact
const row = page.getByRole('row').filter({ hasText: 'John Doe' });
row.getByRole('button', { name: 'Edit' }).click();
// Selenium — XPath relative trong table
driver.findElement(By.xpath("//tr[contains(., 'John Doe')]//button[text()='Edit']"));

NGHIÊM CẤM

❌ Không được làm✅ Thay thế đúng
Đoán locator không inspect DOM/UIbrowser_snapshot() hoặc Appium Inspector trước
Dùng CSS class động (css-1n2xyz, sc-xxx)Dùng role, aria, data-testid, text
Dùng XPath tuyệt đối (//html/body/div[1]...)Dùng XPath tương đối với attribute
Dùng auto-generated ID (ember123, :r1:)Dùng stable attribute hoặc text
Trả locator không verify uniquenessLuôn verify match đúng 1 element
Chỉ trả 1 locator, không có fallbackTrả Primary + Fallback + Reasoning
Đọc .env để lấy credentials loginHỏi User cách login hoặc dùng fixture có sẵn

Checklist cuối

  • Đã inspect DOM/UI hierarchy thực tế (không đoán)
  • Locator theo đúng priority: accessibility > test-id > id/name > semantic > css > xpath
  • Primary locator match đúng 1 element duy nhất
  • Có Fallback locator
  • Có Reasoning giải thích lý do chọn
  • Không dùng dynamic class, absolute xpath, auto-generated ID
  • Locator ổn định qua page reload và nhiều trạng thái
  • (Nếu thêm vào Page class) Đã verify code chạy không lỗi

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.