@mechris3/glassbox 0.1.3 → 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 +94 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -76,6 +76,28 @@ Either way, run `glassbox` commands from the directory containing your `glassbox
|
|
|
76
76
|
- Node.js 20+
|
|
77
77
|
- A Chromium-based browser (Chrome, Brave, Edge)
|
|
78
78
|
|
|
79
|
+
## Environment Variables
|
|
80
|
+
|
|
81
|
+
Config files, hooks, journeys, and page objects are all TypeScript running in Node — `process.env` works everywhere:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
// page-objects/login.page.ts — credentials from env
|
|
85
|
+
async login() {
|
|
86
|
+
const user = process.env.TEST_USERNAME ?? 'default@test.com';
|
|
87
|
+
const pass = process.env.TEST_PASSWORD ?? 'password123';
|
|
88
|
+
await this.fill('[data-testid="email"]', user);
|
|
89
|
+
await this.fill('[data-testid="password"]', pass);
|
|
90
|
+
await this.click('[data-testid="submit"]');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// glassbox.config.ts — headless in CI
|
|
94
|
+
browser: { headless: process.env.CI === 'true' }
|
|
95
|
+
|
|
96
|
+
// helpers/before-each.ts — API key for test data service
|
|
97
|
+
const apiKey = process.env.TEST_API_KEY;
|
|
98
|
+
await fetch('http://...', { headers: { Authorization: `Bearer ${apiKey}` } });
|
|
99
|
+
```
|
|
100
|
+
|
|
79
101
|
## Consumer Usage
|
|
80
102
|
|
|
81
103
|
### Page Objects
|
|
@@ -157,7 +179,78 @@ export default defineConfig({
|
|
|
157
179
|
});
|
|
158
180
|
```
|
|
159
181
|
|
|
160
|
-
###
|
|
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
|
|
161
254
|
|
|
162
255
|
Run code before/after journeys for test data setup and cleanup:
|
|
163
256
|
|
package/package.json
CHANGED