@deriv-com/fe-mcp-servers 0.0.11 → 0.0.12
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/maestro-ai/README.md +269 -0
- package/dist/maestro-ai/mcp-server.js +760 -1
- package/package.json +1 -1
|
@@ -147,6 +147,37 @@ Returns common Maestro patterns with "when to use" guidance.
|
|
|
147
147
|
- Pattern YAML code
|
|
148
148
|
- Usage guidance for each pattern
|
|
149
149
|
|
|
150
|
+
### `maestro_ensure_installed` Tool
|
|
151
|
+
|
|
152
|
+
⚠️ **PREREQUISITE**: Call this FIRST before using `maestro_write_test` or `maestro_run_all_tests`!
|
|
153
|
+
|
|
154
|
+
Checks if Maestro CLI is installed and automatically installs it if missing.
|
|
155
|
+
|
|
156
|
+
**When to Call:**
|
|
157
|
+
|
|
158
|
+
- ALWAYS before `maestro_write_test` (when execute=true)
|
|
159
|
+
- ALWAYS before `maestro_run_all_tests`
|
|
160
|
+
- When setting up a new development environment
|
|
161
|
+
|
|
162
|
+
**Parameters:**
|
|
163
|
+
|
|
164
|
+
- `autoInstall` (boolean, optional): Whether to automatically install if not found (default: true)
|
|
165
|
+
- `forceReinstall` (boolean, optional): Whether to force reinstall even if already installed (default: false)
|
|
166
|
+
|
|
167
|
+
**Returns:**
|
|
168
|
+
|
|
169
|
+
- `installed`: boolean - Whether Maestro is now installed
|
|
170
|
+
- `version`: string - Installed version (if available)
|
|
171
|
+
- `wasInstalled`: boolean - Whether this call performed the installation
|
|
172
|
+
- `message`: string - Status message
|
|
173
|
+
- `details`: object - Additional info (path, install method)
|
|
174
|
+
|
|
175
|
+
**Installation Method:**
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
curl -fsSL "https://get.maestro.mobile.dev" | bash
|
|
179
|
+
```
|
|
180
|
+
|
|
150
181
|
### `maestro_analyze_changes` Tool
|
|
151
182
|
|
|
152
183
|
Automatically analyzes actual git changes in a repository and provides test recommendations.
|
|
@@ -164,6 +195,122 @@ Automatically analyzes actual git changes in a repository and provides test reco
|
|
|
164
195
|
- Interactive elements detected (onClick, Button, Form, etc.)
|
|
165
196
|
- Suggested flow type and whether to create tests
|
|
166
197
|
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## 🚀 Test Execution Tools
|
|
201
|
+
|
|
202
|
+
### `maestro_write_test` Tool
|
|
203
|
+
|
|
204
|
+
Writes a Maestro test YAML file to disk.
|
|
205
|
+
|
|
206
|
+
**Parameters:**
|
|
207
|
+
|
|
208
|
+
- `yaml` (string, required): The YAML content to write
|
|
209
|
+
- `fileName` (string, required): Name of the file (e.g., "login_test.yaml")
|
|
210
|
+
- `directory` (string, optional): Target directory (default: "maestro")
|
|
211
|
+
- `basePath` (string, optional): Project base path (default: current directory)
|
|
212
|
+
|
|
213
|
+
**Returns:**
|
|
214
|
+
|
|
215
|
+
- `success`: boolean
|
|
216
|
+
- `filePath`: Path where file was written
|
|
217
|
+
- `message`: Status message
|
|
218
|
+
|
|
219
|
+
### `maestro_execute_test` Tool
|
|
220
|
+
|
|
221
|
+
Executes a single Maestro test file using the Maestro CLI.
|
|
222
|
+
|
|
223
|
+
**Prerequisites:**
|
|
224
|
+
|
|
225
|
+
- Maestro CLI must be installed (`curl -fsSL https://get.maestro.dev | bash`)
|
|
226
|
+
- A device/simulator must be running
|
|
227
|
+
|
|
228
|
+
**Parameters:**
|
|
229
|
+
|
|
230
|
+
- `flowFile` (string, required): Path to the .yaml flow file
|
|
231
|
+
- `deviceId` (string, optional): Target device ID
|
|
232
|
+
- `env` (object, optional): Environment variables to pass
|
|
233
|
+
- `timeout` (number, optional): Execution timeout in ms (default: 120000)
|
|
234
|
+
|
|
235
|
+
**Returns:**
|
|
236
|
+
|
|
237
|
+
- `success`: boolean
|
|
238
|
+
- `output`: Test output/logs
|
|
239
|
+
- `exitCode`: Process exit code
|
|
240
|
+
- `duration`: Execution time in ms
|
|
241
|
+
|
|
242
|
+
### `maestro_execute_tests_sequential` Tool
|
|
243
|
+
|
|
244
|
+
Executes multiple Maestro test files sequentially with aggregated results.
|
|
245
|
+
|
|
246
|
+
**Parameters:**
|
|
247
|
+
|
|
248
|
+
- `flowFiles` (array, required): Array of flow file paths to execute
|
|
249
|
+
- `deviceId` (string, optional): Target device ID
|
|
250
|
+
- `env` (object, optional): Environment variables to pass
|
|
251
|
+
- `stopOnFailure` (boolean, optional): Stop on first failure (default: false)
|
|
252
|
+
|
|
253
|
+
**Returns:**
|
|
254
|
+
|
|
255
|
+
- `success`: boolean (true if all tests passed)
|
|
256
|
+
- `results`: Array of individual test results
|
|
257
|
+
- `summary`: Aggregated statistics (passed, failed, skipped, duration)
|
|
258
|
+
|
|
259
|
+
### `maestro_generate_and_run` Tool
|
|
260
|
+
|
|
261
|
+
All-in-one workflow: Generate, write, and execute a Maestro test.
|
|
262
|
+
|
|
263
|
+
**Parameters:**
|
|
264
|
+
|
|
265
|
+
- `feature` (string, required): Feature being tested
|
|
266
|
+
- `action` (string, required): Action being tested
|
|
267
|
+
- `flowType` (string, required): Flow type (auth, form, sidebar, modal, navigation, extended)
|
|
268
|
+
- `basePath` (string, optional): Project base path
|
|
269
|
+
- `deviceId` (string, optional): Target device ID
|
|
270
|
+
- `env` (object, optional): Environment variables
|
|
271
|
+
- `execute` (boolean, optional): Execute after writing (default: true)
|
|
272
|
+
- `changedElements` (array, optional): List of changed UI elements
|
|
273
|
+
- `existingTests` (array, optional): List of existing related test files
|
|
274
|
+
|
|
275
|
+
**Returns:**
|
|
276
|
+
|
|
277
|
+
- `generation`: Generated template and guidelines
|
|
278
|
+
- `write`: File write result
|
|
279
|
+
- `execution`: Test execution results (if execute=true)
|
|
280
|
+
- `summary`: Human-readable summary
|
|
281
|
+
|
|
282
|
+
### `maestro_discover_tests` Tool
|
|
283
|
+
|
|
284
|
+
Discovers all Maestro test files in a directory.
|
|
285
|
+
|
|
286
|
+
**Parameters:**
|
|
287
|
+
|
|
288
|
+
- `directory` (string, optional): Directory to search (default: "maestro")
|
|
289
|
+
- `basePath` (string, optional): Project base path
|
|
290
|
+
|
|
291
|
+
**Returns:**
|
|
292
|
+
|
|
293
|
+
- `files`: Array of file paths
|
|
294
|
+
- `count`: Number of files found
|
|
295
|
+
|
|
296
|
+
### `maestro_run_all_tests` Tool
|
|
297
|
+
|
|
298
|
+
Runs all Maestro tests in a directory sequentially.
|
|
299
|
+
|
|
300
|
+
**Parameters:**
|
|
301
|
+
|
|
302
|
+
- `directory` (string, optional): Directory containing tests (default: "maestro")
|
|
303
|
+
- `basePath` (string, optional): Project base path
|
|
304
|
+
- `deviceId` (string, optional): Target device ID
|
|
305
|
+
- `env` (object, optional): Environment variables
|
|
306
|
+
- `stopOnFailure` (boolean, optional): Stop on first failure (default: false)
|
|
307
|
+
|
|
308
|
+
**Returns:**
|
|
309
|
+
|
|
310
|
+
- `discovery`: List of discovered test files
|
|
311
|
+
- `execution`: Results with pass/fail status for each test
|
|
312
|
+
- `summary`: Aggregated statistics
|
|
313
|
+
|
|
167
314
|
## 🎯 Usage Examples
|
|
168
315
|
|
|
169
316
|
Once configured in your MCP client, you can use the tools in your conversations:
|
|
@@ -237,6 +384,58 @@ env:
|
|
|
237
384
|
3. **Quick Reference**: `maestro_cheat_sheet()`
|
|
238
385
|
4. **Pattern Lookup**: `maestro_pattern(patternType: "login")`
|
|
239
386
|
5. **UI Discovery**: `maestro_ui_inspection()`
|
|
387
|
+
6. **Pre-check Installation**: `maestro_ensure_installed()` - Verifies Maestro CLI is ready
|
|
388
|
+
|
|
389
|
+
### Test Execution Use Cases
|
|
390
|
+
|
|
391
|
+
6. **Generate & Run Test**:
|
|
392
|
+
|
|
393
|
+
```
|
|
394
|
+
maestro_generate_and_run(
|
|
395
|
+
feature: "Login",
|
|
396
|
+
action: "OAuth flow",
|
|
397
|
+
flowType: "auth",
|
|
398
|
+
basePath: "/path/to/project"
|
|
399
|
+
)
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
7. **Run All Tests in Directory**:
|
|
403
|
+
|
|
404
|
+
```
|
|
405
|
+
maestro_run_all_tests(
|
|
406
|
+
directory: "maestro",
|
|
407
|
+
basePath: "/path/to/project",
|
|
408
|
+
stopOnFailure: false
|
|
409
|
+
)
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
8. **Execute Specific Tests Sequentially**:
|
|
413
|
+
|
|
414
|
+
```
|
|
415
|
+
maestro_execute_tests_sequential(
|
|
416
|
+
flowFiles: ["maestro/login.yaml", "maestro/checkout.yaml"],
|
|
417
|
+
env: { "BASE_URL": "https://staging.example.com" }
|
|
418
|
+
)
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
9. **Write Test File Only (No Execution)**:
|
|
422
|
+
|
|
423
|
+
```
|
|
424
|
+
maestro_write_test(
|
|
425
|
+
yaml: "<generated yaml content>",
|
|
426
|
+
fileName: "user_settings_reset.yaml",
|
|
427
|
+
basePath: "/path/to/project"
|
|
428
|
+
)
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
10. **Discover Available Tests**:
|
|
432
|
+
|
|
433
|
+
```
|
|
434
|
+
maestro_discover_tests(
|
|
435
|
+
directory: "maestro",
|
|
436
|
+
basePath: "/path/to/project"
|
|
437
|
+
)
|
|
438
|
+
```
|
|
240
439
|
|
|
241
440
|
## Flow Types
|
|
242
441
|
|
|
@@ -259,6 +458,73 @@ env:
|
|
|
259
458
|
| `lastCommit` | Most recent commit | `git diff HEAD~1` |
|
|
260
459
|
| `branch` | Compare to base branch | `git diff {baseBranch}...HEAD` |
|
|
261
460
|
|
|
461
|
+
## 🔄 Complete Workflow Example
|
|
462
|
+
|
|
463
|
+
### ⚠️ IMPORTANT: Always Check Installation First!
|
|
464
|
+
|
|
465
|
+
When a user asks to "generate and run test cases" or use Maestro, follow this order:
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
# Step 0: ALWAYS check Maestro installation first!
|
|
469
|
+
maestro_ensure_installed()
|
|
470
|
+
|
|
471
|
+
# If not installed, the tool will automatically install it via:
|
|
472
|
+
# curl -fsSL "https://get.maestro.mobile.dev" | bash
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### End-to-End: Analyze Changes → Generate → Execute
|
|
476
|
+
|
|
477
|
+
```bash
|
|
478
|
+
# Step 0: Ensure Maestro is installed (REQUIRED)
|
|
479
|
+
maestro_ensure_installed()
|
|
480
|
+
|
|
481
|
+
# Step 1: Analyze git changes to see what needs testing
|
|
482
|
+
maestro_analyze_changes(repoPath: "/path/to/project", changeType: "staged")
|
|
483
|
+
|
|
484
|
+
# Output shows: Settings.tsx changed with onClick handler
|
|
485
|
+
# Suggested flow type: form
|
|
486
|
+
|
|
487
|
+
# Step 2: Generate and run the test in one call
|
|
488
|
+
maestro_generate_and_run(
|
|
489
|
+
feature: "User Settings",
|
|
490
|
+
action: "Reset defaults",
|
|
491
|
+
flowType: "form",
|
|
492
|
+
basePath: "/path/to/project",
|
|
493
|
+
env: { "BASE_URL": "https://localhost:8443" }
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
# Output:
|
|
497
|
+
# - File written to: /path/to/project/maestro/user_settings_reset_defaults.yaml
|
|
498
|
+
# - Test executed: ✅ Passed (duration: 12.5s)
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
### Sequential Test Suite Execution
|
|
502
|
+
|
|
503
|
+
```bash
|
|
504
|
+
# Step 0: Ensure Maestro is installed (REQUIRED)
|
|
505
|
+
maestro_ensure_installed()
|
|
506
|
+
|
|
507
|
+
# Discover all tests
|
|
508
|
+
maestro_discover_tests(basePath: "/path/to/project")
|
|
509
|
+
# Output: Found 5 test files
|
|
510
|
+
|
|
511
|
+
# Run all tests with stop-on-failure
|
|
512
|
+
maestro_run_all_tests(
|
|
513
|
+
basePath: "/path/to/project",
|
|
514
|
+
stopOnFailure: true,
|
|
515
|
+
env: { "BASE_URL": "https://staging.example.com" }
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
# Output:
|
|
519
|
+
# ✅ login_basic.yaml (8.2s)
|
|
520
|
+
# ✅ user_settings_open.yaml (5.1s)
|
|
521
|
+
# ❌ checkout_submit.yaml (12.3s) - FAILED
|
|
522
|
+
# ⏭️ payment_confirm.yaml - SKIPPED
|
|
523
|
+
# ⏭️ logout.yaml - SKIPPED
|
|
524
|
+
#
|
|
525
|
+
# Summary: 2 passed, 1 failed, 2 skipped
|
|
526
|
+
```
|
|
527
|
+
|
|
262
528
|
## 📊 Examples
|
|
263
529
|
|
|
264
530
|
### Auth Flow Template
|
|
@@ -280,6 +546,9 @@ env:
|
|
|
280
546
|
- tapOn:
|
|
281
547
|
text: "Next"
|
|
282
548
|
repeat: 3
|
|
549
|
+
- tapOn:
|
|
550
|
+
text: "Got it"
|
|
551
|
+
repeat: 3
|
|
283
552
|
- tapOn:
|
|
284
553
|
text: "Log in"
|
|
285
554
|
- inputText: ${USER_EMAIL}
|
|
@@ -18492,7 +18492,17 @@ var StdioServerTransport = class {
|
|
|
18492
18492
|
};
|
|
18493
18493
|
|
|
18494
18494
|
// maestro-ai/src/mcp.js
|
|
18495
|
-
import { execSync } from "child_process";
|
|
18495
|
+
import { execSync, spawn } from "child_process";
|
|
18496
|
+
import {
|
|
18497
|
+
writeFileSync,
|
|
18498
|
+
existsSync,
|
|
18499
|
+
mkdirSync,
|
|
18500
|
+
readFileSync,
|
|
18501
|
+
openSync,
|
|
18502
|
+
closeSync,
|
|
18503
|
+
fsyncSync
|
|
18504
|
+
} from "fs";
|
|
18505
|
+
import { join, dirname } from "path";
|
|
18496
18506
|
var FLOW_TYPES = {
|
|
18497
18507
|
auth: {
|
|
18498
18508
|
description: "Login, signup, or password reset flows",
|
|
@@ -18520,6 +18530,156 @@ var FLOW_TYPES = {
|
|
|
18520
18530
|
requiresOnboarding: false
|
|
18521
18531
|
}
|
|
18522
18532
|
};
|
|
18533
|
+
function isMaestroInstalled() {
|
|
18534
|
+
try {
|
|
18535
|
+
execSync("which maestro", { encoding: "utf-8", stdio: "pipe" });
|
|
18536
|
+
return true;
|
|
18537
|
+
} catch {
|
|
18538
|
+
return false;
|
|
18539
|
+
}
|
|
18540
|
+
}
|
|
18541
|
+
function installMaestro() {
|
|
18542
|
+
const installCmd = 'curl -fsSL "https://get.maestro.mobile.dev" | bash';
|
|
18543
|
+
try {
|
|
18544
|
+
try {
|
|
18545
|
+
execSync(installCmd, {
|
|
18546
|
+
encoding: "utf-8",
|
|
18547
|
+
stdio: "pipe",
|
|
18548
|
+
shell: "/bin/bash",
|
|
18549
|
+
timeout: 12e4
|
|
18550
|
+
// 2 minute timeout for install
|
|
18551
|
+
});
|
|
18552
|
+
} catch (curlError) {
|
|
18553
|
+
const curlOutput = curlError.stderr || curlError.stdout || curlError.message;
|
|
18554
|
+
throw new Error(`Installation failed: ${curlOutput}`);
|
|
18555
|
+
}
|
|
18556
|
+
if (isMaestroInstalled()) {
|
|
18557
|
+
return {
|
|
18558
|
+
success: true,
|
|
18559
|
+
message: "\u2705 Maestro CLI installed successfully!",
|
|
18560
|
+
method: "curl"
|
|
18561
|
+
};
|
|
18562
|
+
}
|
|
18563
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE;
|
|
18564
|
+
const maestroBin = join(homeDir, ".maestro", "bin");
|
|
18565
|
+
process.env.PATH = `${maestroBin}:${process.env.PATH}`;
|
|
18566
|
+
try {
|
|
18567
|
+
execSync(`${join(maestroBin, "maestro")} --version`, {
|
|
18568
|
+
encoding: "utf-8",
|
|
18569
|
+
stdio: "pipe"
|
|
18570
|
+
});
|
|
18571
|
+
return {
|
|
18572
|
+
success: true,
|
|
18573
|
+
message: `\u2705 Maestro CLI installed successfully! Added ${maestroBin} to PATH.`,
|
|
18574
|
+
method: "curl"
|
|
18575
|
+
};
|
|
18576
|
+
} catch {
|
|
18577
|
+
return {
|
|
18578
|
+
success: false,
|
|
18579
|
+
message: `\u26A0\uFE0F Maestro installed but not in PATH. Add ${maestroBin} to your PATH and restart your terminal.`,
|
|
18580
|
+
method: "curl"
|
|
18581
|
+
};
|
|
18582
|
+
}
|
|
18583
|
+
} catch (error2) {
|
|
18584
|
+
return {
|
|
18585
|
+
success: false,
|
|
18586
|
+
message: `\u274C Failed to install Maestro: ${error2.message}
|
|
18587
|
+
|
|
18588
|
+
Manual install:
|
|
18589
|
+
${installCmd}`,
|
|
18590
|
+
method: "curl"
|
|
18591
|
+
};
|
|
18592
|
+
}
|
|
18593
|
+
}
|
|
18594
|
+
function ensureMaestroAvailable() {
|
|
18595
|
+
if (isMaestroInstalled()) {
|
|
18596
|
+
return {
|
|
18597
|
+
available: true,
|
|
18598
|
+
message: "Maestro CLI is available",
|
|
18599
|
+
installed: false
|
|
18600
|
+
};
|
|
18601
|
+
}
|
|
18602
|
+
const installResult = installMaestro();
|
|
18603
|
+
return {
|
|
18604
|
+
available: installResult.success,
|
|
18605
|
+
message: installResult.message,
|
|
18606
|
+
installed: installResult.success
|
|
18607
|
+
};
|
|
18608
|
+
}
|
|
18609
|
+
function getMaestroVersion() {
|
|
18610
|
+
try {
|
|
18611
|
+
const version2 = execSync("maestro --version", {
|
|
18612
|
+
encoding: "utf-8",
|
|
18613
|
+
stdio: "pipe"
|
|
18614
|
+
}).trim();
|
|
18615
|
+
return version2;
|
|
18616
|
+
} catch {
|
|
18617
|
+
return null;
|
|
18618
|
+
}
|
|
18619
|
+
}
|
|
18620
|
+
function ensureMaestroInstalled(options = {}) {
|
|
18621
|
+
const { autoInstall = true, forceReinstall = false } = options;
|
|
18622
|
+
const result = {
|
|
18623
|
+
installed: false,
|
|
18624
|
+
version: null,
|
|
18625
|
+
wasInstalled: false,
|
|
18626
|
+
message: "",
|
|
18627
|
+
details: {
|
|
18628
|
+
installMethod: null,
|
|
18629
|
+
path: null
|
|
18630
|
+
}
|
|
18631
|
+
};
|
|
18632
|
+
const alreadyInstalled = isMaestroInstalled();
|
|
18633
|
+
const currentVersion = alreadyInstalled ? getMaestroVersion() : null;
|
|
18634
|
+
if (alreadyInstalled && !forceReinstall) {
|
|
18635
|
+
result.installed = true;
|
|
18636
|
+
result.version = currentVersion;
|
|
18637
|
+
result.wasInstalled = false;
|
|
18638
|
+
result.message = `\u2705 Maestro CLI is already installed (${currentVersion || "version unknown"})`;
|
|
18639
|
+
try {
|
|
18640
|
+
const path = execSync("which maestro", {
|
|
18641
|
+
encoding: "utf-8",
|
|
18642
|
+
stdio: "pipe"
|
|
18643
|
+
}).trim();
|
|
18644
|
+
result.details.path = path;
|
|
18645
|
+
} catch {
|
|
18646
|
+
}
|
|
18647
|
+
return result;
|
|
18648
|
+
}
|
|
18649
|
+
if (!autoInstall) {
|
|
18650
|
+
result.installed = false;
|
|
18651
|
+
result.version = null;
|
|
18652
|
+
result.wasInstalled = false;
|
|
18653
|
+
result.message = `\u274C Maestro CLI is not installed. Set autoInstall: true to install automatically.`;
|
|
18654
|
+
result.details.installCommand = 'curl -fsSL "https://get.maestro.mobile.dev" | bash';
|
|
18655
|
+
return result;
|
|
18656
|
+
}
|
|
18657
|
+
const installResult = installMaestro();
|
|
18658
|
+
if (installResult.success) {
|
|
18659
|
+
const newVersion = getMaestroVersion();
|
|
18660
|
+
result.installed = true;
|
|
18661
|
+
result.version = newVersion;
|
|
18662
|
+
result.wasInstalled = true;
|
|
18663
|
+
result.message = installResult.message;
|
|
18664
|
+
result.details.installMethod = installResult.method;
|
|
18665
|
+
try {
|
|
18666
|
+
const path = execSync("which maestro", {
|
|
18667
|
+
encoding: "utf-8",
|
|
18668
|
+
stdio: "pipe"
|
|
18669
|
+
}).trim();
|
|
18670
|
+
result.details.path = path;
|
|
18671
|
+
} catch {
|
|
18672
|
+
}
|
|
18673
|
+
} else {
|
|
18674
|
+
result.installed = false;
|
|
18675
|
+
result.version = null;
|
|
18676
|
+
result.wasInstalled = false;
|
|
18677
|
+
result.message = installResult.message;
|
|
18678
|
+
result.details.installMethod = installResult.method;
|
|
18679
|
+
result.details.installCommand = 'curl -fsSL "https://get.maestro.mobile.dev" | bash';
|
|
18680
|
+
}
|
|
18681
|
+
return result;
|
|
18682
|
+
}
|
|
18523
18683
|
function getMaestroCheatSheet() {
|
|
18524
18684
|
const commands = [
|
|
18525
18685
|
{
|
|
@@ -19289,6 +19449,305 @@ ${analysis.shouldCreateTest ? `1. Review the changed UI files above
|
|
|
19289
19449
|
}
|
|
19290
19450
|
};
|
|
19291
19451
|
}
|
|
19452
|
+
function writeTestFile(options = {}) {
|
|
19453
|
+
const {
|
|
19454
|
+
yaml,
|
|
19455
|
+
fileName,
|
|
19456
|
+
directory = "maestro",
|
|
19457
|
+
basePath = process.cwd(),
|
|
19458
|
+
execute = true,
|
|
19459
|
+
deviceId = null,
|
|
19460
|
+
env = {},
|
|
19461
|
+
feature = "",
|
|
19462
|
+
action = "",
|
|
19463
|
+
flowType = null,
|
|
19464
|
+
changedElements = [],
|
|
19465
|
+
existingTests = []
|
|
19466
|
+
} = options;
|
|
19467
|
+
const generation = generateMaestroTest({
|
|
19468
|
+
feature,
|
|
19469
|
+
action,
|
|
19470
|
+
flowType,
|
|
19471
|
+
changedElements,
|
|
19472
|
+
existingTests
|
|
19473
|
+
});
|
|
19474
|
+
if (!yaml || !fileName) {
|
|
19475
|
+
return {
|
|
19476
|
+
success: false,
|
|
19477
|
+
filePath: null,
|
|
19478
|
+
message: "\u274C Missing required parameters: yaml and fileName are required"
|
|
19479
|
+
};
|
|
19480
|
+
}
|
|
19481
|
+
const normalizedFileName = fileName.endsWith(".yaml") ? fileName : `${fileName}.yaml`;
|
|
19482
|
+
const targetDir = join(basePath, directory);
|
|
19483
|
+
const filePath = join(targetDir, normalizedFileName);
|
|
19484
|
+
try {
|
|
19485
|
+
if (!existsSync(targetDir)) {
|
|
19486
|
+
mkdirSync(targetDir, { recursive: true });
|
|
19487
|
+
}
|
|
19488
|
+
writeFileSync(filePath, yaml, "utf-8");
|
|
19489
|
+
const fd = openSync(filePath, "r");
|
|
19490
|
+
fsyncSync(fd);
|
|
19491
|
+
closeSync(fd);
|
|
19492
|
+
const result = {
|
|
19493
|
+
success: true,
|
|
19494
|
+
filePath,
|
|
19495
|
+
message: `\u2705 Test file written successfully to: ${filePath}`,
|
|
19496
|
+
generation
|
|
19497
|
+
// Include test generation guidelines and instructions
|
|
19498
|
+
};
|
|
19499
|
+
if (execute) {
|
|
19500
|
+
const sleep = (ms) => {
|
|
19501
|
+
const end = Date.now() + ms;
|
|
19502
|
+
while (Date.now() < end) {
|
|
19503
|
+
}
|
|
19504
|
+
};
|
|
19505
|
+
sleep(500);
|
|
19506
|
+
const execution = executeTest({
|
|
19507
|
+
flowFile: filePath,
|
|
19508
|
+
deviceId,
|
|
19509
|
+
env
|
|
19510
|
+
});
|
|
19511
|
+
result.execution = execution;
|
|
19512
|
+
result.message += execution.success ? "\n\u2705 Test executed successfully!" : "\n\u274C Test execution failed.";
|
|
19513
|
+
}
|
|
19514
|
+
return result;
|
|
19515
|
+
} catch (error2) {
|
|
19516
|
+
return {
|
|
19517
|
+
success: false,
|
|
19518
|
+
filePath: null,
|
|
19519
|
+
message: `\u274C Failed to write test file: ${error2.message}`
|
|
19520
|
+
};
|
|
19521
|
+
}
|
|
19522
|
+
}
|
|
19523
|
+
function executeTest(options = {}) {
|
|
19524
|
+
const { flowFile, deviceId = null, env = {}, timeout = 12e4 } = options;
|
|
19525
|
+
if (!flowFile) {
|
|
19526
|
+
return {
|
|
19527
|
+
success: false,
|
|
19528
|
+
output: "\u274C Missing required parameter: flowFile",
|
|
19529
|
+
exitCode: 1,
|
|
19530
|
+
duration: 0
|
|
19531
|
+
};
|
|
19532
|
+
}
|
|
19533
|
+
const maestroCheck = ensureMaestroAvailable();
|
|
19534
|
+
if (!maestroCheck.available) {
|
|
19535
|
+
return {
|
|
19536
|
+
success: false,
|
|
19537
|
+
output: maestroCheck.message,
|
|
19538
|
+
exitCode: 1,
|
|
19539
|
+
duration: 0,
|
|
19540
|
+
maestroInstalled: false
|
|
19541
|
+
};
|
|
19542
|
+
}
|
|
19543
|
+
const args = ["test"];
|
|
19544
|
+
if (deviceId) {
|
|
19545
|
+
args.push("--device", deviceId);
|
|
19546
|
+
}
|
|
19547
|
+
Object.entries(env).forEach(([key, value]) => {
|
|
19548
|
+
args.push("-e", `${key}=${value}`);
|
|
19549
|
+
});
|
|
19550
|
+
args.push(flowFile);
|
|
19551
|
+
const command = `maestro ${args.join(" ")}`;
|
|
19552
|
+
const startTime = Date.now();
|
|
19553
|
+
try {
|
|
19554
|
+
const output = execSync(command, {
|
|
19555
|
+
encoding: "utf-8",
|
|
19556
|
+
timeout,
|
|
19557
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
19558
|
+
env: { ...process.env, ...env }
|
|
19559
|
+
});
|
|
19560
|
+
const duration3 = Date.now() - startTime;
|
|
19561
|
+
return {
|
|
19562
|
+
success: true,
|
|
19563
|
+
output: `${maestroCheck.installed ? "\u{1F4E6} " + maestroCheck.message + "\n\n" : ""}\u2705 Test passed!
|
|
19564
|
+
|
|
19565
|
+
Command: ${command}
|
|
19566
|
+
|
|
19567
|
+
${output}`,
|
|
19568
|
+
exitCode: 0,
|
|
19569
|
+
duration: duration3,
|
|
19570
|
+
command,
|
|
19571
|
+
maestroInstalled: maestroCheck.installed
|
|
19572
|
+
};
|
|
19573
|
+
} catch (error2) {
|
|
19574
|
+
const duration3 = Date.now() - startTime;
|
|
19575
|
+
const output = error2.stdout || error2.stderr || error2.message;
|
|
19576
|
+
return {
|
|
19577
|
+
success: false,
|
|
19578
|
+
output: `${maestroCheck.installed ? "\u{1F4E6} " + maestroCheck.message + "\n\n" : ""}\u274C Test failed!
|
|
19579
|
+
|
|
19580
|
+
Command: ${command}
|
|
19581
|
+
|
|
19582
|
+
${output}`,
|
|
19583
|
+
exitCode: error2.status || 1,
|
|
19584
|
+
duration: duration3,
|
|
19585
|
+
command,
|
|
19586
|
+
maestroInstalled: maestroCheck.installed
|
|
19587
|
+
};
|
|
19588
|
+
}
|
|
19589
|
+
}
|
|
19590
|
+
function executeTestsSequentially(options = {}) {
|
|
19591
|
+
const {
|
|
19592
|
+
flowFiles = [],
|
|
19593
|
+
deviceId = null,
|
|
19594
|
+
env = {},
|
|
19595
|
+
stopOnFailure = false
|
|
19596
|
+
} = options;
|
|
19597
|
+
if (!flowFiles || flowFiles.length === 0) {
|
|
19598
|
+
return {
|
|
19599
|
+
success: false,
|
|
19600
|
+
results: [],
|
|
19601
|
+
summary: {
|
|
19602
|
+
total: 0,
|
|
19603
|
+
passed: 0,
|
|
19604
|
+
failed: 0,
|
|
19605
|
+
skipped: 0,
|
|
19606
|
+
totalDuration: 0
|
|
19607
|
+
}
|
|
19608
|
+
};
|
|
19609
|
+
}
|
|
19610
|
+
const results = [];
|
|
19611
|
+
let stopped = false;
|
|
19612
|
+
for (const flowFile of flowFiles) {
|
|
19613
|
+
if (stopped) {
|
|
19614
|
+
results.push({
|
|
19615
|
+
flowFile,
|
|
19616
|
+
success: false,
|
|
19617
|
+
output: "\u23ED\uFE0F Skipped due to previous failure",
|
|
19618
|
+
exitCode: -1,
|
|
19619
|
+
duration: 0,
|
|
19620
|
+
skipped: true
|
|
19621
|
+
});
|
|
19622
|
+
continue;
|
|
19623
|
+
}
|
|
19624
|
+
const result = executeTest({ flowFile, deviceId, env });
|
|
19625
|
+
results.push({
|
|
19626
|
+
flowFile,
|
|
19627
|
+
...result,
|
|
19628
|
+
skipped: false
|
|
19629
|
+
});
|
|
19630
|
+
if (!result.success && stopOnFailure) {
|
|
19631
|
+
stopped = true;
|
|
19632
|
+
}
|
|
19633
|
+
}
|
|
19634
|
+
const passed = results.filter((r) => r.success).length;
|
|
19635
|
+
const failed = results.filter((r) => !r.success && !r.skipped).length;
|
|
19636
|
+
const skipped = results.filter((r) => r.skipped).length;
|
|
19637
|
+
const totalDuration = results.reduce((sum, r) => sum + r.duration, 0);
|
|
19638
|
+
return {
|
|
19639
|
+
success: failed === 0,
|
|
19640
|
+
results,
|
|
19641
|
+
summary: {
|
|
19642
|
+
total: flowFiles.length,
|
|
19643
|
+
passed,
|
|
19644
|
+
failed,
|
|
19645
|
+
skipped,
|
|
19646
|
+
totalDuration,
|
|
19647
|
+
report: `
|
|
19648
|
+
## \u{1F4CA} Test Execution Summary
|
|
19649
|
+
|
|
19650
|
+
| Metric | Value |
|
|
19651
|
+
|--------|-------|
|
|
19652
|
+
| Total Tests | ${flowFiles.length} |
|
|
19653
|
+
| \u2705 Passed | ${passed} |
|
|
19654
|
+
| \u274C Failed | ${failed} |
|
|
19655
|
+
| \u23ED\uFE0F Skipped | ${skipped} |
|
|
19656
|
+
| \u23F1\uFE0F Duration | ${(totalDuration / 1e3).toFixed(2)}s |
|
|
19657
|
+
|
|
19658
|
+
### Results by Flow:
|
|
19659
|
+
${results.map(
|
|
19660
|
+
(r) => `- ${r.success ? "\u2705" : r.skipped ? "\u23ED\uFE0F" : "\u274C"} \`${r.flowFile}\` (${(r.duration / 1e3).toFixed(2)}s)`
|
|
19661
|
+
).join("\n")}
|
|
19662
|
+
`
|
|
19663
|
+
}
|
|
19664
|
+
};
|
|
19665
|
+
}
|
|
19666
|
+
function discoverTestFiles(options = {}) {
|
|
19667
|
+
const { directory = "maestro", basePath = process.cwd() } = options;
|
|
19668
|
+
const targetDir = join(basePath, directory);
|
|
19669
|
+
if (!existsSync(targetDir)) {
|
|
19670
|
+
return {
|
|
19671
|
+
files: [],
|
|
19672
|
+
count: 0,
|
|
19673
|
+
message: `\u274C Directory not found: ${targetDir}`
|
|
19674
|
+
};
|
|
19675
|
+
}
|
|
19676
|
+
try {
|
|
19677
|
+
const result = execSync(`find "${targetDir}" -name "*.yaml" -type f`, {
|
|
19678
|
+
encoding: "utf-8"
|
|
19679
|
+
});
|
|
19680
|
+
const files = result.trim().split("\n").filter(Boolean);
|
|
19681
|
+
return {
|
|
19682
|
+
files,
|
|
19683
|
+
count: files.length,
|
|
19684
|
+
message: `Found ${files.length} test file(s) in ${targetDir}`
|
|
19685
|
+
};
|
|
19686
|
+
} catch (error2) {
|
|
19687
|
+
return {
|
|
19688
|
+
files: [],
|
|
19689
|
+
count: 0,
|
|
19690
|
+
message: `\u274C Error discovering files: ${error2.message}`
|
|
19691
|
+
};
|
|
19692
|
+
}
|
|
19693
|
+
}
|
|
19694
|
+
function runAllTests(options = {}) {
|
|
19695
|
+
const {
|
|
19696
|
+
directory = "maestro",
|
|
19697
|
+
basePath = process.cwd(),
|
|
19698
|
+
deviceId = null,
|
|
19699
|
+
env = {},
|
|
19700
|
+
stopOnFailure = false,
|
|
19701
|
+
files = null
|
|
19702
|
+
} = options;
|
|
19703
|
+
let discovery;
|
|
19704
|
+
let filesToRun;
|
|
19705
|
+
if (files && Array.isArray(files) && files.length > 0) {
|
|
19706
|
+
filesToRun = files.map((f) => {
|
|
19707
|
+
if (f.startsWith("/")) {
|
|
19708
|
+
return f;
|
|
19709
|
+
}
|
|
19710
|
+
return join(basePath, f);
|
|
19711
|
+
});
|
|
19712
|
+
discovery = {
|
|
19713
|
+
files: filesToRun,
|
|
19714
|
+
count: filesToRun.length,
|
|
19715
|
+
message: `Using ${filesToRun.length} specified test file(s)`,
|
|
19716
|
+
mode: "specified"
|
|
19717
|
+
};
|
|
19718
|
+
} else {
|
|
19719
|
+
discovery = discoverTestFiles({ directory, basePath });
|
|
19720
|
+
discovery.mode = "discovered";
|
|
19721
|
+
filesToRun = discovery.files;
|
|
19722
|
+
}
|
|
19723
|
+
if (!filesToRun || filesToRun.length === 0) {
|
|
19724
|
+
return {
|
|
19725
|
+
discovery,
|
|
19726
|
+
execution: {
|
|
19727
|
+
success: false,
|
|
19728
|
+
results: [],
|
|
19729
|
+
summary: {
|
|
19730
|
+
total: 0,
|
|
19731
|
+
passed: 0,
|
|
19732
|
+
failed: 0,
|
|
19733
|
+
skipped: 0,
|
|
19734
|
+
totalDuration: 0,
|
|
19735
|
+
report: "No test files found to execute."
|
|
19736
|
+
}
|
|
19737
|
+
}
|
|
19738
|
+
};
|
|
19739
|
+
}
|
|
19740
|
+
const execution = executeTestsSequentially({
|
|
19741
|
+
flowFiles: filesToRun,
|
|
19742
|
+
deviceId,
|
|
19743
|
+
env,
|
|
19744
|
+
stopOnFailure
|
|
19745
|
+
});
|
|
19746
|
+
return {
|
|
19747
|
+
discovery,
|
|
19748
|
+
execution
|
|
19749
|
+
};
|
|
19750
|
+
}
|
|
19292
19751
|
|
|
19293
19752
|
// maestro-ai/src/mcp-server.js
|
|
19294
19753
|
var transport = new StdioServerTransport();
|
|
@@ -19306,6 +19765,53 @@ var server = new Server(
|
|
|
19306
19765
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
19307
19766
|
return {
|
|
19308
19767
|
tools: [
|
|
19768
|
+
{
|
|
19769
|
+
name: "maestro_ensure_installed",
|
|
19770
|
+
description: `\u26A0\uFE0F PREREQUISITE: Call this FIRST before using maestro_write_test or maestro_run_all_tests!
|
|
19771
|
+
|
|
19772
|
+
Checks if Maestro CLI is installed and automatically installs it if missing.
|
|
19773
|
+
|
|
19774
|
+
## \u{1F6A8} CRITICAL: Always Call First
|
|
19775
|
+
When a user asks to "generate and run tests" or "run Maestro tests", you MUST:
|
|
19776
|
+
1. FIRST call maestro_ensure_installed() to verify/install Maestro CLI
|
|
19777
|
+
2. THEN proceed with test generation/execution tools
|
|
19778
|
+
|
|
19779
|
+
## Purpose
|
|
19780
|
+
Ensures Maestro CLI is available before running any Maestro commands. This tool:
|
|
19781
|
+
- Checks if Maestro CLI is installed on the system
|
|
19782
|
+
- Automatically installs it if missing (via Homebrew or curl)
|
|
19783
|
+
- Returns version and path information
|
|
19784
|
+
|
|
19785
|
+
## When to Use
|
|
19786
|
+
- ALWAYS before maestro_write_test (if execute=true)
|
|
19787
|
+
- ALWAYS before maestro_run_all_tests
|
|
19788
|
+
- When setting up a new development environment
|
|
19789
|
+
- To troubleshoot Maestro installation issues
|
|
19790
|
+
|
|
19791
|
+
## Installation Method
|
|
19792
|
+
Uses curl installer: \`curl -fsSL "https://get.maestro.mobile.dev" | bash\`
|
|
19793
|
+
|
|
19794
|
+
## Output
|
|
19795
|
+
- installed: Whether Maestro is now installed
|
|
19796
|
+
- version: Installed version (if available)
|
|
19797
|
+
- wasInstalled: Whether this call performed the installation
|
|
19798
|
+
- message: Status message
|
|
19799
|
+
- details: Additional info (path, install method)`,
|
|
19800
|
+
inputSchema: {
|
|
19801
|
+
type: "object",
|
|
19802
|
+
properties: {
|
|
19803
|
+
autoInstall: {
|
|
19804
|
+
type: "boolean",
|
|
19805
|
+
description: "Whether to automatically install Maestro if not found (default: true)"
|
|
19806
|
+
},
|
|
19807
|
+
forceReinstall: {
|
|
19808
|
+
type: "boolean",
|
|
19809
|
+
description: "Whether to force reinstall even if already installed (default: false)"
|
|
19810
|
+
}
|
|
19811
|
+
},
|
|
19812
|
+
required: []
|
|
19813
|
+
}
|
|
19814
|
+
},
|
|
19309
19815
|
{
|
|
19310
19816
|
name: "maestro_generate_test",
|
|
19311
19817
|
description: `Generates comprehensive Maestro test instructions for web applications.
|
|
@@ -19542,6 +20048,198 @@ Automatically runs git commands to:
|
|
|
19542
20048
|
},
|
|
19543
20049
|
required: ["repoPath"]
|
|
19544
20050
|
}
|
|
20051
|
+
},
|
|
20052
|
+
{
|
|
20053
|
+
name: "maestro_write_test",
|
|
20054
|
+
description: `Writes a Maestro test YAML file to disk and automatically executes it.
|
|
20055
|
+
|
|
20056
|
+
## \u{1F6A8} PREREQUISITE: Call maestro_ensure_installed() FIRST before using this tool!
|
|
20057
|
+
|
|
20058
|
+
## Purpose
|
|
20059
|
+
Saves generated test YAML to a file and AUTOMATICALLY RUNS IT. This is the primary tool for "generate and run" workflows.
|
|
20060
|
+
|
|
20061
|
+
## IMPORTANT
|
|
20062
|
+
- \u26A0\uFE0F ALWAYS call maestro_ensure_installed() first to ensure Maestro CLI is available!
|
|
20063
|
+
- This tool ALREADY executes the test by default - do NOT call maestro_run_all_tests after this!
|
|
20064
|
+
- Use this for running newly generated tests
|
|
20065
|
+
- The execution result is included in the response
|
|
20066
|
+
- This tool internally calls maestro_generate_test to provide guidelines and instructions
|
|
20067
|
+
|
|
20068
|
+
## Parameters
|
|
20069
|
+
- yaml: The YAML content to write (required)
|
|
20070
|
+
- fileName: Name of the file, e.g., "login_test.yaml" (required)
|
|
20071
|
+
- directory: Target directory (default: "maestro")
|
|
20072
|
+
- basePath: Project base path (default: current directory)
|
|
20073
|
+
- execute: Whether to auto-execute after writing (default: true)
|
|
20074
|
+
- deviceId: Device ID for execution (optional)
|
|
20075
|
+
- env: Environment variables for execution (optional)
|
|
20076
|
+
- feature: Feature being tested (for test generation guidelines)
|
|
20077
|
+
- action: Action being tested (for test generation guidelines)
|
|
20078
|
+
- flowType: Type of flow template (auth, sidebar, form, modal, navigation, extended)
|
|
20079
|
+
- changedElements: List of UI elements that were changed
|
|
20080
|
+
- existingTests: List of existing related test files
|
|
20081
|
+
|
|
20082
|
+
## Output
|
|
20083
|
+
- success: boolean
|
|
20084
|
+
- filePath: Path where file was written
|
|
20085
|
+
- message: Status message
|
|
20086
|
+
- generation: Test generation guidelines and instructions (from maestro_generate_test)
|
|
20087
|
+
- execution: Test execution results (if execute=true)`,
|
|
20088
|
+
inputSchema: {
|
|
20089
|
+
type: "object",
|
|
20090
|
+
properties: {
|
|
20091
|
+
yaml: {
|
|
20092
|
+
type: "string",
|
|
20093
|
+
description: "The YAML content to write"
|
|
20094
|
+
},
|
|
20095
|
+
fileName: {
|
|
20096
|
+
type: "string",
|
|
20097
|
+
description: 'Name of the file (e.g., "login_test.yaml")'
|
|
20098
|
+
},
|
|
20099
|
+
directory: {
|
|
20100
|
+
type: "string",
|
|
20101
|
+
description: 'Target directory (default: "maestro")'
|
|
20102
|
+
},
|
|
20103
|
+
basePath: {
|
|
20104
|
+
type: "string",
|
|
20105
|
+
description: "Project base path (default: current directory)"
|
|
20106
|
+
},
|
|
20107
|
+
execute: {
|
|
20108
|
+
type: "boolean",
|
|
20109
|
+
description: "Whether to auto-execute the test after writing (default: true)"
|
|
20110
|
+
},
|
|
20111
|
+
deviceId: {
|
|
20112
|
+
type: "string",
|
|
20113
|
+
description: "Device ID to run on (optional)"
|
|
20114
|
+
},
|
|
20115
|
+
env: {
|
|
20116
|
+
type: "object",
|
|
20117
|
+
description: "Environment variables to pass",
|
|
20118
|
+
additionalProperties: { type: "string" }
|
|
20119
|
+
},
|
|
20120
|
+
feature: {
|
|
20121
|
+
type: "string",
|
|
20122
|
+
description: 'Feature being tested for generation guidelines (e.g., "User Settings")'
|
|
20123
|
+
},
|
|
20124
|
+
action: {
|
|
20125
|
+
type: "string",
|
|
20126
|
+
description: 'Action being tested for generation guidelines (e.g., "Reset defaults")'
|
|
20127
|
+
},
|
|
20128
|
+
flowType: {
|
|
20129
|
+
type: "string",
|
|
20130
|
+
enum: [
|
|
20131
|
+
"auth",
|
|
20132
|
+
"sidebar",
|
|
20133
|
+
"form",
|
|
20134
|
+
"modal",
|
|
20135
|
+
"navigation",
|
|
20136
|
+
"extended"
|
|
20137
|
+
],
|
|
20138
|
+
description: "Type of flow template for generation guidelines"
|
|
20139
|
+
},
|
|
20140
|
+
changedElements: {
|
|
20141
|
+
type: "array",
|
|
20142
|
+
items: { type: "string" },
|
|
20143
|
+
description: "List of UI elements that were changed (from git diff)"
|
|
20144
|
+
},
|
|
20145
|
+
existingTests: {
|
|
20146
|
+
type: "array",
|
|
20147
|
+
items: { type: "string" },
|
|
20148
|
+
description: "List of existing related test files to potentially extend"
|
|
20149
|
+
}
|
|
20150
|
+
},
|
|
20151
|
+
required: ["yaml", "fileName"]
|
|
20152
|
+
}
|
|
20153
|
+
},
|
|
20154
|
+
{
|
|
20155
|
+
name: "maestro_discover_tests",
|
|
20156
|
+
description: `Discovers all Maestro test files in a directory.
|
|
20157
|
+
|
|
20158
|
+
## Purpose
|
|
20159
|
+
Lists all .yaml test files in the maestro/ directory.
|
|
20160
|
+
|
|
20161
|
+
## Parameters
|
|
20162
|
+
- directory: Directory to search (default: "maestro")
|
|
20163
|
+
- basePath: Project base path
|
|
20164
|
+
|
|
20165
|
+
## Output
|
|
20166
|
+
- files: Array of file paths
|
|
20167
|
+
- count: Number of files found`,
|
|
20168
|
+
inputSchema: {
|
|
20169
|
+
type: "object",
|
|
20170
|
+
properties: {
|
|
20171
|
+
directory: {
|
|
20172
|
+
type: "string",
|
|
20173
|
+
description: 'Directory to search (default: "maestro")'
|
|
20174
|
+
},
|
|
20175
|
+
basePath: {
|
|
20176
|
+
type: "string",
|
|
20177
|
+
description: "Project base path"
|
|
20178
|
+
}
|
|
20179
|
+
},
|
|
20180
|
+
required: []
|
|
20181
|
+
}
|
|
20182
|
+
},
|
|
20183
|
+
{
|
|
20184
|
+
name: "maestro_run_all_tests",
|
|
20185
|
+
description: `Runs the ENTIRE Maestro test suite.
|
|
20186
|
+
|
|
20187
|
+
## \u{1F6A8} PREREQUISITE: Call maestro_ensure_installed() FIRST before using this tool!
|
|
20188
|
+
|
|
20189
|
+
## \u26A0\uFE0F ONLY use when user EXPLICITLY asks to "run all tests" or "run the test suite"
|
|
20190
|
+
|
|
20191
|
+
## DO NOT USE for:
|
|
20192
|
+
- "generate and run test cases" \u2192 use maestro_write_test instead (it auto-executes)
|
|
20193
|
+
- Running a newly generated test \u2192 maestro_write_test already does this
|
|
20194
|
+
- Any workflow that involves generating new tests
|
|
20195
|
+
|
|
20196
|
+
## Purpose
|
|
20197
|
+
Discovers and executes ALL existing .yaml test files in the maestro/ directory.
|
|
20198
|
+
|
|
20199
|
+
## Parameters
|
|
20200
|
+
- files: Specific test file paths (optional)
|
|
20201
|
+
- directory: Directory containing tests (default: "maestro")
|
|
20202
|
+
- basePath: Project base path
|
|
20203
|
+
- deviceId: Target device ID
|
|
20204
|
+
- env: Environment variables
|
|
20205
|
+
- stopOnFailure: Stop on first failure (default: false)
|
|
20206
|
+
|
|
20207
|
+
## Output
|
|
20208
|
+
- discovery: List of test files found
|
|
20209
|
+
- execution: Results with pass/fail status for each test
|
|
20210
|
+
- summary: Aggregated statistics`,
|
|
20211
|
+
inputSchema: {
|
|
20212
|
+
type: "object",
|
|
20213
|
+
properties: {
|
|
20214
|
+
files: {
|
|
20215
|
+
type: "array",
|
|
20216
|
+
items: { type: "string" },
|
|
20217
|
+
description: "Specific test file paths to run (if provided, only these files are executed instead of discovering all)"
|
|
20218
|
+
},
|
|
20219
|
+
directory: {
|
|
20220
|
+
type: "string",
|
|
20221
|
+
description: 'Directory containing tests (default: "maestro") - only used if files is not provided'
|
|
20222
|
+
},
|
|
20223
|
+
basePath: {
|
|
20224
|
+
type: "string",
|
|
20225
|
+
description: "Project base path"
|
|
20226
|
+
},
|
|
20227
|
+
deviceId: {
|
|
20228
|
+
type: "string",
|
|
20229
|
+
description: "Device ID to run on"
|
|
20230
|
+
},
|
|
20231
|
+
env: {
|
|
20232
|
+
type: "object",
|
|
20233
|
+
description: "Environment variables",
|
|
20234
|
+
additionalProperties: { type: "string" }
|
|
20235
|
+
},
|
|
20236
|
+
stopOnFailure: {
|
|
20237
|
+
type: "boolean",
|
|
20238
|
+
description: "Stop on first failure (default: false)"
|
|
20239
|
+
}
|
|
20240
|
+
},
|
|
20241
|
+
required: []
|
|
20242
|
+
}
|
|
19545
20243
|
}
|
|
19546
20244
|
]
|
|
19547
20245
|
};
|
|
@@ -19550,6 +20248,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
19550
20248
|
const { name, arguments: args } = request.params;
|
|
19551
20249
|
let result;
|
|
19552
20250
|
switch (name) {
|
|
20251
|
+
case "maestro_ensure_installed": {
|
|
20252
|
+
const { autoInstall, forceReinstall } = args || {};
|
|
20253
|
+
result = ensureMaestroInstalled({
|
|
20254
|
+
autoInstall: autoInstall !== false,
|
|
20255
|
+
// defaults to true
|
|
20256
|
+
forceReinstall: forceReinstall || false
|
|
20257
|
+
});
|
|
20258
|
+
break;
|
|
20259
|
+
}
|
|
19553
20260
|
case "maestro_generate_test": {
|
|
19554
20261
|
const { feature, action, flowType, changedElements, existingTests } = args || {};
|
|
19555
20262
|
result = generateMaestroTest({
|
|
@@ -19600,6 +20307,58 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
19600
20307
|
});
|
|
19601
20308
|
break;
|
|
19602
20309
|
}
|
|
20310
|
+
case "maestro_write_test": {
|
|
20311
|
+
const {
|
|
20312
|
+
yaml,
|
|
20313
|
+
fileName,
|
|
20314
|
+
directory,
|
|
20315
|
+
basePath,
|
|
20316
|
+
execute,
|
|
20317
|
+
deviceId,
|
|
20318
|
+
env,
|
|
20319
|
+
feature,
|
|
20320
|
+
action,
|
|
20321
|
+
flowType,
|
|
20322
|
+
changedElements,
|
|
20323
|
+
existingTests
|
|
20324
|
+
} = args || {};
|
|
20325
|
+
result = writeTestFile({
|
|
20326
|
+
yaml,
|
|
20327
|
+
fileName,
|
|
20328
|
+
directory: directory || "maestro",
|
|
20329
|
+
basePath: basePath || process.cwd(),
|
|
20330
|
+
execute: execute !== false,
|
|
20331
|
+
// defaults to true
|
|
20332
|
+
deviceId: deviceId || null,
|
|
20333
|
+
env: env || {},
|
|
20334
|
+
feature: feature || "",
|
|
20335
|
+
action: action || "",
|
|
20336
|
+
flowType: flowType || null,
|
|
20337
|
+
changedElements: changedElements || [],
|
|
20338
|
+
existingTests: existingTests || []
|
|
20339
|
+
});
|
|
20340
|
+
break;
|
|
20341
|
+
}
|
|
20342
|
+
case "maestro_discover_tests": {
|
|
20343
|
+
const { directory, basePath } = args || {};
|
|
20344
|
+
result = discoverTestFiles({
|
|
20345
|
+
directory: directory || "maestro",
|
|
20346
|
+
basePath: basePath || process.cwd()
|
|
20347
|
+
});
|
|
20348
|
+
break;
|
|
20349
|
+
}
|
|
20350
|
+
case "maestro_run_all_tests": {
|
|
20351
|
+
const { files, directory, basePath, deviceId, env, stopOnFailure } = args || {};
|
|
20352
|
+
result = runAllTests({
|
|
20353
|
+
files: files || null,
|
|
20354
|
+
directory: directory || "maestro",
|
|
20355
|
+
basePath: basePath || process.cwd(),
|
|
20356
|
+
deviceId: deviceId || null,
|
|
20357
|
+
env: env || {},
|
|
20358
|
+
stopOnFailure: stopOnFailure || false
|
|
20359
|
+
});
|
|
20360
|
+
break;
|
|
20361
|
+
}
|
|
19603
20362
|
default:
|
|
19604
20363
|
throw new Error(`Unknown tool: ${name}`);
|
|
19605
20364
|
}
|