create-agent-skills 1.0.0 → 1.1.0
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 +6 -0
- package/package.json +1 -1
- package/skills/maestro-testing/SKILL.md +691 -0
- package/skills/maestro-testing/examples/checkout-flow.md +246 -0
- package/skills/maestro-testing/examples/login-flow.md +164 -0
- package/skills/maestro-testing/examples/project-structure.md +245 -0
- package/skills/maestro-testing/resources/commands-reference.md +142 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# E-commerce Checkout Flow Example
|
|
2
|
+
|
|
3
|
+
Complete checkout flow with scrolling, form filling, and payment.
|
|
4
|
+
|
|
5
|
+
## Flow File: checkout-test.yaml
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
appId: com.example.shop
|
|
9
|
+
name: "User can complete checkout with credit card"
|
|
10
|
+
tags:
|
|
11
|
+
- checkout
|
|
12
|
+
- payment
|
|
13
|
+
- e2e
|
|
14
|
+
- critical
|
|
15
|
+
env:
|
|
16
|
+
CARD_NUMBER: "4111111111111111"
|
|
17
|
+
CARD_EXPIRY: "12/28"
|
|
18
|
+
CARD_CVV: "123"
|
|
19
|
+
SHIPPING_ADDRESS: "123 Main Street"
|
|
20
|
+
SHIPPING_CITY: "San Francisco"
|
|
21
|
+
SHIPPING_ZIP: "94102"
|
|
22
|
+
---
|
|
23
|
+
# Prerequisite: User is logged in and has items in cart
|
|
24
|
+
- launchApp
|
|
25
|
+
|
|
26
|
+
# Navigate to cart
|
|
27
|
+
- tapOn:
|
|
28
|
+
id: "cart_icon"
|
|
29
|
+
|
|
30
|
+
# Verify cart has items
|
|
31
|
+
- assertVisible:
|
|
32
|
+
id: "cart_items_list"
|
|
33
|
+
|
|
34
|
+
# Verify total is displayed
|
|
35
|
+
- assertVisible: "Total: \\$[0-9]+\\.[0-9]{2}"
|
|
36
|
+
|
|
37
|
+
# Proceed to checkout
|
|
38
|
+
- tapOn:
|
|
39
|
+
id: "checkout_button"
|
|
40
|
+
enabled: true
|
|
41
|
+
|
|
42
|
+
# === SHIPPING SECTION ===
|
|
43
|
+
- assertVisible: "Shipping Address"
|
|
44
|
+
|
|
45
|
+
# Fill shipping form
|
|
46
|
+
- tapOn:
|
|
47
|
+
id: "address_input"
|
|
48
|
+
- inputText: "${SHIPPING_ADDRESS}"
|
|
49
|
+
|
|
50
|
+
- tapOn:
|
|
51
|
+
id: "city_input"
|
|
52
|
+
- inputText: "${SHIPPING_CITY}"
|
|
53
|
+
|
|
54
|
+
- tapOn:
|
|
55
|
+
id: "zip_input"
|
|
56
|
+
- inputText: "${SHIPPING_ZIP}"
|
|
57
|
+
|
|
58
|
+
- hideKeyboard
|
|
59
|
+
|
|
60
|
+
# Scroll to continue button if needed
|
|
61
|
+
- scrollUntilVisible:
|
|
62
|
+
element:
|
|
63
|
+
id: "continue_to_payment"
|
|
64
|
+
direction: DOWN
|
|
65
|
+
|
|
66
|
+
- tapOn:
|
|
67
|
+
id: "continue_to_payment"
|
|
68
|
+
|
|
69
|
+
# === PAYMENT SECTION ===
|
|
70
|
+
- assertVisible: "Payment Method"
|
|
71
|
+
|
|
72
|
+
# Select credit card
|
|
73
|
+
- tapOn:
|
|
74
|
+
below: "Payment Method"
|
|
75
|
+
text: "Credit Card"
|
|
76
|
+
|
|
77
|
+
# Fill card details
|
|
78
|
+
- tapOn:
|
|
79
|
+
id: "card_number_input"
|
|
80
|
+
- inputText: "${CARD_NUMBER}"
|
|
81
|
+
|
|
82
|
+
- tapOn:
|
|
83
|
+
id: "card_expiry_input"
|
|
84
|
+
- inputText: "${CARD_EXPIRY}"
|
|
85
|
+
|
|
86
|
+
- tapOn:
|
|
87
|
+
id: "card_cvv_input"
|
|
88
|
+
- inputText: "${CARD_CVV}"
|
|
89
|
+
|
|
90
|
+
- hideKeyboard
|
|
91
|
+
|
|
92
|
+
# === ORDER REVIEW ===
|
|
93
|
+
- scrollUntilVisible:
|
|
94
|
+
element:
|
|
95
|
+
id: "place_order_button"
|
|
96
|
+
direction: DOWN
|
|
97
|
+
|
|
98
|
+
# Verify order summary
|
|
99
|
+
- assertVisible: "Order Summary"
|
|
100
|
+
- assertVisible:
|
|
101
|
+
id: "order_total"
|
|
102
|
+
|
|
103
|
+
# Take screenshot before placing order
|
|
104
|
+
- takeScreenshot: "order_review"
|
|
105
|
+
|
|
106
|
+
# Place order
|
|
107
|
+
- tapOn:
|
|
108
|
+
id: "place_order_button"
|
|
109
|
+
enabled: true
|
|
110
|
+
|
|
111
|
+
# Wait for order processing
|
|
112
|
+
- extendedWaitUntil:
|
|
113
|
+
visible: "Order Confirmed"
|
|
114
|
+
timeout: 30000
|
|
115
|
+
|
|
116
|
+
# Verify confirmation
|
|
117
|
+
- assertVisible: "Order #[0-9]+"
|
|
118
|
+
- assertVisible: "Thank you for your order"
|
|
119
|
+
|
|
120
|
+
# Capture confirmation
|
|
121
|
+
- takeScreenshot: "order_confirmation"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Subflow: add-product-to-cart.yaml
|
|
125
|
+
|
|
126
|
+
```yaml
|
|
127
|
+
# subflows/add-product-to-cart.yaml
|
|
128
|
+
# Requires PRODUCT_NAME and optional QUANTITY
|
|
129
|
+
|
|
130
|
+
- scrollUntilVisible:
|
|
131
|
+
element: "${PRODUCT_NAME}"
|
|
132
|
+
direction: DOWN
|
|
133
|
+
timeout: 15000
|
|
134
|
+
|
|
135
|
+
- tapOn: "${PRODUCT_NAME}"
|
|
136
|
+
|
|
137
|
+
# Wait for product detail page
|
|
138
|
+
- assertVisible:
|
|
139
|
+
id: "product_detail_screen"
|
|
140
|
+
|
|
141
|
+
# Increase quantity if specified
|
|
142
|
+
- runFlow:
|
|
143
|
+
when:
|
|
144
|
+
true: ${QUANTITY && QUANTITY > 1}
|
|
145
|
+
commands:
|
|
146
|
+
- repeat:
|
|
147
|
+
times: ${QUANTITY - 1}
|
|
148
|
+
commands:
|
|
149
|
+
- tapOn:
|
|
150
|
+
id: "quantity_increase"
|
|
151
|
+
|
|
152
|
+
# Add to cart
|
|
153
|
+
- tapOn:
|
|
154
|
+
id: "add_to_cart_button"
|
|
155
|
+
|
|
156
|
+
# Verify added
|
|
157
|
+
- assertVisible: "Added to cart"
|
|
158
|
+
|
|
159
|
+
# Go back to continue shopping or proceed
|
|
160
|
+
- back
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Using with Login Subflow
|
|
164
|
+
|
|
165
|
+
```yaml
|
|
166
|
+
appId: com.example.shop
|
|
167
|
+
name: "Complete purchase flow from login to confirmation"
|
|
168
|
+
tags:
|
|
169
|
+
- e2e
|
|
170
|
+
- purchase
|
|
171
|
+
env:
|
|
172
|
+
EMAIL: customer@example.com
|
|
173
|
+
PASSWORD: CustomerPass123!
|
|
174
|
+
PRODUCT_NAME: "Blue Wireless Headphones"
|
|
175
|
+
QUANTITY: 1
|
|
176
|
+
CARD_NUMBER: "4111111111111111"
|
|
177
|
+
CARD_EXPIRY: "12/28"
|
|
178
|
+
CARD_CVV: "123"
|
|
179
|
+
---
|
|
180
|
+
- launchApp:
|
|
181
|
+
clearState: true
|
|
182
|
+
|
|
183
|
+
# Login
|
|
184
|
+
- runFlow:
|
|
185
|
+
file: ../subflows/login-steps.yaml
|
|
186
|
+
env:
|
|
187
|
+
EMAIL: ${EMAIL}
|
|
188
|
+
PASSWORD: ${PASSWORD}
|
|
189
|
+
|
|
190
|
+
# Add product to cart
|
|
191
|
+
- runFlow:
|
|
192
|
+
file: ../subflows/add-product-to-cart.yaml
|
|
193
|
+
env:
|
|
194
|
+
PRODUCT_NAME: ${PRODUCT_NAME}
|
|
195
|
+
QUANTITY: ${QUANTITY}
|
|
196
|
+
|
|
197
|
+
# Go to cart and checkout
|
|
198
|
+
- tapOn:
|
|
199
|
+
id: "cart_icon"
|
|
200
|
+
|
|
201
|
+
- runFlow: ../subflows/checkout-steps.yaml
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Handling Cart Edge Cases
|
|
205
|
+
|
|
206
|
+
```yaml
|
|
207
|
+
appId: com.example.shop
|
|
208
|
+
name: "Empty cart shows appropriate message"
|
|
209
|
+
tags:
|
|
210
|
+
- cart
|
|
211
|
+
- edge-case
|
|
212
|
+
---
|
|
213
|
+
- launchApp:
|
|
214
|
+
clearState: true
|
|
215
|
+
|
|
216
|
+
# Skip login for guest browsing
|
|
217
|
+
- runFlow:
|
|
218
|
+
when:
|
|
219
|
+
visible: "Continue as Guest"
|
|
220
|
+
commands:
|
|
221
|
+
- tapOn: "Continue as Guest"
|
|
222
|
+
|
|
223
|
+
# Navigate to cart
|
|
224
|
+
- tapOn:
|
|
225
|
+
id: "cart_icon"
|
|
226
|
+
|
|
227
|
+
# Verify empty cart state
|
|
228
|
+
- assertVisible: "Your cart is empty"
|
|
229
|
+
- assertVisible:
|
|
230
|
+
id: "start_shopping_button"
|
|
231
|
+
|
|
232
|
+
# Checkout button should be disabled or hidden
|
|
233
|
+
- assertNotVisible:
|
|
234
|
+
id: "checkout_button"
|
|
235
|
+
enabled: true
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Key Patterns Used
|
|
239
|
+
|
|
240
|
+
1. **Scroll before interact** - `scrollUntilVisible` before tapping buttons at bottom
|
|
241
|
+
2. **Form filling pattern** - Tap field → Input text → Next field
|
|
242
|
+
3. **Regex assertions** - `"Order #[0-9]+"` for dynamic content
|
|
243
|
+
4. **Conditional flows** - Handle optional UI elements
|
|
244
|
+
5. **Screenshots at key points** - Before and after critical actions
|
|
245
|
+
6. **Extended waits** - For payment processing (30s timeout)
|
|
246
|
+
7. **Relative selectors** - `below: "Payment Method"` for context
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Login Flow Example
|
|
2
|
+
|
|
3
|
+
This example demonstrates a complete login flow test with best practices.
|
|
4
|
+
|
|
5
|
+
## Flow File: login-test.yaml
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
appId: com.example.myapp
|
|
9
|
+
name: "User can login with valid credentials"
|
|
10
|
+
tags:
|
|
11
|
+
- smoke
|
|
12
|
+
- auth
|
|
13
|
+
- critical
|
|
14
|
+
env:
|
|
15
|
+
TEST_EMAIL: testuser@example.com
|
|
16
|
+
TEST_PASSWORD: SecurePass123!
|
|
17
|
+
---
|
|
18
|
+
# Start fresh
|
|
19
|
+
- launchApp:
|
|
20
|
+
clearState: true
|
|
21
|
+
|
|
22
|
+
# Wait for app to load
|
|
23
|
+
- assertVisible:
|
|
24
|
+
id: "splash_screen"
|
|
25
|
+
optional: true
|
|
26
|
+
- extendedWaitUntil:
|
|
27
|
+
visible:
|
|
28
|
+
id: "login_screen"
|
|
29
|
+
timeout: 10000
|
|
30
|
+
|
|
31
|
+
# Enter email
|
|
32
|
+
- tapOn:
|
|
33
|
+
id: "email_input"
|
|
34
|
+
- inputText: "${TEST_EMAIL}"
|
|
35
|
+
|
|
36
|
+
# Enter password
|
|
37
|
+
- tapOn:
|
|
38
|
+
id: "password_input"
|
|
39
|
+
- inputText: "${TEST_PASSWORD}"
|
|
40
|
+
|
|
41
|
+
# Hide keyboard if needed
|
|
42
|
+
- hideKeyboard
|
|
43
|
+
|
|
44
|
+
# Submit login
|
|
45
|
+
- tapOn:
|
|
46
|
+
id: "login_button"
|
|
47
|
+
enabled: true
|
|
48
|
+
|
|
49
|
+
# Wait for login to complete
|
|
50
|
+
- extendedWaitUntil:
|
|
51
|
+
visible:
|
|
52
|
+
id: "home_screen"
|
|
53
|
+
timeout: 15000
|
|
54
|
+
|
|
55
|
+
# Verify successful login
|
|
56
|
+
- assertVisible: "Welcome back"
|
|
57
|
+
- assertVisible:
|
|
58
|
+
id: "user_profile_icon"
|
|
59
|
+
|
|
60
|
+
# Take screenshot for report
|
|
61
|
+
- takeScreenshot: "login_success"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Subflow: login-steps.yaml
|
|
65
|
+
|
|
66
|
+
Reusable login component for other tests:
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
# subflows/login-steps.yaml
|
|
70
|
+
# Requires EMAIL and PASSWORD env variables from parent flow
|
|
71
|
+
|
|
72
|
+
- tapOn:
|
|
73
|
+
id: "email_input"
|
|
74
|
+
- inputText: "${EMAIL}"
|
|
75
|
+
|
|
76
|
+
- tapOn:
|
|
77
|
+
id: "password_input"
|
|
78
|
+
- inputText: "${PASSWORD}"
|
|
79
|
+
|
|
80
|
+
- hideKeyboard
|
|
81
|
+
|
|
82
|
+
- tapOn:
|
|
83
|
+
id: "login_button"
|
|
84
|
+
|
|
85
|
+
- extendedWaitUntil:
|
|
86
|
+
visible:
|
|
87
|
+
id: "home_screen"
|
|
88
|
+
timeout: 15000
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Using the Subflow
|
|
92
|
+
|
|
93
|
+
```yaml
|
|
94
|
+
appId: com.example.myapp
|
|
95
|
+
name: "User can access settings after login"
|
|
96
|
+
env:
|
|
97
|
+
EMAIL: testuser@example.com
|
|
98
|
+
PASSWORD: SecurePass123!
|
|
99
|
+
---
|
|
100
|
+
- launchApp:
|
|
101
|
+
clearState: true
|
|
102
|
+
|
|
103
|
+
# Reuse login subflow
|
|
104
|
+
- runFlow: ../subflows/login-steps.yaml
|
|
105
|
+
|
|
106
|
+
# Continue with test-specific steps
|
|
107
|
+
- tapOn:
|
|
108
|
+
id: "menu_button"
|
|
109
|
+
- tapOn: "Settings"
|
|
110
|
+
- assertVisible: "Account Settings"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Handling Login Errors
|
|
114
|
+
|
|
115
|
+
```yaml
|
|
116
|
+
appId: com.example.myapp
|
|
117
|
+
name: "Invalid login shows error message"
|
|
118
|
+
tags:
|
|
119
|
+
- auth
|
|
120
|
+
- negative
|
|
121
|
+
env:
|
|
122
|
+
INVALID_EMAIL: wrong@example.com
|
|
123
|
+
INVALID_PASSWORD: wrongpassword
|
|
124
|
+
---
|
|
125
|
+
- launchApp:
|
|
126
|
+
clearState: true
|
|
127
|
+
|
|
128
|
+
- tapOn:
|
|
129
|
+
id: "email_input"
|
|
130
|
+
- inputText: "${INVALID_EMAIL}"
|
|
131
|
+
|
|
132
|
+
- tapOn:
|
|
133
|
+
id: "password_input"
|
|
134
|
+
- inputText: "${INVALID_PASSWORD}"
|
|
135
|
+
|
|
136
|
+
- tapOn:
|
|
137
|
+
id: "login_button"
|
|
138
|
+
|
|
139
|
+
# Verify error is shown
|
|
140
|
+
- assertVisible:
|
|
141
|
+
text: "Invalid email or password"
|
|
142
|
+
- assertVisible:
|
|
143
|
+
id: "error_banner"
|
|
144
|
+
|
|
145
|
+
# Verify still on login screen
|
|
146
|
+
- assertVisible:
|
|
147
|
+
id: "login_screen"
|
|
148
|
+
|
|
149
|
+
# Verify login button still available (not navigated away)
|
|
150
|
+
- assertVisible:
|
|
151
|
+
id: "login_button"
|
|
152
|
+
enabled: true
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Best Practices Applied
|
|
156
|
+
|
|
157
|
+
1. **Clear state** - Start fresh with `clearState: true`
|
|
158
|
+
2. **Use IDs** - `id: "login_button"` instead of text
|
|
159
|
+
3. **Environment variables** - Credentials in `env` section
|
|
160
|
+
4. **Explicit waits** - `extendedWaitUntil` for async operations
|
|
161
|
+
5. **Screenshots** - Capture proof of success
|
|
162
|
+
6. **Subflows** - Reusable login component
|
|
163
|
+
7. **Meaningful tags** - `smoke`, `auth`, `critical`
|
|
164
|
+
8. **Descriptive names** - Clear test intent
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# Project Structure Template
|
|
2
|
+
|
|
3
|
+
Recommended Maestro test project structure for maintainable test suites.
|
|
4
|
+
|
|
5
|
+
## Directory Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
project-root/
|
|
9
|
+
├── .maestro/
|
|
10
|
+
│ ├── config.yaml # Workspace configuration
|
|
11
|
+
│ ├── flows/ # Test flows (runnable tests)
|
|
12
|
+
│ │ ├── auth/
|
|
13
|
+
│ │ │ ├── login.yaml
|
|
14
|
+
│ │ │ ├── logout.yaml
|
|
15
|
+
│ │ │ ├── forgot-password.yaml
|
|
16
|
+
│ │ │ └── register.yaml
|
|
17
|
+
│ │ ├── onboarding/
|
|
18
|
+
│ │ │ ├── first-time-user.yaml
|
|
19
|
+
│ │ │ └── skip-onboarding.yaml
|
|
20
|
+
│ │ ├── checkout/
|
|
21
|
+
│ │ │ ├── guest-checkout.yaml
|
|
22
|
+
│ │ │ ├── member-checkout.yaml
|
|
23
|
+
│ │ │ └── failed-payment.yaml
|
|
24
|
+
│ │ └── profile/
|
|
25
|
+
│ │ ├── edit-profile.yaml
|
|
26
|
+
│ │ └── change-password.yaml
|
|
27
|
+
│ ├── subflows/ # Reusable components (not run directly)
|
|
28
|
+
│ │ ├── auth/
|
|
29
|
+
│ │ │ ├── login-steps.yaml
|
|
30
|
+
│ │ │ ├── logout-steps.yaml
|
|
31
|
+
│ │ │ └── fill-registration-form.yaml
|
|
32
|
+
│ │ ├── navigation/
|
|
33
|
+
│ │ │ ├── go-to-home.yaml
|
|
34
|
+
│ │ │ ├── go-to-cart.yaml
|
|
35
|
+
│ │ │ ├── go-to-profile.yaml
|
|
36
|
+
│ │ │ └── go-to-settings.yaml
|
|
37
|
+
│ │ ├── common/
|
|
38
|
+
│ │ │ ├── dismiss-popups.yaml
|
|
39
|
+
│ │ │ ├── accept-cookies.yaml
|
|
40
|
+
│ │ │ └── handle-permissions.yaml
|
|
41
|
+
│ │ └── cart/
|
|
42
|
+
│ │ ├── add-to-cart.yaml
|
|
43
|
+
│ │ ├── remove-from-cart.yaml
|
|
44
|
+
│ │ └── update-quantity.yaml
|
|
45
|
+
│ └── scripts/ # JavaScript helpers
|
|
46
|
+
│ ├── data-generators.js
|
|
47
|
+
│ ├── date-helpers.js
|
|
48
|
+
│ └── api-helpers.js
|
|
49
|
+
├── test-results/ # Output directory (gitignored)
|
|
50
|
+
│ ├── screenshots/
|
|
51
|
+
│ ├── recordings/
|
|
52
|
+
│ └── reports/
|
|
53
|
+
└── .gitignore
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## config.yaml
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
# .maestro/config.yaml
|
|
60
|
+
|
|
61
|
+
# Include all flows, exclude work-in-progress
|
|
62
|
+
flows:
|
|
63
|
+
- 'flows/**/*.yaml'
|
|
64
|
+
- '!flows/**/*-wip.yaml'
|
|
65
|
+
- '!flows/**/*.draft.yaml'
|
|
66
|
+
|
|
67
|
+
# Default tags to include/exclude
|
|
68
|
+
includeTags: [] # Empty = include all
|
|
69
|
+
excludeTags:
|
|
70
|
+
- flaky
|
|
71
|
+
- manual
|
|
72
|
+
|
|
73
|
+
# Execution order for dependent tests
|
|
74
|
+
executionOrder:
|
|
75
|
+
continueOnFailure: false # Stop on first failure for CI
|
|
76
|
+
flowsOrder:
|
|
77
|
+
- flows/auth/login.yaml # Login first
|
|
78
|
+
- flows/onboarding/*.yaml # Then onboarding
|
|
79
|
+
|
|
80
|
+
# Output directory
|
|
81
|
+
testOutputDir: ../test-results
|
|
82
|
+
|
|
83
|
+
# Platform settings
|
|
84
|
+
platform:
|
|
85
|
+
ios:
|
|
86
|
+
disableAnimations: true
|
|
87
|
+
snapshotKeyHonorModalViews: false
|
|
88
|
+
android:
|
|
89
|
+
disableAnimations: true
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## PR-specific config: pr-config.yaml
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
# .maestro/pr-config.yaml
|
|
96
|
+
# Faster subset for pull request checks
|
|
97
|
+
|
|
98
|
+
flows:
|
|
99
|
+
- 'flows/**/*.yaml'
|
|
100
|
+
|
|
101
|
+
includeTags:
|
|
102
|
+
- smoke
|
|
103
|
+
- critical
|
|
104
|
+
|
|
105
|
+
excludeTags:
|
|
106
|
+
- slow
|
|
107
|
+
- flaky
|
|
108
|
+
|
|
109
|
+
executionOrder:
|
|
110
|
+
continueOnFailure: true # Run all tests, report failures
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Subflow Template
|
|
114
|
+
|
|
115
|
+
```yaml
|
|
116
|
+
# .maestro/subflows/auth/login-steps.yaml
|
|
117
|
+
#
|
|
118
|
+
# Reusable login component
|
|
119
|
+
# Required env: EMAIL, PASSWORD
|
|
120
|
+
# Assumes: App is launched and on login screen
|
|
121
|
+
|
|
122
|
+
- tapOn:
|
|
123
|
+
id: "email_input"
|
|
124
|
+
- inputText: "${EMAIL}"
|
|
125
|
+
|
|
126
|
+
- tapOn:
|
|
127
|
+
id: "password_input"
|
|
128
|
+
- inputText: "${PASSWORD}"
|
|
129
|
+
|
|
130
|
+
- hideKeyboard
|
|
131
|
+
|
|
132
|
+
- tapOn:
|
|
133
|
+
id: "login_button"
|
|
134
|
+
|
|
135
|
+
- extendedWaitUntil:
|
|
136
|
+
visible:
|
|
137
|
+
id: "home_screen"
|
|
138
|
+
timeout: 15000
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Flow Template
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
# .maestro/flows/auth/login.yaml
|
|
145
|
+
|
|
146
|
+
appId: com.example.myapp
|
|
147
|
+
name: "User can login with valid credentials"
|
|
148
|
+
tags:
|
|
149
|
+
- auth
|
|
150
|
+
- smoke
|
|
151
|
+
- critical
|
|
152
|
+
env:
|
|
153
|
+
EMAIL: testuser@example.com
|
|
154
|
+
PASSWORD: TestPass123!
|
|
155
|
+
---
|
|
156
|
+
# Setup
|
|
157
|
+
- launchApp:
|
|
158
|
+
clearState: true
|
|
159
|
+
|
|
160
|
+
# Handle any initial popups
|
|
161
|
+
- runFlow:
|
|
162
|
+
when:
|
|
163
|
+
visible: "Allow Notifications"
|
|
164
|
+
file: ../../subflows/common/handle-permissions.yaml
|
|
165
|
+
|
|
166
|
+
# Execute login
|
|
167
|
+
- runFlow:
|
|
168
|
+
file: ../../subflows/auth/login-steps.yaml
|
|
169
|
+
|
|
170
|
+
# Verify success
|
|
171
|
+
- assertVisible: "Welcome"
|
|
172
|
+
- assertVisible:
|
|
173
|
+
id: "home_screen"
|
|
174
|
+
|
|
175
|
+
# Capture result
|
|
176
|
+
- takeScreenshot: "login_success"
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## .gitignore
|
|
180
|
+
|
|
181
|
+
```gitignore
|
|
182
|
+
# Maestro test outputs
|
|
183
|
+
test-results/
|
|
184
|
+
*.mp4
|
|
185
|
+
*.png
|
|
186
|
+
|
|
187
|
+
# Local environment overrides
|
|
188
|
+
.maestro/config.local.yaml
|
|
189
|
+
|
|
190
|
+
# IDE
|
|
191
|
+
.idea/
|
|
192
|
+
.vscode/
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Naming Conventions
|
|
196
|
+
|
|
197
|
+
| Type | Convention | Example |
|
|
198
|
+
|------|------------|---------|
|
|
199
|
+
| Flows | `action-subject.yaml` | `login.yaml`, `add-to-cart.yaml` |
|
|
200
|
+
| Subflows | `action-steps.yaml` | `login-steps.yaml`, `checkout-steps.yaml` |
|
|
201
|
+
| Directories | lowercase, plural | `flows/`, `subflows/`, `scripts/` |
|
|
202
|
+
| Tags | lowercase, hyphenated | `smoke`, `e2e`, `auth`, `critical` |
|
|
203
|
+
|
|
204
|
+
## Tag Strategy
|
|
205
|
+
|
|
206
|
+
```yaml
|
|
207
|
+
# Execution tags
|
|
208
|
+
- smoke # Quick sanity tests (~5 min)
|
|
209
|
+
- e2e # Full end-to-end flows
|
|
210
|
+
- critical # Must pass for release
|
|
211
|
+
|
|
212
|
+
# Feature tags
|
|
213
|
+
- auth # Authentication flows
|
|
214
|
+
- checkout # Purchase flows
|
|
215
|
+
- profile # User profile tests
|
|
216
|
+
|
|
217
|
+
# Filter tags
|
|
218
|
+
- flaky # Known flaky tests
|
|
219
|
+
- slow # Long-running tests
|
|
220
|
+
- manual # Require manual intervention
|
|
221
|
+
- wip # Work in progress
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Running Tests
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
# All tests
|
|
228
|
+
maestro test .maestro/flows/
|
|
229
|
+
|
|
230
|
+
# With workspace config
|
|
231
|
+
maestro test --config .maestro/config.yaml .maestro/flows/
|
|
232
|
+
|
|
233
|
+
# PR config (smoke tests only)
|
|
234
|
+
maestro test --config .maestro/pr-config.yaml .maestro/flows/
|
|
235
|
+
|
|
236
|
+
# Specific feature
|
|
237
|
+
maestro test .maestro/flows/auth/
|
|
238
|
+
|
|
239
|
+
# By tags
|
|
240
|
+
maestro test --include-tags=smoke,critical .maestro/flows/
|
|
241
|
+
maestro test --exclude-tags=flaky,slow .maestro/flows/
|
|
242
|
+
|
|
243
|
+
# Continuous mode for development
|
|
244
|
+
maestro test --continuous .maestro/flows/auth/login.yaml
|
|
245
|
+
```
|