mcp-maestro-mobile-ai 1.3.1 → 1.6.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.
@@ -3,8 +3,18 @@
3
3
  *
4
4
  * Provides comprehensive, GENERIC instructions for AI to generate valid Maestro YAML.
5
5
  * Works for ANY test scenario - login, forms, navigation, search, etc.
6
+ *
7
+ * Includes Known Issues & Solutions Registry for handling edge cases
8
+ * discovered during test execution across Flutter, React Native, and Native apps.
6
9
  */
7
10
 
11
+ import {
12
+ getInteractionPatternHints,
13
+ getYamlGenerationRules,
14
+ getBestPractices,
15
+ PatternCategory,
16
+ } from "./knownIssues.js";
17
+
8
18
  /**
9
19
  * COMPREHENSIVE YAML generation instructions for AI
10
20
  * These are GENERIC rules that apply to ALL test scenarios
@@ -210,6 +220,195 @@ appId: {APP_ID}
210
220
 
211
221
  ---
212
222
 
223
+ ### 7.1. FLUTTER DROPDOWN PATTERNS (⚠️ CRITICAL FOR FLUTTER APPS)
224
+
225
+ Flutter dropdowns are rendered as overlays and require special handling:
226
+
227
+ #### Method 1: Tap and Select by Visible Text (Preferred)
228
+ \`\`\`yaml
229
+ # Step 1: Tap on the dropdown to open it
230
+ - tapOn: "Current Selected Value" # e.g., "Australia"
231
+
232
+ # Step 2: Wait for dropdown overlay to appear
233
+ - waitForAnimationToEnd
234
+
235
+ # Step 3: Tap on the desired option by its exact text
236
+ - tapOn: "Desired Option Text" # e.g., "United Kingdom"
237
+ \`\`\`
238
+
239
+ #### Method 2: Using text: selector for partial matching
240
+ \`\`\`yaml
241
+ # Open dropdown
242
+ - tapOn:
243
+ text: "Australia"
244
+ - waitForAnimationToEnd
245
+
246
+ # Select option using partial text match
247
+ - tapOn:
248
+ text: "United Kingdom"
249
+ \`\`\`
250
+
251
+ #### Method 3: Scroll within dropdown to find option
252
+ \`\`\`yaml
253
+ # Open dropdown
254
+ - tapOn: "Australia"
255
+ - waitForAnimationToEnd
256
+
257
+ # If option is not visible, scroll within the dropdown
258
+ - scroll:
259
+ direction: DOWN
260
+
261
+ # Then tap on the option
262
+ - tapOn: "United Kingdom"
263
+ \`\`\`
264
+
265
+ #### Method 4: Using index for dropdown items (when text fails)
266
+ \`\`\`yaml
267
+ # Open dropdown
268
+ - tapOn: "Australia"
269
+ - waitForAnimationToEnd
270
+
271
+ # Select by index (0-based) - e.g., index 1 for second item
272
+ - tapOn:
273
+ index: 1
274
+ \`\`\`
275
+
276
+ #### Method 5: Using containsText for flexible matching
277
+ \`\`\`yaml
278
+ # Open dropdown
279
+ - tapOn:
280
+ containsText: "Austral"
281
+ - waitForAnimationToEnd
282
+
283
+ # Select using contains
284
+ - tapOn:
285
+ containsText: "United King"
286
+ \`\`\`
287
+
288
+ #### Method 6: Using optional property for robust selection
289
+ \`\`\`yaml
290
+ # Open dropdown and handle if already open
291
+ - tapOn:
292
+ text: "Australia"
293
+ optional: true
294
+ - waitForAnimationToEnd
295
+
296
+ # Try multiple selectors for the option
297
+ - runFlow:
298
+ when:
299
+ visible: "United Kingdom"
300
+ commands:
301
+ - tapOn: "United Kingdom"
302
+
303
+ # Fallback: try by index if text fails
304
+ - runFlow:
305
+ when:
306
+ notVisible: "United Kingdom"
307
+ commands:
308
+ - tapOn:
309
+ index: 1
310
+ \`\`\`
311
+
312
+ #### Method 7: Using assertVisible before tap (for debugging)
313
+ \`\`\`yaml
314
+ # Open dropdown
315
+ - tapOn: "Australia"
316
+ - waitForAnimationToEnd
317
+
318
+ # Verify the dropdown opened and options are visible
319
+ - assertVisible: "United Kingdom"
320
+
321
+ # Now tap
322
+ - tapOn: "United Kingdom"
323
+ \`\`\`
324
+
325
+ #### Method 8: Complete Login with Dropdown Example
326
+ \`\`\`yaml
327
+ appId: com.example.app
328
+ ---
329
+ # Login with region selection
330
+ - clearState
331
+ - launchApp
332
+
333
+ # Enter credentials (always tap before input!)
334
+ - tapOn: "Username"
335
+ - inputText: "myuser"
336
+ - tapOn: "Password"
337
+ - inputText: "mypassword"
338
+
339
+ # Hide keyboard before interacting with dropdown
340
+ - hideKeyboard
341
+
342
+ # Select region from dropdown
343
+ - tapOn: "Australia" # Current selection - opens dropdown
344
+ - waitForAnimationToEnd # Wait for overlay
345
+ - tapOn: "United Kingdom" # Desired option
346
+
347
+ # Submit form
348
+ - tapOn: "SIGN IN"
349
+
350
+ # Verify success
351
+ - assertVisible: "Welcome"
352
+ \`\`\`
353
+
354
+ #### Flutter Dropdown Tips:
355
+ 1. ⚠️ **ALWAYS hideKeyboard** before tapping a dropdown - keyboard may cover it!
356
+ 2. ⚠️ **ALWAYS waitForAnimationToEnd** after opening dropdown
357
+ 3. ⚠️ Use **exact text** of the dropdown options when possible
358
+ 4. ⚠️ If options are scrollable, add **scroll** command before selecting
359
+ 5. ⚠️ Some Flutter dropdowns need a **pressKey: escape** to close if tapped outside
360
+ 6. ⚠️ Use **assertVisible** before tapOn to verify dropdown is open
361
+
362
+ #### Flutter Accessibility Issue - When Text Matching Fails:
363
+ ⚠️ **CRITICAL**: Flutter apps often expose elements only via \`accessibilityText\`, not \`text\`.
364
+ When Maestro cannot find elements by text, use **point coordinates**:
365
+
366
+ \`\`\`yaml
367
+ # Step 1: Use maestro hierarchy command to get element bounds
368
+ # Example: bounds = "[53,1238][1028,1386]"
369
+ # Calculate center: X = (53+1028)/2 = 540, Y = (1238+1386)/2 = 1312
370
+
371
+ # Step 2: Tap by coordinates
372
+ - tapOn:
373
+ point: 540,1312
374
+
375
+ # For dropdown options, get bounds from hierarchy and tap centers:
376
+ - tapOn:
377
+ point: 540,1438 # Center of "United Kingdom" option
378
+ \`\`\`
379
+
380
+ **How to get coordinates:**
381
+ 1. Run \`maestro hierarchy\` to dump UI tree
382
+ 2. Find element by \`accessibilityText\` in the JSON
383
+ 3. Extract \`bounds\`: "[left,top][right,bottom]"
384
+ 4. Calculate center: X = (left+right)/2, Y = (top+bottom)/2
385
+ 5. Use \`tapOn: point: X,Y\`
386
+
387
+ ---
388
+
389
+ ### 7.2. PICKER AND DATE PICKER PATTERNS (Mobile)
390
+
391
+ \`\`\`yaml
392
+ # Date Picker
393
+ - tapOn: "Date Field"
394
+ - waitForAnimationToEnd
395
+ - scroll:
396
+ direction: DOWN
397
+ element: "year picker"
398
+ - tapOn: "2024"
399
+ - tapOn: "OK"
400
+
401
+ # Time Picker
402
+ - tapOn: "Time Field"
403
+ - waitForAnimationToEnd
404
+ - tapOn: "10" # Hour
405
+ - tapOn: "30" # Minutes
406
+ - tapOn: "AM" # Period
407
+ - tapOn: "OK"
408
+ \`\`\`
409
+
410
+ ---
411
+
213
412
  ### 8. ❌ NEVER DO THESE
214
413
 
215
414
  1. ❌ **NEVER** use \`inputText\` without \`tapOn\` first
@@ -218,6 +417,8 @@ appId: {APP_ID}
218
417
  4. ❌ **NEVER** use coordinates (x,y) unless absolutely necessary
219
418
  5. ❌ **NEVER** use hardcoded waits (\`sleep\`) - use \`extendedWaitUntil\`
220
419
  6. ❌ **NEVER** forget to hide keyboard after text input if it blocks elements
420
+ 7. ❌ **NEVER** tap dropdown options without \`waitForAnimationToEnd\` after opening dropdown
421
+ 8. ❌ **NEVER** try to select dropdown option while keyboard is visible
221
422
 
222
423
  ---
223
424
 
@@ -229,6 +430,9 @@ appId: {APP_ID}
229
430
  4. ✅ **ALWAYS** add \`assertVisible\` to verify expected results
230
431
  5. ✅ **ALWAYS** use waits for elements that may take time to load
231
432
  6. ✅ **ALWAYS** hide keyboard if it might block buttons: \`- hideKeyboard\`
433
+ 7. ✅ **ALWAYS** use \`hideKeyboard\` BEFORE interacting with dropdowns
434
+ 8. ✅ **ALWAYS** use \`waitForAnimationToEnd\` AFTER opening a dropdown/picker
435
+ 9. ✅ **ALWAYS** tap the CURRENT VALUE to open dropdown, then tap the DESIRED VALUE
232
436
 
233
437
  ---
234
438
 
@@ -304,6 +508,12 @@ export function getYamlGenerationContext(appId, appContext = null) {
304
508
  }
305
509
  }
306
510
 
511
+ // Add generic interaction patterns for reliable tests
512
+ context += getInteractionPatternHints();
513
+
514
+ // Add best practices
515
+ context += getBestPractices();
516
+
307
517
  return context;
308
518
  }
309
519
 
@@ -402,6 +612,86 @@ appId: {APP_ID}
402
612
  - scroll # If logout is below fold
403
613
  - tapOn: "{logout_button}"
404
614
  - assertVisible: "{login_screen_element}"
615
+ `,
616
+
617
+ dropdown: `# Dropdown/Picker Selection Pattern
618
+ appId: {APP_ID}
619
+ ---
620
+ - clearState
621
+ - launchApp
622
+
623
+ # IMPORTANT: Hide keyboard first if any text field was focused
624
+ - hideKeyboard
625
+
626
+ # Step 1: Tap on dropdown (tap the CURRENT selected value)
627
+ - tapOn: "{current_selected_value}"
628
+
629
+ # Step 2: ALWAYS wait for dropdown overlay to appear
630
+ - waitForAnimationToEnd
631
+
632
+ # Step 3: If options might be scrollable, scroll to find the option
633
+ # - scroll:
634
+ # direction: DOWN
635
+
636
+ # Step 4: Tap on the desired option
637
+ - tapOn: "{desired_option_value}"
638
+
639
+ # Step 5: Verify selection (optional)
640
+ - assertVisible: "{desired_option_value}"
641
+ `,
642
+
643
+ flutter_login_with_dropdown: `# Flutter Login with Dropdown Pattern (e.g., Region Selection)
644
+ appId: {APP_ID}
645
+ ---
646
+ # Login with dropdown selection (e.g., region/country picker)
647
+ - clearState
648
+ - launchApp
649
+
650
+ # Enter username
651
+ - tapOn: "{username_field}"
652
+ - inputText: "{username}"
653
+
654
+ # Enter password
655
+ - tapOn: "{password_field}"
656
+ - inputText: "{password}"
657
+
658
+ # CRITICAL: Hide keyboard before dropdown interaction!
659
+ - hideKeyboard
660
+
661
+ # Select from dropdown
662
+ - tapOn: "{current_dropdown_value}" # e.g., "Australia"
663
+ - waitForAnimationToEnd # Wait for dropdown overlay
664
+ - tapOn: "{desired_dropdown_value}" # e.g., "United Kingdom"
665
+
666
+ # Submit
667
+ - tapOn: "{submit_button}"
668
+
669
+ # Verify success
670
+ - assertVisible: "{success_indicator}"
671
+ `,
672
+
673
+ flutter_multi_dropdown: `# Flutter Multiple Dropdowns Pattern
674
+ appId: {APP_ID}
675
+ ---
676
+ - clearState
677
+ - launchApp
678
+
679
+ # First dropdown
680
+ - hideKeyboard
681
+ - tapOn: "{dropdown_1_current_value}"
682
+ - waitForAnimationToEnd
683
+ - tapOn: "{dropdown_1_desired_value}"
684
+
685
+ # Wait for first dropdown to close
686
+ - waitForAnimationToEnd
687
+
688
+ # Second dropdown
689
+ - tapOn: "{dropdown_2_current_value}"
690
+ - waitForAnimationToEnd
691
+ - tapOn: "{dropdown_2_desired_value}"
692
+
693
+ # Continue with form...
694
+ - tapOn: "{submit_button}"
405
695
  `,
406
696
  };
407
697
 
@@ -550,10 +840,23 @@ This ensures consistent YAML generation next time!
550
840
  `;
551
841
  }
552
842
 
843
+ // Re-export interaction pattern utilities for direct access
844
+ export {
845
+ getInteractionPatternHints,
846
+ getYamlGenerationRules,
847
+ getBestPractices,
848
+ PatternCategory,
849
+ } from "./knownIssues.js";
850
+
553
851
  export default {
554
852
  YAML_GENERATION_INSTRUCTIONS,
555
853
  getYamlGenerationContext,
556
854
  TEST_PATTERNS,
557
855
  validateYamlStructure,
558
856
  getScreenAnalysisInstructions,
857
+ // Interaction pattern utilities
858
+ getInteractionPatternHints,
859
+ getYamlGenerationRules,
860
+ getBestPractices,
861
+ PatternCategory,
559
862
  };