Skip to content

Commit c410d60

Browse files
author
GitLab CI
committed
refactor: split bridge_flutter_handlers into 8 sub-handlers
bridge_flutter_handlers.dart (989→35 lines) now delegates to: - bf_inspection.dart: inspect, snapshot, widget tree (172 lines) - bf_interaction.dart: tap, enter_text, swipe, gesture (155 lines) - bf_screenshot.dart: screenshot, visual verify/diff (245 lines) - bf_navigation.dart: route, go_back, nav stack (23 lines) - bf_logging.dart: logs, errors, network monitoring (131 lines) - bf_batch.dart: execute_batch, coordinate actions (103 lines) - bf_assertions.dart: assert_visible/text/count (78 lines) - bf_state.dart: page state, diagnostics, plugins (61 lines) dart analyze: 0 errors.
1 parent f12eda7 commit c410d60

File tree

10 files changed

+1078
-980
lines changed

10 files changed

+1078
-980
lines changed

lib/src/cli/server.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ part 'tool_handlers/flutter_helpers.dart';
2828
part 'tool_handlers/plugin_handlers.dart';
2929
part 'tool_handlers/discovery_helpers.dart';
3030
part 'tool_handlers/bridge_flutter_handlers.dart';
31+
part 'tool_handlers/bf_inspection.dart';
32+
part 'tool_handlers/bf_interaction.dart';
33+
part 'tool_handlers/bf_screenshot.dart';
34+
part 'tool_handlers/bf_navigation.dart';
35+
part 'tool_handlers/bf_logging.dart';
36+
part 'tool_handlers/bf_batch.dart';
37+
part 'tool_handlers/bf_assertions.dart';
38+
part 'tool_handlers/bf_state.dart';
3139
part 'tool_handlers/connection_handlers.dart';
3240
part 'tool_handlers/cdp_connection_handlers.dart';
3341
part 'tool_handlers/dev_tool_handlers.dart';
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
part of '../server.dart';
2+
3+
extension _BfAssertions on FlutterMcpServer {
4+
Future<dynamic> _handleAssertionTool(String name, Map<String, dynamic> args, AppDriver? client) async {
5+
switch (name) {
6+
case 'assert_batch':
7+
final assertions = (args['assertions'] as List<dynamic>?) ?? [];
8+
final results = <Map<String, dynamic>>[];
9+
int passed = 0;
10+
int failed = 0;
11+
for (final assertion in assertions) {
12+
final a = assertion as Map<String, dynamic>;
13+
final aType = a['type'] as String;
14+
try {
15+
final toolName = aType == 'visible' ? 'assert_visible'
16+
: aType == 'not_visible' ? 'assert_not_visible'
17+
: aType == 'text' ? 'assert_text'
18+
: aType == 'element_count' ? 'assert_element_count'
19+
: aType;
20+
final toolArgs = <String, dynamic>{
21+
if (a['key'] != null) 'key': a['key'],
22+
if (a['text'] != null) 'text': a['text'],
23+
if (a['expected'] != null) 'expected': a['expected'],
24+
if (a['count'] != null) 'expected_count': a['count'],
25+
};
26+
final result = await _executeToolInner(toolName, toolArgs);
27+
final success = result is Map && result['success'] == true;
28+
if (success) passed++; else failed++;
29+
results.add({'type': aType, 'success': success, 'result': result});
30+
} catch (e) {
31+
failed++;
32+
results.add({'type': aType, 'success': false, 'error': e.toString()});
33+
}
34+
}
35+
return {
36+
'success': failed == 0,
37+
'total': assertions.length,
38+
'passed': passed,
39+
'failed': failed,
40+
'results': results,
41+
};
42+
43+
// === NEW: Assertions ===
44+
case 'assert_visible':
45+
if (client is BridgeDriver) {
46+
final found = await client.findElement(key: args['key'], text: args['text']);
47+
final isVisible = found.isNotEmpty && found['found'] == true;
48+
return {"success": isVisible, "visible": isVisible, "message": isVisible ? "Element is visible" : "Element not found"};
49+
}
50+
final fc = _asFlutterClient(client!, 'assert_visible');
51+
return await _assertVisible(args, fc, shouldBeVisible: true);
52+
53+
case 'assert_not_visible':
54+
if (client is BridgeDriver) {
55+
final found = await client.findElement(key: args['key'], text: args['text']);
56+
final isGone = found.isEmpty || found['found'] != true;
57+
return {"success": isGone, "visible": !isGone, "message": isGone ? "Element is not visible" : "Element is still visible"};
58+
}
59+
final fc = _asFlutterClient(client!, 'assert_not_visible');
60+
return await _assertVisible(args, fc, shouldBeVisible: false);
61+
62+
case 'assert_text':
63+
if (client is BridgeDriver) {
64+
final actual = await client.getText(key: args['key']);
65+
final expected = args['expected'] as String?;
66+
final matches = actual == expected;
67+
return {"success": matches, "actual": actual, "expected": expected, "message": matches ? "Text matches" : "Text mismatch"};
68+
}
69+
final fc = _asFlutterClient(client!, 'assert_text');
70+
return await _assertText(args, fc);
71+
72+
case 'assert_element_count':
73+
if (client is BridgeDriver) {
74+
final elements = await client.getInteractiveElements();
75+
final count = elements.length;
76+
final expected = args['expected'] as int?;
77+
final matches = expected == null || count == expected;
78+
return {"success": matches, "count": count, "expected": expected, "message": matches ? "Count matches" : "Expected $expected but found $count"};
79+
}
80+
final fc = _asFlutterClient(client!, 'assert_element_count');
81+
return await _assertElementCount(args, fc);
82+
83+
// === NEW: Page State ===
84+
default:
85+
return null;
86+
}
87+
}
88+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
part of '../server.dart';
2+
3+
extension _BfBatch on FlutterMcpServer {
4+
Future<dynamic> _handleBatchCoordTool(String name, Map<String, dynamic> args, AppDriver? client) async {
5+
switch (name) {
6+
case 'execute_batch':
7+
if (client is BridgeDriver) {
8+
final actions = args['actions'] as List? ?? [];
9+
final results = <Map<String, dynamic>>[];
10+
for (final action in actions) {
11+
if (action is Map<String, dynamic>) {
12+
final toolName = action['tool'] as String?;
13+
final toolArgs = Map<String, dynamic>.from(action['args'] as Map? ?? {});
14+
if (toolName != null) {
15+
try {
16+
final result = await client.callMethod(toolName, toolArgs);
17+
results.add({'tool': toolName, 'success': true, 'result': result});
18+
} catch (e) {
19+
results.add({'tool': toolName, 'success': false, 'error': e.toString()});
20+
}
21+
}
22+
}
23+
}
24+
return {"success": true, "results": results, "count": results.length};
25+
}
26+
final fc = _asFlutterClient(client!, 'execute_batch');
27+
return await _executeBatch(args, fc);
28+
29+
// === NEW: Coordinate-based Actions ===
30+
case 'tap_at':
31+
if (client is BridgeDriver) {
32+
await client.callMethod('tap_at', {'x': (args['x'] as num).toDouble(), 'y': (args['y'] as num).toDouble()});
33+
return {"success": true, "action": "tap_at", "x": args['x'], "y": args['y']};
34+
}
35+
final fc = _asFlutterClient(client!, 'tap_at');
36+
final x = (args['x'] as num).toDouble();
37+
final y = (args['y'] as num).toDouble();
38+
await fc.tapAt(x, y);
39+
return {"success": true, "action": "tap_at", "x": x, "y": y};
40+
41+
case 'long_press_at':
42+
if (client is BridgeDriver) {
43+
await client.callMethod('long_press_at', {'x': (args['x'] as num).toDouble(), 'y': (args['y'] as num).toDouble(), 'duration': args['duration'] ?? 500});
44+
return {"success": true, "action": "long_press_at", "x": args['x'], "y": args['y']};
45+
}
46+
final fc = _asFlutterClient(client!, 'long_press_at');
47+
final x = (args['x'] as num).toDouble();
48+
final y = (args['y'] as num).toDouble();
49+
final duration = args['duration'] ?? 500;
50+
await fc.longPressAt(x, y, duration: duration);
51+
return {"success": true, "action": "long_press_at", "x": x, "y": y};
52+
53+
case 'swipe_coordinates':
54+
if (client is BridgeDriver) {
55+
await client.callMethod('swipe_coordinates', {
56+
'start_x': ((args['start_x'] ?? args['startX']) as num).toDouble(),
57+
'start_y': ((args['start_y'] ?? args['startY']) as num).toDouble(),
58+
'end_x': ((args['end_x'] ?? args['endX']) as num).toDouble(),
59+
'end_y': ((args['end_y'] ?? args['endY']) as num).toDouble(),
60+
'duration': args['duration'] ?? args['durationMs'] ?? 300,
61+
});
62+
return {"success": true, "action": "swipe_coordinates"};
63+
}
64+
final fc = _asFlutterClient(client!, 'swipe_coordinates');
65+
final startX = ((args['start_x'] ?? args['startX']) as num).toDouble();
66+
final startY = ((args['start_y'] ?? args['startY']) as num).toDouble();
67+
final endX = ((args['end_x'] ?? args['endX']) as num).toDouble();
68+
final endY = ((args['end_y'] ?? args['endY']) as num).toDouble();
69+
final duration = args['duration'] ?? 300;
70+
await fc.swipeCoordinates(startX, startY, endX, endY,
71+
duration: duration);
72+
return {"success": true, "action": "swipe_coordinates"};
73+
74+
case 'edge_swipe':
75+
if (client is BridgeDriver) {
76+
return await client.callMethod('edge_swipe', {'edge': args['edge'], 'direction': args['direction'], 'distance': (args['distance'] as num?)?.toDouble() ?? 200});
77+
}
78+
final fc = _asFlutterClient(client!, 'edge_swipe');
79+
final edge = args['edge'] as String;
80+
final direction = args['direction'] as String;
81+
final distance = (args['distance'] as num?)?.toDouble() ?? 200;
82+
final result = await fc.edgeSwipe(
83+
edge: edge, direction: direction, distance: distance);
84+
return result;
85+
86+
case 'gesture':
87+
if (client is BridgeDriver) {
88+
return await client.callMethod('gesture', args);
89+
}
90+
final fc = _asFlutterClient(client!, 'gesture');
91+
return await _performGesture(args, fc);
92+
93+
case 'wait_for_idle':
94+
if (client is BridgeDriver) {
95+
return {"success": true, "message": "Bridge platform ready"};
96+
}
97+
final fc = _asFlutterClient(client!, 'wait_for_idle');
98+
return await _waitForIdle(args, fc);
99+
100+
// === NEW: Smart Scroll ===
101+
case 'scroll_until_visible':
102+
if (client is BridgeDriver) {
103+
return await client.callMethod('scroll_until_visible', {'key': args['key'], 'text': args['text'], 'direction': args['direction'] ?? 'down', 'max_scrolls': args['max_scrolls'] ?? 10});
104+
}
105+
final fc = _asFlutterClient(client!, 'scroll_until_visible');
106+
return await _scrollUntilVisible(args, fc);
107+
108+
// === Batch Assertions ===
109+
default:
110+
return null;
111+
}
112+
}
113+
}

0 commit comments

Comments
 (0)