@@ -3,6 +3,7 @@ import 'dart:convert';
33import 'dart:io' ;
44
55import '../drivers/app_driver.dart' ;
6+ import 'device_presets.dart' ;
67
78/// AppDriver that communicates with any web page via Chrome DevTools Protocol.
89///
@@ -1003,26 +1004,54 @@ class CdpDriver implements AppDriver {
10031004
10041005 /// Emulate device (mobile/tablet).
10051006 Future <Map <String , dynamic >> emulateDevice (String device) async {
1006- final devices = < String , Map <String , dynamic >> {
1007- 'iphone-12' : {'width' : 390 , 'height' : 844 , 'scale' : 3.0 , 'mobile' : true , 'ua' : 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X)' },
1008- 'iphone-14' : {'width' : 393 , 'height' : 852 , 'scale' : 3.0 , 'mobile' : true , 'ua' : 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X)' },
1009- 'pixel-7' : {'width' : 412 , 'height' : 915 , 'scale' : 2.625 , 'mobile' : true , 'ua' : 'Mozilla/5.0 (Linux; Android 13; Pixel 7)' },
1010- 'ipad-pro' : {'width' : 1024 , 'height' : 1366 , 'scale' : 2.0 , 'mobile' : true , 'ua' : 'Mozilla/5.0 (iPad; CPU OS 16_0 like Mac OS X)' },
1011- 'desktop-1080p' : {'width' : 1920 , 'height' : 1080 , 'scale' : 1.0 , 'mobile' : false , 'ua' : null },
1012- };
1013- final d = devices[device];
1014- if (d == null ) return {"success" : false , "message" : "Unknown device: $device " , "available" : devices.keys.toList ()};
1007+ // Empty/blank device name → list all available devices
1008+ if (device.trim ().isEmpty) {
1009+ final categories = listDevicesByCategory ();
1010+ return {
1011+ "success" : true ,
1012+ "action" : "list" ,
1013+ "total" : devicePresets.length,
1014+ "devices" : categories,
1015+ };
1016+ }
1017+
1018+ final preset = lookupDevice (device);
1019+ if (preset == null ) {
1020+ // Find close matches for helpful error
1021+ final normalized = device.trim ().toLowerCase ().replaceAll (RegExp (r'[\s_]+' ), '-' );
1022+ final suggestions = devicePresets.keys
1023+ .where ((k) => k.contains (normalized) || normalized.contains (k))
1024+ .take (10 )
1025+ .toList ();
1026+ return {
1027+ "success" : false ,
1028+ "message" : "Unknown device: $device " ,
1029+ "suggestions" : suggestions,
1030+ "total_available" : devicePresets.length,
1031+ "hint" : "Pass an empty device name to list all available devices." ,
1032+ };
1033+ }
10151034
10161035 await _call ('Emulation.setDeviceMetricsOverride' , {
1017- 'width' : d[ ' width' ] ,
1018- 'height' : d[ ' height' ] ,
1019- 'deviceScaleFactor' : d[ 'scale' ] ,
1020- 'mobile' : d[ 'mobile' ] ,
1036+ 'width' : preset. width,
1037+ 'height' : preset. height,
1038+ 'deviceScaleFactor' : preset.deviceScaleFactor ,
1039+ 'mobile' : preset.isMobile ,
10211040 });
1022- if (d['ua' ] != null ) {
1023- await _call ('Emulation.setUserAgentOverride' , {'userAgent' : d['ua' ]});
1041+ if (preset.hasTouch) {
1042+ await _call ('Emulation.setTouchEmulationEnabled' , {'enabled' : true });
1043+ }
1044+ if (preset.userAgent != null ) {
1045+ await _call ('Emulation.setUserAgentOverride' , {'userAgent' : preset.userAgent});
10241046 }
1025- return {"success" : true , "device" : device, "viewport" : {"width" : d['width' ], "height" : d['height' ]}};
1047+ return {
1048+ "success" : true ,
1049+ "device" : device,
1050+ "viewport" : {"width" : preset.width, "height" : preset.height},
1051+ "deviceScaleFactor" : preset.deviceScaleFactor,
1052+ "isMobile" : preset.isMobile,
1053+ "hasTouch" : preset.hasTouch,
1054+ };
10261055 }
10271056
10281057 /// Generate PDF (headless Chrome only).
0 commit comments