@testspectra/skills 1.1.0-rc.0 → 1.1.0-rc.1
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/dist/skills/matchers-and-assertions/SKILL.md +37 -4
- package/dist/skills/page-objects-and-selectors/SKILL.md +26 -1
- package/dist/skills/workspace-structure/SKILL.md +17 -5
- package/package.json +1 -1
- package/skills/matchers-and-assertions/SKILL.md +37 -4
- package/skills/page-objects-and-selectors/SKILL.md +26 -1
- package/skills/workspace-structure/SKILL.md +17 -5
|
@@ -53,6 +53,15 @@ per action/assertion in `.testspectra/reports/`.
|
|
|
53
53
|
| `await el.shouldHaveAttribute(attr, val)` | `Expect element "{el}" attribute "{attr}" to be "{val}"` |
|
|
54
54
|
| `await el.shouldBeFocused()` | `Expect element "{el}" to be focused` |
|
|
55
55
|
|
|
56
|
+
Every assertion above (and every collection matcher in section 3) accepts a trailing
|
|
57
|
+
`options?: { timeoutMs }` to override the adaptive fail-fast timeout for that one call, without
|
|
58
|
+
raising it for every other assertion in the test — use this when a specific assertion is known to
|
|
59
|
+
need longer, e.g. right after a deliberately delayed `Spectra.intercept(..., { delayMs })` mock:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
await ReportPage.statusBadge.shouldHaveText('Ready', { timeoutMs: 5000 });
|
|
63
|
+
```
|
|
64
|
+
|
|
56
65
|
---
|
|
57
66
|
|
|
58
67
|
## 3. Collection Matchers (on `Spectra.getAll(selector)` getters)
|
|
@@ -68,12 +77,36 @@ per action/assertion in `.testspectra/reports/`.
|
|
|
68
77
|
|
|
69
78
|
---
|
|
70
79
|
|
|
71
|
-
## 4.
|
|
80
|
+
## 4. Scoped Element Lookups: `.get()` / `.getAll()`
|
|
81
|
+
|
|
82
|
+
Any resolved element also has `.get(childSelector)` / `.getAll(childSelector)`, chaining a lookup
|
|
83
|
+
scoped to that element's own subtree — Playwright's `locator.locator()` model. **Prefer this over
|
|
84
|
+
a hand-written compound selector** whenever disambiguating repeated markup that shares one
|
|
85
|
+
selector across rows (a list of cards, a table's rows):
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// WRONG: `.buy-btn` is the same selector on every card — this always resolves the first one.
|
|
89
|
+
await Spectra.get('.buy-btn').click();
|
|
90
|
+
|
|
91
|
+
// RIGHT: scoped to the specific card first.
|
|
92
|
+
const thirdCard = Spectra.getAll('.product-card').nth(2);
|
|
93
|
+
await thirdCard.get('.buy-btn').click();
|
|
94
|
+
await thirdCard.getAll('.badge').shouldHaveLength(2);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Works identically from a Page Object property, since it's already a resolved element:
|
|
98
|
+
`await SettingsPage.modal.get('#save-btn').click()`. `.first()`/`.last()`/`.nth(i)` on a scoped
|
|
99
|
+
collection carry the scope forward too. No behavior difference on web vs. Android — resolution
|
|
100
|
+
(real DOM query vs. bounds-containment) is handled transparently underneath.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 5. Global `Spectra.*` Commands
|
|
72
105
|
|
|
73
106
|
| Category | Syntax |
|
|
74
107
|
| :------------- | :------------------------------------------------------------------------------------------------------- |
|
|
75
108
|
| Navigation | `await Spectra.navigate("/login")` / `.back()` / `.forward()` / `.refresh()` |
|
|
76
|
-
| Element query | `Spectra.get(selector)` / `Spectra.getAll(selector)`
|
|
109
|
+
| Element query | `Spectra.get(selector)` / `Spectra.getAll(selector)` — chain `.get()`/`.getAll()` on the result to scope a lookup, see section 4 |
|
|
77
110
|
| Window | `await Spectra.setViewport(1920, 1080)` |
|
|
78
111
|
| Timing | `await Spectra.wait(1000)` |
|
|
79
112
|
| Keyboard | `await Spectra.pressKey("Enter")` |
|
|
@@ -81,7 +114,7 @@ per action/assertion in `.testspectra/reports/`.
|
|
|
81
114
|
| Mobile gesture | `await Spectra.swipe({ direction: "left", distance: 300 })` |
|
|
82
115
|
| Network mock | `const mock = await Spectra.intercept({ url, method, response })` — see the `network-interception` skill |
|
|
83
116
|
|
|
84
|
-
##
|
|
117
|
+
## 6. `Spectra.browser.*` — Browser/Context Level
|
|
85
118
|
|
|
86
119
|
| Syntax | Purpose |
|
|
87
120
|
| :-------------------------------------------------- | :------------------------------ |
|
|
@@ -96,7 +129,7 @@ per action/assertion in `.testspectra/reports/`.
|
|
|
96
129
|
|
|
97
130
|
---
|
|
98
131
|
|
|
99
|
-
##
|
|
132
|
+
## 7. Full Example
|
|
100
133
|
|
|
101
134
|
```typescript
|
|
102
135
|
it('should login as administrator and assert dashboard metrics', async () => {
|
|
@@ -98,7 +98,32 @@ cross-matching.
|
|
|
98
98
|
|
|
99
99
|
---
|
|
100
100
|
|
|
101
|
-
## 3.
|
|
101
|
+
## 3. Scoping a Lookup Instead of Writing a Compound Selector
|
|
102
|
+
|
|
103
|
+
When the same selector repeats across rows (a list of cards, a table), **do not** reach for a
|
|
104
|
+
compound CSS selector (`.card:nth-child(3) .buy-btn`) or an index baked into an xpath — those are
|
|
105
|
+
brittle and don't have a cross-platform equivalent (Android has no CSS nth-child). Chain
|
|
106
|
+
`.get(childSelector)` / `.getAll(childSelector)` off an already-resolved element instead; it
|
|
107
|
+
scopes the lookup to that element's own subtree:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
class ProductsPage {
|
|
111
|
+
get cards() {
|
|
112
|
+
return Spectra.getAll('.product-card');
|
|
113
|
+
}
|
|
114
|
+
cardBuyButton(index: number) {
|
|
115
|
+
return this.cards.nth(index).get('.buy-btn');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
This is a Page Object concern, not a new selector prefix — `~`/`#`/xpath all still apply as the
|
|
121
|
+
child selector, resolved within the parent's subtree instead of the whole document/screen. See the
|
|
122
|
+
`matchers-and-assertions` skill for the full `.get()`/`.getAll()` chaining behavior.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 4. Never Use Raw `$` / `$$` / `browser` / `driver`
|
|
102
127
|
|
|
103
128
|
TestSpectra encapsulates the entire runtime into a single global `Spectra` object. There is no raw
|
|
104
129
|
WebdriverIO `$`, `$$`, `browser`, or `driver` in test-authoring code — always go through
|
|
@@ -123,11 +123,8 @@ export default defineConfig({
|
|
|
123
123
|
maxConcurrentSessions: '1',
|
|
124
124
|
headless: true,
|
|
125
125
|
implicitWait: '5000',
|
|
126
|
-
pageLoadTimeout: '30000',
|
|
127
|
-
scriptTimeout: '30000',
|
|
128
126
|
parallelizationMode: 'testcase', // "testcase" | "suite"
|
|
129
127
|
},
|
|
130
|
-
browsers: [{ id: 'chrome-desktop', type: 'chrome', mobileEmulation: false }],
|
|
131
128
|
androidConfig: {
|
|
132
129
|
driverServer: 'tcp://127.0.0.1:8200',
|
|
133
130
|
appPackage: 'dev.example.app',
|
|
@@ -153,8 +150,8 @@ export default defineConfig({
|
|
|
153
150
|
networkMonitoringEnabled: true,
|
|
154
151
|
fastResponseTime: '200',
|
|
155
152
|
normalResponseTime: '1000',
|
|
156
|
-
monitoredDomains: [],
|
|
157
|
-
environmentVariables:
|
|
153
|
+
monitoredDomains: [{ domain: 'api.example.com', enabled: true }],
|
|
154
|
+
environmentVariables: { API_MODE: 'sandbox' },
|
|
158
155
|
},
|
|
159
156
|
});
|
|
160
157
|
```
|
|
@@ -162,6 +159,21 @@ export default defineConfig({
|
|
|
162
159
|
Numeric-looking fields (timeouts, ports, session counts) accept either a `number` or a numeric
|
|
163
160
|
`string` — stay consistent with whatever the rest of the file already uses.
|
|
164
161
|
|
|
162
|
+
`executionConfig` notes:
|
|
163
|
+
|
|
164
|
+
- **`monitoredDomains`**: empty/absent = record every domain; non-empty = only requests matching
|
|
165
|
+
an `enabled: true` entry (exact hostname or leading-`*` wildcard) get recorded/streamed to the
|
|
166
|
+
Network panel. `networkMonitoringEnabled: false` disables recording outright regardless of this
|
|
167
|
+
list.
|
|
168
|
+
- **`fastResponseTime`/`normalResponseTime`**: millisecond thresholds the VS Code Network panel
|
|
169
|
+
uses to color each request's Duration cell (fast/normal/slow). No effect on test execution
|
|
170
|
+
itself.
|
|
171
|
+
- **`environmentVariables`**: a flat `Record<string, string>` (not an array, not nested
|
|
172
|
+
key/value pairs) — every key becomes both a typed `Spectra.env.KEY` entry (generated into
|
|
173
|
+
`.testspectra/types/env.d.ts`, autocompleted, zero-import — **prefer this over `process.env`**,
|
|
174
|
+
which is untyped `string | undefined`) and a plain `process.env.KEY` string for code that must
|
|
175
|
+
consume the untyped standard API.
|
|
176
|
+
|
|
165
177
|
---
|
|
166
178
|
|
|
167
179
|
## 6. Core `spectra` CLI Commands
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testspectra/skills",
|
|
3
|
-
"version": "1.1.0-rc.
|
|
3
|
+
"version": "1.1.0-rc.1",
|
|
4
4
|
"description": "Installer & manager for modular AI agent skills that standardize TestSpectra script authoring (specs, page objects, steps, actions, hooks, fixtures, matchers)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|
|
@@ -53,6 +53,15 @@ per action/assertion in `.testspectra/reports/`.
|
|
|
53
53
|
| `await el.shouldHaveAttribute(attr, val)` | `Expect element "{el}" attribute "{attr}" to be "{val}"` |
|
|
54
54
|
| `await el.shouldBeFocused()` | `Expect element "{el}" to be focused` |
|
|
55
55
|
|
|
56
|
+
Every assertion above (and every collection matcher in section 3) accepts a trailing
|
|
57
|
+
`options?: { timeoutMs }` to override the adaptive fail-fast timeout for that one call, without
|
|
58
|
+
raising it for every other assertion in the test — use this when a specific assertion is known to
|
|
59
|
+
need longer, e.g. right after a deliberately delayed `Spectra.intercept(..., { delayMs })` mock:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
await ReportPage.statusBadge.shouldHaveText('Ready', { timeoutMs: 5000 });
|
|
63
|
+
```
|
|
64
|
+
|
|
56
65
|
---
|
|
57
66
|
|
|
58
67
|
## 3. Collection Matchers (on `Spectra.getAll(selector)` getters)
|
|
@@ -68,12 +77,36 @@ per action/assertion in `.testspectra/reports/`.
|
|
|
68
77
|
|
|
69
78
|
---
|
|
70
79
|
|
|
71
|
-
## 4.
|
|
80
|
+
## 4. Scoped Element Lookups: `.get()` / `.getAll()`
|
|
81
|
+
|
|
82
|
+
Any resolved element also has `.get(childSelector)` / `.getAll(childSelector)`, chaining a lookup
|
|
83
|
+
scoped to that element's own subtree — Playwright's `locator.locator()` model. **Prefer this over
|
|
84
|
+
a hand-written compound selector** whenever disambiguating repeated markup that shares one
|
|
85
|
+
selector across rows (a list of cards, a table's rows):
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// WRONG: `.buy-btn` is the same selector on every card — this always resolves the first one.
|
|
89
|
+
await Spectra.get('.buy-btn').click();
|
|
90
|
+
|
|
91
|
+
// RIGHT: scoped to the specific card first.
|
|
92
|
+
const thirdCard = Spectra.getAll('.product-card').nth(2);
|
|
93
|
+
await thirdCard.get('.buy-btn').click();
|
|
94
|
+
await thirdCard.getAll('.badge').shouldHaveLength(2);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Works identically from a Page Object property, since it's already a resolved element:
|
|
98
|
+
`await SettingsPage.modal.get('#save-btn').click()`. `.first()`/`.last()`/`.nth(i)` on a scoped
|
|
99
|
+
collection carry the scope forward too. No behavior difference on web vs. Android — resolution
|
|
100
|
+
(real DOM query vs. bounds-containment) is handled transparently underneath.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 5. Global `Spectra.*` Commands
|
|
72
105
|
|
|
73
106
|
| Category | Syntax |
|
|
74
107
|
| :------------- | :------------------------------------------------------------------------------------------------------- |
|
|
75
108
|
| Navigation | `await Spectra.navigate("/login")` / `.back()` / `.forward()` / `.refresh()` |
|
|
76
|
-
| Element query | `Spectra.get(selector)` / `Spectra.getAll(selector)`
|
|
109
|
+
| Element query | `Spectra.get(selector)` / `Spectra.getAll(selector)` — chain `.get()`/`.getAll()` on the result to scope a lookup, see section 4 |
|
|
77
110
|
| Window | `await Spectra.setViewport(1920, 1080)` |
|
|
78
111
|
| Timing | `await Spectra.wait(1000)` |
|
|
79
112
|
| Keyboard | `await Spectra.pressKey("Enter")` |
|
|
@@ -81,7 +114,7 @@ per action/assertion in `.testspectra/reports/`.
|
|
|
81
114
|
| Mobile gesture | `await Spectra.swipe({ direction: "left", distance: 300 })` |
|
|
82
115
|
| Network mock | `const mock = await Spectra.intercept({ url, method, response })` — see the `network-interception` skill |
|
|
83
116
|
|
|
84
|
-
##
|
|
117
|
+
## 6. `Spectra.browser.*` — Browser/Context Level
|
|
85
118
|
|
|
86
119
|
| Syntax | Purpose |
|
|
87
120
|
| :-------------------------------------------------- | :------------------------------ |
|
|
@@ -96,7 +129,7 @@ per action/assertion in `.testspectra/reports/`.
|
|
|
96
129
|
|
|
97
130
|
---
|
|
98
131
|
|
|
99
|
-
##
|
|
132
|
+
## 7. Full Example
|
|
100
133
|
|
|
101
134
|
```typescript
|
|
102
135
|
it('should login as administrator and assert dashboard metrics', async () => {
|
|
@@ -98,7 +98,32 @@ cross-matching.
|
|
|
98
98
|
|
|
99
99
|
---
|
|
100
100
|
|
|
101
|
-
## 3.
|
|
101
|
+
## 3. Scoping a Lookup Instead of Writing a Compound Selector
|
|
102
|
+
|
|
103
|
+
When the same selector repeats across rows (a list of cards, a table), **do not** reach for a
|
|
104
|
+
compound CSS selector (`.card:nth-child(3) .buy-btn`) or an index baked into an xpath — those are
|
|
105
|
+
brittle and don't have a cross-platform equivalent (Android has no CSS nth-child). Chain
|
|
106
|
+
`.get(childSelector)` / `.getAll(childSelector)` off an already-resolved element instead; it
|
|
107
|
+
scopes the lookup to that element's own subtree:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
class ProductsPage {
|
|
111
|
+
get cards() {
|
|
112
|
+
return Spectra.getAll('.product-card');
|
|
113
|
+
}
|
|
114
|
+
cardBuyButton(index: number) {
|
|
115
|
+
return this.cards.nth(index).get('.buy-btn');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
This is a Page Object concern, not a new selector prefix — `~`/`#`/xpath all still apply as the
|
|
121
|
+
child selector, resolved within the parent's subtree instead of the whole document/screen. See the
|
|
122
|
+
`matchers-and-assertions` skill for the full `.get()`/`.getAll()` chaining behavior.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 4. Never Use Raw `$` / `$$` / `browser` / `driver`
|
|
102
127
|
|
|
103
128
|
TestSpectra encapsulates the entire runtime into a single global `Spectra` object. There is no raw
|
|
104
129
|
WebdriverIO `$`, `$$`, `browser`, or `driver` in test-authoring code — always go through
|
|
@@ -123,11 +123,8 @@ export default defineConfig({
|
|
|
123
123
|
maxConcurrentSessions: '1',
|
|
124
124
|
headless: true,
|
|
125
125
|
implicitWait: '5000',
|
|
126
|
-
pageLoadTimeout: '30000',
|
|
127
|
-
scriptTimeout: '30000',
|
|
128
126
|
parallelizationMode: 'testcase', // "testcase" | "suite"
|
|
129
127
|
},
|
|
130
|
-
browsers: [{ id: 'chrome-desktop', type: 'chrome', mobileEmulation: false }],
|
|
131
128
|
androidConfig: {
|
|
132
129
|
driverServer: 'tcp://127.0.0.1:8200',
|
|
133
130
|
appPackage: 'dev.example.app',
|
|
@@ -153,8 +150,8 @@ export default defineConfig({
|
|
|
153
150
|
networkMonitoringEnabled: true,
|
|
154
151
|
fastResponseTime: '200',
|
|
155
152
|
normalResponseTime: '1000',
|
|
156
|
-
monitoredDomains: [],
|
|
157
|
-
environmentVariables:
|
|
153
|
+
monitoredDomains: [{ domain: 'api.example.com', enabled: true }],
|
|
154
|
+
environmentVariables: { API_MODE: 'sandbox' },
|
|
158
155
|
},
|
|
159
156
|
});
|
|
160
157
|
```
|
|
@@ -162,6 +159,21 @@ export default defineConfig({
|
|
|
162
159
|
Numeric-looking fields (timeouts, ports, session counts) accept either a `number` or a numeric
|
|
163
160
|
`string` — stay consistent with whatever the rest of the file already uses.
|
|
164
161
|
|
|
162
|
+
`executionConfig` notes:
|
|
163
|
+
|
|
164
|
+
- **`monitoredDomains`**: empty/absent = record every domain; non-empty = only requests matching
|
|
165
|
+
an `enabled: true` entry (exact hostname or leading-`*` wildcard) get recorded/streamed to the
|
|
166
|
+
Network panel. `networkMonitoringEnabled: false` disables recording outright regardless of this
|
|
167
|
+
list.
|
|
168
|
+
- **`fastResponseTime`/`normalResponseTime`**: millisecond thresholds the VS Code Network panel
|
|
169
|
+
uses to color each request's Duration cell (fast/normal/slow). No effect on test execution
|
|
170
|
+
itself.
|
|
171
|
+
- **`environmentVariables`**: a flat `Record<string, string>` (not an array, not nested
|
|
172
|
+
key/value pairs) — every key becomes both a typed `Spectra.env.KEY` entry (generated into
|
|
173
|
+
`.testspectra/types/env.d.ts`, autocompleted, zero-import — **prefer this over `process.env`**,
|
|
174
|
+
which is untyped `string | undefined`) and a plain `process.env.KEY` string for code that must
|
|
175
|
+
consume the untyped standard API.
|
|
176
|
+
|
|
165
177
|
---
|
|
166
178
|
|
|
167
179
|
## 6. Core `spectra` CLI Commands
|