@mechris3/glassbox 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -179,7 +179,78 @@ export default defineConfig({
|
|
|
179
179
|
});
|
|
180
180
|
```
|
|
181
181
|
|
|
182
|
-
###
|
|
182
|
+
### Best Practices
|
|
183
|
+
|
|
184
|
+
### Journeys are pure orchestration
|
|
185
|
+
|
|
186
|
+
A journey's `execute()` method should contain **only** calls to page object methods — no conditionals, no assertions, no direct selectors:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
// Good — reads like a user story
|
|
190
|
+
async execute() {
|
|
191
|
+
await this.loginPage.loginAs('alice@company.com', 'alice123');
|
|
192
|
+
await this.dashboardPage.verifyLoaded();
|
|
193
|
+
await this.dashboardPage.navigateToProjects();
|
|
194
|
+
await this.projectsPage.verifyProjectCount(6);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Bad — logic leaking into journey
|
|
198
|
+
async execute() {
|
|
199
|
+
await this.fill('[data-testid="email"]', 'alice@company.com');
|
|
200
|
+
const count = await this.getText('[data-testid="count"]');
|
|
201
|
+
if (parseInt(count) !== 6) throw new Error('wrong');
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Page objects own everything
|
|
206
|
+
|
|
207
|
+
- **Selectors** — store in a private `selectors` object, never expose them
|
|
208
|
+
- **Interaction** — `loginAs()`, `addTask()`, `filterByStatus()` — intention-revealing names
|
|
209
|
+
- **Validation** — `verifyLoaded()`, `verifyErrorMessage()`, `verifyTaskCount()` — throw descriptive errors on failure
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
export class ProjectsPage extends BasePage {
|
|
213
|
+
private selectors = {
|
|
214
|
+
projectCard: '[data-testid="project-card"]',
|
|
215
|
+
filterActive: '[data-testid="filter-active"]',
|
|
216
|
+
count: '[data-testid="project-count"]',
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
async filterByActive() {
|
|
220
|
+
await this.click(this.selectors.filterActive);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async verifyProjectCount(expected: number) {
|
|
224
|
+
const count = await this.getText(this.selectors.count);
|
|
225
|
+
if (count !== String(expected)) {
|
|
226
|
+
throw new Error(`Expected ${expected} projects, got ${count}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Selector strategy
|
|
233
|
+
|
|
234
|
+
- Always use `[data-testid="..."]` — never CSS classes, tag names, or DOM structure
|
|
235
|
+
- For repeated items, include an identifier: `[data-testid="task-item-${id}"]`
|
|
236
|
+
- If an element doesn't have a `data-testid`, add one to your app first
|
|
237
|
+
|
|
238
|
+
### File organisation
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
glassbox/
|
|
242
|
+
├── page-objects/
|
|
243
|
+
│ ├── login.page.ts ← one class per file
|
|
244
|
+
│ ├── dashboard.page.ts
|
|
245
|
+
│ └── projects.page.ts
|
|
246
|
+
├── journeys/
|
|
247
|
+
│ ├── login-and-browse.journey.ts
|
|
248
|
+
│ └── create-task.journey.ts
|
|
249
|
+
└── helpers/
|
|
250
|
+
└── before-each.ts
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Lifecycle Hooks
|
|
183
254
|
|
|
184
255
|
Run code before/after journeys for test data setup and cleanup:
|
|
185
256
|
|
package/package.json
CHANGED