Skip to content

Commit 4a7c53b

Browse files
committed
chore: bump version to 1.24.1; enhance locator strategy documentation and clean up markdown formatting
1 parent 21495a2 commit 4a7c53b

5 files changed

Lines changed: 57 additions & 43 deletions

File tree

.claude/skills/vasu-playwright-utils/references/locators.md

Lines changed: 46 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Source: `src/vasu-playwright-lib/utils/locator-utils.ts`
44

55
## Locator Strategy Priority
66

7-
When choosing locators for test code, follow this priority order (best to worst). Always prefer the highest-priority strategy that uniquely identifies the element.
7+
When choosing locators for test code, follow this priority order (best to worst). **Prefer unique CSS or XPath with stable attributes over text-based locators** so that when a check fails you can tell quickly whether the element is missing (bug) or the copy changed (new functionality / locale).
88

99
### 1. `data-testid` attributes (Best)
1010

@@ -23,8 +23,11 @@ Custom data attributes that carry stable, semantic meaning.
2323
// HTML: <div data-product-id="shoes-001">...</div>
2424
await click('[data-product-id="shoes-001"]');
2525

26-
// HTML: <tr data-row-type="header">...</tr>
27-
await expectElementToBeVisible('[data-row-type="header"]');
26+
// HTML: <h2 data-test="complete-header">Thank you for your order</h2>
27+
const orderCompleteMessage = () => getLocator('[data-test="complete-header"]');
28+
await expectElementToContainText(orderCompleteMessage(), /thank you for your order/i, {
29+
message: 'Checkout complete message should be displayed',
30+
});
2831
```
2932

3033
### 3. `id` attributes
@@ -51,34 +54,9 @@ await fill('[name="email"]', 'user@example.com');
5154
await selectByText('[name="country"]', 'United States');
5255
```
5356

54-
### 5. Playwright built-in locators
57+
### 5. XPath with unique attributes
5558

56-
Semantic locators that match how users perceive the page. Resilient to DOM changes.
57-
58-
```typescript
59-
// By ARIA role + accessible name (preferred for interactive elements)
60-
await click(getLocatorByRole('button', { name: 'Submit' }));
61-
await fill(getLocatorByRole('textbox', { name: 'Email' }), 'user@example.com');
62-
await click(getLocatorByRole('link', { name: 'Sign up' }));
63-
await check(getLocatorByRole('checkbox', { name: 'Remember me' }));
64-
65-
// By label text (preferred for form fields)
66-
await fill(getLocatorByLabel('Email address'), 'user@example.com');
67-
68-
// By placeholder text
69-
await fill(getLocatorByPlaceholder('Search...'), 'playwright');
70-
71-
// By visible text content
72-
await click(getLocatorByText('Add to cart'));
73-
74-
// Use regex for partial or case-insensitive matching
75-
await click(getLocatorByRole('button', { name: /submit/i }));
76-
await click(getLocatorByText(/view details/i));
77-
```
78-
79-
### 6. XPath with unique attributes
80-
81-
Use only when higher-priority strategies are unavailable. Target stable attributes.
59+
Use when no data-_ or id is available. Target **stable attributes** (e.g. `data-test`, `aria-_`, `type`), not text.
8260

8361
```typescript
8462
// Good: XPath with stable attributes
@@ -89,9 +67,9 @@ await click('//input[@type="email"]');
8967
await click('//div[@data-section="billing"]//button[@type="submit"]');
9068
```
9169

92-
### 7. CSS with unique attributes
70+
### 6. CSS with unique attributes
9371

94-
Similar to XPath — use stable attributes, not structural position.
72+
Use stable attribute selectors so the locator does not depend on copy or locale.
9573

9674
```typescript
9775
// Good: attribute-based CSS
@@ -100,6 +78,42 @@ await fill('input[type="email"]', 'user@example.com');
10078

10179
// Good: scoped by stable parent
10280
await click('.billing-section button[type="submit"]');
81+
82+
// Good: data-test (e.g. Sauce Demo checkout complete)
83+
const orderCompleteMessage = () => getLocator('[data-test="complete-header"]');
84+
await expectElementToContainText(orderCompleteMessage(), /thank you for your order/i);
85+
```
86+
87+
### 7. Playwright built-in locators (role / text) — use only when no stable selector exists
88+
89+
Text- and role-based locators are **flaky**: they change with copy, locale, and country. If the only way to find an element is by its text, a failure does not tell you whether the element is missing (bug) or the wording changed (new feature / i18n). Prefer **data-testid**, **data-\***, **id**, or **unique CSS/XPath** first.
90+
91+
When you must use role or text:
92+
93+
```typescript
94+
// By ARIA role + accessible name
95+
await click(getLocatorByRole('button', { name: 'Submit' }));
96+
await fill(getLocatorByRole('textbox', { name: 'Email' }), 'user@example.com');
97+
98+
// By label text (form fields)
99+
await fill(getLocatorByLabel('Email address'), 'user@example.com');
100+
101+
// By placeholder text
102+
await fill(getLocatorByPlaceholder('Search...'), 'playwright');
103+
104+
// By visible text — avoid for assertions; use stable selector + assert text separately
105+
await click(getLocatorByText('Add to cart'));
106+
```
107+
108+
**Assertions:** Prefer a **stable locator** for the element and assert the **text in the assertion**. That way a failure shows "expected text X, got Y" (copy change) vs element not found (bug).
109+
110+
```typescript
111+
// Prefer: stable selector + text in assertion
112+
const orderCompleteMessage = () => getLocator('[data-test="complete-header"]');
113+
await expectElementToContainText(orderCompleteMessage(), /thank you for your order/i);
114+
115+
// Avoid: locating by text — fails ambiguously if copy or locale changes
116+
// const orderCompleteMessage = () => getLocatorByRole('heading', { name: /thank you for your order/i });
103117
```
104118

105119
### 8. XPath (structural)

.cursor/rules/project.mdc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
2-
description: Project-wide instructions and conventions for vasu-ts-template
3-
globs: ['**/*']
2+
description: Project-wide instructions and conventions
3+
globs: ["**/*"]
44
---
55

66
@file CLAUDE.md

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Playwright TypeScript Template
1+
# Playwright TypeScript Project
22

33
## Project Overview
44

package-lock.json

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "playwright-ts-template",
3-
"version": "1.24.0",
3+
"version": "1.24.1",
44
"description": "Playwright Type script framework is designed for Web (Desktop & Mobile), API, and Electron apps. Stable and Robust layer on top of Playwright with inbuilt Utilities, Linting, Logging, Web hooks, Github actions, Reports and much more",
55
"keywords": [
66
"playwright",
@@ -29,7 +29,7 @@
2929
"allure-commandline": "^2.38.0",
3030
"allure-playwright": "^3.6.0",
3131
"axios": "^1.13.6",
32-
"vasu-playwright-utils": "^1.24.0"
32+
"vasu-playwright-utils": "^1.24.1"
3333
},
3434
"scripts": {
3535
"clean": "rimraf dist",

0 commit comments

Comments
 (0)