Skip to content

Commit ea4bb0e

Browse files
committed
fix: update readme instructions + improve readability
1 parent 4ac7a56 commit ea4bb0e

File tree

1 file changed

+95
-53
lines changed

1 file changed

+95
-53
lines changed

README.md

Lines changed: 95 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,21 @@ Every piece of UI becomes a class. Every selector becomes a typed accessor. Ever
88

99
- **Everything is a PageObject**: No artificial hierarchy between "pages" and "components". A modal, a button, and a page are all constructed the exact same way.
1010
- **Strongly Typed over Stringly-Typed**: Eliminate raw locator strings in tests completely. Tests only use typed accessors.
11-
- **Composable over flat**: Controls nest inside controls to mirror the actual DOM structure.
11+
- **Composable over flat**: Controls nest inside controls to mirror the actual DOM or component structure.
1212
- **Relative Locators & Reusability**: Because controls nest, locators are chained under the hood (`parent.locator().getByTestId(...)`). Selectors only need to be unique *within their parent component*, massively improving reusability. Instead of `data-testid="checkout-page-cart-item-remove-button"`, you just use `data-testid="Remove"`.
1313
- **Lazy over eager**: Locator chains rebuild dynamically only when accessed, ensuring resilience against dynamic DOM changes and re-renders.
1414
- **Playwright Best Practices Embedded**: Deep support for user-facing attributes (`getByRole`, `getByText`, etc.) natively via decorators, with web-first assertions built-in.
1515

1616
### The Control Graph
17-
Controls compose into a hierarchical structure mirroring the DOM:
17+
Controls compose into a hierarchical structure mirroring the DOM or your components structure:
1818

1919
```typescript
20+
import { PageObject, SelectorByRole, RootSelector, Selector, ListSelector, ListPageObject } from "playwright-page-object";
21+
import { test } from "./fixtures";
22+
2023
// Define your controls once
24+
class ButtonControl extends PageObject {}
25+
2126
class CartItemControl extends PageObject {
2227
@SelectorByRole("button", { name: "Remove" })
2328
accessor RemoveButton = new ButtonControl();
@@ -43,7 +48,7 @@ test("remove first cart item", async ({ checkoutPage }) => {
4348
## 📦 Installation
4449

4550
```bash
46-
npm install playwright-page-object
51+
npm install -D playwright-page-object
4752
```
4853

4954
*Note: This library heavily relies on the ECMAScript standard `accessor` keyword (stable in TypeScript 5.0+). Make sure your `tsconfig.json` targets an appropriate environment (`"target": "ES2015"` or higher) or ensures decorators are supported.*
@@ -56,54 +61,71 @@ When a class extends `PageObject`, it inherits a rich set of built-in methods fo
5661

5762
Like raw Playwright, these actions automatically wait for the element to become actionable (visible, enabled, stable).
5863

59-
- `.click(options?)`: Clicks the element.
60-
- `.dblclick(options?)`: Double-clicks the element.
61-
- `.hover(options?)`: Hovers over the element.
62-
- `.fill(value, options?)`: Fills the input with the given value.
63-
- `.clear(options?)`: Clears the input value.
64-
- `.check(options?)`: Checks a checkbox or radio button.
65-
- `.uncheck(options?)`: Unchecks a checkbox.
66-
- `.press(key, options?)`: Presses a key (e.g., `Enter`, `Tab`) on the element.
64+
| Method | Description |
65+
|--------|-------------|
66+
| `.click(options?)` | Clicks the element. |
67+
| `.dblclick(options?)` | Double-clicks the element. |
68+
| `.hover(options?)` | Hovers over the element. |
69+
| `.fill(value, options?)` | Fills the input with the given value. |
70+
| `.clear(options?)` | Clears the input value. |
71+
| `.check(options?)` | Checks a checkbox or radio button. |
72+
| `.uncheck(options?)` | Unchecks a checkbox. |
73+
| `.press(key, options?)` | Presses a key (e.g., `Enter`, `Tab`) on the element. |
6774

6875
### Waits
69-
- `.waitVisible()`: Waits for the element to become visible.
70-
- `.waitHidden()`: Waits for the element to become hidden.
71-
- `.waitText(text)`: Waits for the element to have the given text (string or regex).
72-
- `.waitValue(value)`: Waits for the element to have the given value.
73-
- `.waitNoValue()`: Waits for the element to have no value.
74-
- `.waitCount(count)`: Waits for the locator to resolve to the given count.
75-
- `.waitChecked()`: Waits for a checkbox/radio to be checked.
76-
- `.waitUnChecked()`: Waits for a checkbox/radio to be unchecked.
77-
- `.waitProp(name, value)`: Waits for a React/Vue prop (data attribute) to equal the given value.
78-
- `.waitPropAbsence(name, value)`: Waits for a React/Vue prop (data attribute) to NOT equal the given value.
76+
77+
| Method | Description |
78+
|--------|-------------|
79+
| `.waitVisible()` | Waits for the element to become visible. |
80+
| `.waitHidden()` | Waits for the element to become hidden. |
81+
| `.waitText(text)` | Waits for the element to have the given text (string or regex). |
82+
| `.waitValue(value)` | Waits for the element to have the given value. |
83+
| `.waitNoValue()` | Waits for the element to have no value. |
84+
| `.waitCount(count)` | Waits for the locator to resolve to the given count. |
85+
| `.waitChecked()` | Waits for a checkbox/radio to be checked. |
86+
| `.waitUnChecked()` | Waits for a checkbox/radio to be unchecked. |
87+
| `.waitProp(name, value)` | Waits for a React/Vue prop (data attribute) to equal the given value. |
88+
| `.waitPropAbsence(name, value)` | Waits for a React/Vue prop (data attribute) to NOT equal the given value. |
7989

8090
### Assertions
8191

8292
Provides native Playwright assertions securely tied to the underlying locator.
83-
- `.expect()`: Returns a Playwright expect assertion for this locator (e.g., `await myControl.expect().toBeEnabled()`).
84-
- `.expect({ soft: true })`: Support for soft assertions that do not fail the test immediately.
93+
94+
| Method | Description |
95+
|--------|-------------|
96+
| `.expect()` | Returns a Playwright expect assertion for this locator (e.g., `await myControl.expect().toBeEnabled()`). |
97+
| `.expect({ soft: true })` | Support for soft assertions that do not fail the test immediately. |
8598

8699
## 📚 Comprehensive API Reference: `ListPageObject`
87100

88101
Manage collections of elements effortlessly with `ListPageObject`.
89102

90103
### The `.items` Proxy
91-
- **Array-like access**: Access specific items directly via index: `list.items[0]`
92-
- **Async iteration**: Iterate over all matching items easily: `for await (const item of list.items) { ... }`
104+
105+
| Feature | Description |
106+
|---------|-------------|
107+
| Array-like access | Access specific items directly via index: `list.items[0]` |
108+
| Async iteration | Iterate over all matching items easily: `for await (const item of list.items) { ... }` |
93109

94110
### Retrieval & Filtering
95-
- `.first()`: Returns the first item (index 0).
96-
- `.last()`: Returns the last item (index -1).
97-
- `.getItemByIndex(index)`: Returns the item at the given index.
98-
- `.filter(options)`: Returns items matching the given Playwright filter options.
99-
- `.filterByText(text)`: Returns items containing the given text.
100-
- `.filterByTestId(id)`: Returns items that contain an element with the given test id.
101-
- `.getItemByText(text)`: Returns the specific item containing the given text.
102-
- `.getItemByRole(role, options?)`: Returns the specific item matching the given ARIA role.
111+
112+
| Method | Description |
113+
|--------|-------------|
114+
| `.first()` | Returns the first item (index 0). |
115+
| `.last()` | Returns the last item (index -1). |
116+
| `.getItemByIndex(index)` | Returns the item at the given index. |
117+
| `.filter(options)` | Returns items matching the given Playwright filter options. |
118+
| `.filterByText(text)` | Returns items containing the given text. |
119+
| `.filterByTestId(id)` | Returns items that contain an element with the given test id. |
120+
| `.getItemByText(text)` | Returns the specific item containing the given text. |
121+
| `.getItemByRole(role, options?)` | Returns the specific item matching the given ARIA role. |
103122

104123
### Utilities
105-
- `.count()`: Returns the total number of items in the list.
106-
- `.getAll()`: Returns all items as an array of page objects.
124+
125+
| Method | Description |
126+
|--------|-------------|
127+
| `.count()` | Returns the total number of items in the list. |
128+
| `.getAll()` | Returns all items as an array of page objects. |
107129

108130
## 🏷️ Decorators Cheat Sheet
109131

@@ -113,31 +135,43 @@ The library provides 1-to-1 mappings with Playwright's native locator methods.
113135
Each strategy has a `@Selector...` variant for nested controls and a `@RootSelector...` variant for top-level pages.
114136

115137
### Root Locators (Top-level entry points)
138+
116139
Used on classes to define the base locator for a page or top-level component.
117-
- `@RootSelector(id)` -> `getByTestId(id)`
118-
- `@RootSelectorByRole(role, options?)` -> `getByRole(role, options)`
119-
- `@RootSelectorByText(text, options?)` -> `getByText(text, options)`
120-
- `@RootSelectorByLabel(label, options?)` -> `getByLabel(label, options)`
121-
- `@RootSelectorByPlaceholder(placeholder, options?)` -> `getByPlaceholder(placeholder, options)`
122-
- `@RootSelectorByAltText(altText, options?)` -> `getByAltText(altText, options)`
123-
- `@RootSelectorByTitle(title, options?)` -> `getByTitle(title, options)`
140+
141+
| Decorator | Maps to |
142+
|-----------|---------|
143+
| `@RootSelector(id)` | `getByTestId(id)` |
144+
| `@RootSelectorByRole(role, options?)` | `getByRole(role, options)` |
145+
| `@RootSelectorByText(text, options?)` | `getByText(text, options)` |
146+
| `@RootSelectorByLabel(label, options?)` | `getByLabel(label, options)` |
147+
| `@RootSelectorByPlaceholder(placeholder, options?)` | `getByPlaceholder(placeholder, options)` |
148+
| `@RootSelectorByAltText(altText, options?)` | `getByAltText(altText, options)` |
149+
| `@RootSelectorByTitle(title, options?)` | `getByTitle(title, options)` |
124150

125151
### Child Locators (Nested elements)
152+
126153
Used on properties (`accessor`) inside a `PageObject` to locate children relative to the parent.
127-
- `@Selector(id)` -> `getByTestId(id)`
128-
- `@SelectorByRole(role, options?)` -> `getByRole(role, options)`
129-
- `@SelectorByText(text, options?)` -> `getByText(text, options)`
130-
- `@SelectorByLabel(label, options?)` -> `getByLabel(label, options)`
131-
- `@SelectorByPlaceholder(placeholder, options?)` -> `getByPlaceholder(placeholder, options)`
132-
- `@SelectorByAltText(altText, options?)` -> `getByAltText(altText, options)`
133-
- `@SelectorByTitle(title, options?)` -> `getByTitle(title, options)`
134-
- `@SelectorBy(selectorFunction)` -> Custom locator function
154+
155+
| Decorator | Maps to |
156+
|-----------|---------|
157+
| `@Selector(id)` | `getByTestId(id)` |
158+
| `@SelectorByRole(role, options?)` | `getByRole(role, options)` |
159+
| `@SelectorByText(text, options?)` | `getByText(text, options)` |
160+
| `@SelectorByLabel(label, options?)` | `getByLabel(label, options)` |
161+
| `@SelectorByPlaceholder(placeholder, options?)` | `getByPlaceholder(placeholder, options)` |
162+
| `@SelectorByAltText(altText, options?)` | `getByAltText(altText, options)` |
163+
| `@SelectorByTitle(title, options?)` | `getByTitle(title, options)` |
164+
| `@SelectorBy(selectorFunction)` | Custom locator function |
135165

136166
### List Locators
167+
137168
Used for collections of elements.
138-
- `@ListSelector(id)` -> `getByTestId(new RegExp(id))` (matches children sharing a test ID pattern)
139-
- `@ListStrictSelector(id)` -> `getByTestId(id)` (exact match)
140-
- `@ListRootSelector(id)` -> `getByTestId(id)` on the root level
169+
170+
| Decorator | Maps to |
171+
|-----------|---------|
172+
| `@ListSelector(id)` | `getByTestId(new RegExp(id))` — matches children sharing a test ID pattern |
173+
| `@ListStrictSelector(id)` | `getByTestId(id)` — exact match |
174+
| `@ListRootSelector(id)` | `getByTestId(id)` on the root level |
141175

142176
## 🔄 Incremental Adoption (Brownfield Projects)
143177

@@ -225,3 +259,11 @@ test("should apply promo code and remove first item", async ({ checkoutPage }) =
225259
}
226260
});
227261
```
262+
263+
## Contributing
264+
265+
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on how to contribute to this project.
266+
267+
## License
268+
269+
This project is licensed under the MIT License. See [LICENSE](LICENSE) for details.

0 commit comments

Comments
 (0)