From 0d8e681316a1359ac1e70ca17e0cd5d0b694f4ce Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Thu, 11 Jul 2019 22:54:54 -0700 Subject: [PATCH 01/23] Added test to change device orientation #66 --- test/screenshots_test.dart | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/test/screenshots_test.dart b/test/screenshots_test.dart index dc2591ea..dc4948c3 100644 --- a/test/screenshots_test.dart +++ b/test/screenshots_test.dart @@ -563,5 +563,53 @@ devices: expect(regExp.stringMatch(line), line); expect(regExp.hasMatch(line), true); }); + + group('manage device orientation', () { + test('find ios simulator orientation', () async { + final udId = '03D4FC12-3927-4C8B-A226-17DE34AE9C18'; + final env = Platform.environment; + final preferencesDir = + '${env['HOME']}/Library/Developer/CoreSimulator/Devices/$udId/data/Library/Preferences'; + await Directory(preferencesDir).listSync().forEach((fsEntity) { + // print contents + final filePath = fsEntity.path; + print('filePath=$filePath'); + try { + final contents = run.cmd('plutil', + ['-convert', 'xml1', '-r', '-o', '-', filePath], '.', true); + print('contents=$contents'); + } catch (e) { + print('error: $e'); + } + }); + }); + + test('set android emulator orientation', () async { + final emulatorId = 'Nexus_6P_API_28'; + final daemonClient = DaemonClient(); + await daemonClient.start; + final deviceId = await daemonClient.launchEmulator(emulatorId); + // change orientation to landscape + final landscape = '1'; + final portrait = '0'; + final changeOrientation = (orientation) async { + run.cmd('adb', [ + '-s', + deviceId, + 'shell', + 'settings', + 'put', + 'system', + 'user_rotation', + orientation + ]); + await Future.delayed(Duration(milliseconds: 5000)); + }; + await changeOrientation(landscape); + await changeOrientation(portrait); + expect(await run.shutdownAndroidEmulator(daemonClient, deviceId), + deviceId); + }); + }); }); } From 37eadcedb25bd7ddcecc88b9051877fdb62164b5 Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Tue, 16 Jul 2019 17:05:55 -0700 Subject: [PATCH 02/23] Added initial support for landscape #66 --- example/screenshots.yaml | 5 + lib/resources/script/sim_orientation.scpt | 1 + lib/src/orientation.dart | 56 ++++++++++ lib/src/resources.dart | 4 + lib/src/run.dart | 34 +++++- lib/src/utils.dart | 20 +++- test/screenshots_test.dart | 122 ++++++++++++++-------- 7 files changed, 195 insertions(+), 47 deletions(-) create mode 100644 lib/resources/script/sim_orientation.scpt create mode 100644 lib/src/orientation.dart diff --git a/example/screenshots.yaml b/example/screenshots.yaml index 0a4bb302..c97ed96f 100644 --- a/example/screenshots.yaml +++ b/example/screenshots.yaml @@ -18,10 +18,15 @@ locales: devices: ios: iPhone XS Max: + frame: false + orientation: LandscapeRight iPad Pro (12.9-inch) (2nd generation): frame: false + orientation: LandscapeRight android: Nexus 6P: + frame: false + orientation: LandscapeRight # Frame screenshots frame: true \ No newline at end of file diff --git a/lib/resources/script/sim_orientation.scpt b/lib/resources/script/sim_orientation.scpt new file mode 100644 index 00000000..9b8d89d4 --- /dev/null +++ b/lib/resources/script/sim_orientation.scpt @@ -0,0 +1 @@ +on run argv my do_submenu("Simulator", "Hardware", "Orientation", item 1 of argv) return item 1 of argv end run on do_submenu(app_name, menu_name, menu_item, submenu_item) try -- bring the target application to the front tell application app_name activate end tell tell application "System Events" tell process app_name tell menu bar 1 tell menu bar item menu_name tell menu menu_name tell menu item menu_item tell menu menu_item click menu item submenu_item end tell end tell end tell end tell end tell end tell end tell return true on error error_message return false end try end do_submenu \ No newline at end of file diff --git a/lib/src/orientation.dart b/lib/src/orientation.dart new file mode 100644 index 00000000..d57a564d --- /dev/null +++ b/lib/src/orientation.dart @@ -0,0 +1,56 @@ +import 'globals.dart'; +import 'utils.dart' as utils; +import 'run.dart' as run; + +enum Orientation { Portrait, LandscapeRight, PortraitUpsideDown, LandscapeLeft } + +/// Change orientation of a running emulator or simulator. +/// (No known way of supporting real devices.) +void changeDeviceOrientation(DeviceType deviceType, Orientation orientation, + {String deviceId, String scriptDir}) { + final androidOrientations = { + 'Portrait': '0', + 'LandscapeRight': '1', + 'PortraitUpsideDown': '2', + 'LandscapeLeft': '3' + }; + final iosOrientations = { + 'Portrait': 'Portrait', + 'LandscapeRight': 'Landscape Right', + 'PortraitUpsideDown': 'Portrait Upside Down', + 'LandscapeLeft': 'Landscape Left' + }; + const sim_orientation_script = 'sim_orientation.scpt'; + final _orientation = utils.getStringFromEnum(orientation); + print('_orientation=$_orientation'); + switch (deviceType) { + case DeviceType.android: + run.cmd('adb', [ + '-s', + deviceId, + 'shell', + 'settings', + 'put', + 'system', + 'user_rotation', + androidOrientations[_orientation] + ]); + break; + case DeviceType.ios: + // requires permission when run for first time + run.cmd('osascript', [ + '$scriptDir/$sim_orientation_script', + iosOrientations[_orientation] + ]); + break; + } +} + +Orientation getOrientationEnum(String orientation) { + final _orientation = + utils.getEnumFromString(Orientation.values, orientation); + _orientation == null + ? throw 'Error: orientation \'$orientation\' not found' + : null; + return _orientation; +} diff --git a/lib/src/resources.dart b/lib/src/resources.dart index 33dcef38..a064c25c 100644 --- a/lib/src/resources.dart +++ b/lib/src/resources.dart @@ -31,6 +31,10 @@ Future unpackScripts(String dstDir) async { 'resources/script/simulator-controller', dstDir, ); + await unpackScript( + 'resources/script/sim_orientation.scpt', + dstDir, + ); } /// Read script from resources and install in staging area. diff --git a/lib/src/run.dart b/lib/src/run.dart index f538e499..4f8aca28 100644 --- a/lib/src/run.dart +++ b/lib/src/run.dart @@ -6,6 +6,7 @@ import 'daemon_client.dart'; import 'fastlane.dart' as fastlane; import 'globals.dart'; import 'image_processor.dart'; +import 'orientation.dart' as orient; import 'resources.dart' as resources; import 'screens.dart'; import 'utils.dart' as utils; @@ -26,6 +27,7 @@ Future run([String configPath = kConfigFileName]) async { // start flutter daemon print('Starting flutter daemon...'); final daemonClient = DaemonClient(); + daemonClient.verbose = true; await daemonClient.start; // get all attached devices and running emulators/simulators final devices = await daemonClient.devices; @@ -112,7 +114,9 @@ Future runTestsOnAll(DaemonClient daemonClient, List devices, List emulators, } } } - assert(deviceId != null); + deviceId == null + ? throw 'Error: device \'$configDeviceName\' not found' + : null; // Check for a running android device or emulator bool isRunningAndroidDeviceOrEmulator(Map device, Map emulator) { @@ -167,6 +171,34 @@ Future runTestsOnAll(DaemonClient daemonClient, List devices, List emulators, await config.storeEnv(screens, configDeviceName, locale, utils.getStringFromEnum(deviceType)); + // Change orientation if required + final deviceOrientation = configInfo['devices'] + [utils.getStringFromEnum(deviceType)][configDeviceName] + ['orientation']; + if (deviceOrientation.isNotEmpty) { + final orientation = orient.getOrientationEnum(deviceOrientation); + switch (deviceType) { + case DeviceType.android: + final currentDevice = + utils.getDeviceFromId(await daemonClient.devices, deviceId); + currentDevice == null + ? throw 'Error: device \'$configDeviceName\' not found in flutter daemon.' + : null; + if (currentDevice['emulator']) { + orient.changeDeviceOrientation(deviceType, orientation, + deviceId: deviceId); + } else { + print( + 'Warning: cannot change orientation of a real android device.'); + } + break; + case DeviceType.ios: + orient.changeDeviceOrientation(deviceType, orientation, + scriptDir: '$stagingDir/resources/script'); + break; + } + } + // run tests for (final testPath in testPaths) { print( diff --git a/lib/src/utils.dart b/lib/src/utils.dart index ee52b7e8..fe2b1084 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -184,6 +184,12 @@ Future prefixFilesInDir(String dirPath, String prefix) async { /// Converts [enum] value to [String]. String getStringFromEnum(dynamic _enum) => _enum.toString().split('.').last; +/// Converts [String] to [enum]. +T getEnumFromString(List values, String value) { + return values.firstWhere((type) => getStringFromEnum(type) == value, + orElse: () => null); +} + /// Returns locale of currently attached android device. String androidDeviceLocale(String deviceId) { final deviceLocale = run @@ -276,7 +282,7 @@ List getAllConfiguredDeviceNames(Map configInfo) { return deviceNames; } -/// Get device from deviceName. +/// Get device for deviceName from list of devices. Map getDevice(List devices, String deviceName) { return devices.firstWhere( (device) => device['model'] == null @@ -285,6 +291,12 @@ Map getDevice(List devices, String deviceName) { orElse: () => null); } +/// Get device for deviceId from list of devices. +Map getDeviceFromId(List devices, String deviceId) { + return devices.firstWhere((device) => device['id'] == deviceId, + orElse: () => null); +} + /// Wait for message to appear in sys log and return first matching line Future waitSysLogMsg(String deviceId, RegExp regExp) async { run.cmd('adb', ['logcat', '-c']); @@ -312,3 +324,9 @@ Map findEmulator(List emulators, String emulatorName) { return emulators.firstWhere((emulator) => emulator['name'] == emulatorName, orElse: () => null); } + +/// Run AppleScript +/// Requires permission on first run. +void runOsaScript(String script, List args) { + run.cmd('osascript', [script, ...args]); +} diff --git a/test/screenshots_test.dart b/test/screenshots_test.dart index dc4948c3..96361105 100644 --- a/test/screenshots_test.dart +++ b/test/screenshots_test.dart @@ -6,6 +6,7 @@ import 'package:screenshots/src/config.dart'; import 'package:screenshots/src/daemon_client.dart'; import 'package:screenshots/src/globals.dart'; import 'package:screenshots/src/image_processor.dart'; +import 'package:screenshots/src/orientation.dart' as orient; import 'package:screenshots/src/screens.dart'; import 'package:screenshots/src/image_magick.dart' as im; import 'package:screenshots/src/resources.dart' as resources; @@ -280,10 +281,14 @@ void main() { final simulatorName = 'iPhone X'; final simulatorInfo = utils.getHighestIosSimulator(utils.getIosSimulators(), simulatorName); - // note: daemonClient should get an 'add.device' event after simulator startup final deviceId = simulatorInfo['udid']; + final daemonClient = DaemonClient(); + daemonClient.verbose = true; + await daemonClient.start; run.startSimulator(deviceId); + await waitForEmulatorToStart(daemonClient, simulatorName); run.shutdownSimulator(deviceId); + await daemonClient.stop; }); test('start emulator on travis', () async { @@ -563,53 +568,80 @@ devices: expect(regExp.stringMatch(line), line); expect(regExp.hasMatch(line), true); }); + }); - group('manage device orientation', () { - test('find ios simulator orientation', () async { - final udId = '03D4FC12-3927-4C8B-A226-17DE34AE9C18'; - final env = Platform.environment; - final preferencesDir = - '${env['HOME']}/Library/Developer/CoreSimulator/Devices/$udId/data/Library/Preferences'; - await Directory(preferencesDir).listSync().forEach((fsEntity) { - // print contents - final filePath = fsEntity.path; - print('filePath=$filePath'); - try { - final contents = run.cmd('plutil', - ['-convert', 'xml1', '-r', '-o', '-', filePath], '.', true); - print('contents=$contents'); - } catch (e) { - print('error: $e'); - } - }); + group('manage device orientation', () { + test('find ios simulator orientation', () async { + final udId = '03D4FC12-3927-4C8B-A226-17DE34AE9C18'; + final env = Platform.environment; + final preferencesDir = + '${env['HOME']}/Library/Developer/CoreSimulator/Devices/$udId/data/Library/Preferences'; + await Directory(preferencesDir).listSync().forEach((fsEntity) { + // print contents + final filePath = fsEntity.path; + print('filePath=$filePath'); + try { + final contents = run.cmd('plutil', + ['-convert', 'xml1', '-r', '-o', '-', filePath], '.', true); + print('contents=$contents'); + } catch (e) { + print('error: $e'); + } }); + }); - test('set android emulator orientation', () async { - final emulatorId = 'Nexus_6P_API_28'; - final daemonClient = DaemonClient(); - await daemonClient.start; - final deviceId = await daemonClient.launchEmulator(emulatorId); - // change orientation to landscape - final landscape = '1'; - final portrait = '0'; - final changeOrientation = (orientation) async { - run.cmd('adb', [ - '-s', - deviceId, - 'shell', - 'settings', - 'put', - 'system', - 'user_rotation', - orientation - ]); - await Future.delayed(Duration(milliseconds: 5000)); - }; - await changeOrientation(landscape); - await changeOrientation(portrait); - expect(await run.shutdownAndroidEmulator(daemonClient, deviceId), - deviceId); - }); + test('set ios simulator orientation', () async { + final scriptDir = 'lib/resources/script'; + final simulatorName = 'iPhone 7 Plus'; + final simulatorInfo = + utils.getHighestIosSimulator(utils.getIosSimulators(), simulatorName); + final deviceId = simulatorInfo['udid']; + run.startSimulator(deviceId); + final daemonClient = DaemonClient(); + daemonClient.verbose = true; + await daemonClient.start; + await waitForEmulatorToStart(daemonClient, simulatorName); + await Future.delayed(Duration(milliseconds: 5000)); // finish booting + orient.changeDeviceOrientation( + DeviceType.ios, orient.Orientation.LandscapeRight, + scriptDir: scriptDir); + await Future.delayed(Duration(milliseconds: 3000)); + orient.changeDeviceOrientation( + DeviceType.ios, orient.Orientation.Portrait, + scriptDir: scriptDir); + await Future.delayed(Duration(milliseconds: 1000)); + run.shutdownSimulator(deviceId); + await daemonClient.stop; + }); + + test('set android emulator orientation', () async { + final emulatorId = 'Nexus_6P_API_28'; + final daemonClient = DaemonClient(); + await daemonClient.start; + final deviceId = await daemonClient.launchEmulator(emulatorId); + orient.changeDeviceOrientation( + DeviceType.android, orient.Orientation.LandscapeRight, + deviceId: deviceId); + await Future.delayed(Duration(milliseconds: 3000)); + orient.changeDeviceOrientation( + DeviceType.android, orient.Orientation.Portrait, + deviceId: deviceId); + await Future.delayed(Duration(milliseconds: 3000)); + expect( + await run.shutdownAndroidEmulator(daemonClient, deviceId), deviceId); }); }); } + +Future waitForEmulatorToStart( + DaemonClient daemonClient, String simulatorName) async { + bool started = false; + while (!started) { + final devices = await daemonClient.devices; + final device = devices.firstWhere( + (device) => device['name'] == simulatorName && device['emulator'], + orElse: () => null); + started = device != null; + await Future.delayed(Duration(milliseconds: 1000)); + } +} From 481298bfcdea2592d66d8660c322ae160b0aba0e Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Tue, 16 Jul 2019 17:38:49 -0700 Subject: [PATCH 03/23] Fixed end-of-line --- lib/resources/script/sim_orientation.scpt | 32 ++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/resources/script/sim_orientation.scpt b/lib/resources/script/sim_orientation.scpt index 9b8d89d4..9106074d 100644 --- a/lib/resources/script/sim_orientation.scpt +++ b/lib/resources/script/sim_orientation.scpt @@ -1 +1,31 @@ -on run argv my do_submenu("Simulator", "Hardware", "Orientation", item 1 of argv) return item 1 of argv end run on do_submenu(app_name, menu_name, menu_item, submenu_item) try -- bring the target application to the front tell application app_name activate end tell tell application "System Events" tell process app_name tell menu bar 1 tell menu bar item menu_name tell menu menu_name tell menu item menu_item tell menu menu_item click menu item submenu_item end tell end tell end tell end tell end tell end tell end tell return true on error error_message return false end try end do_submenu \ No newline at end of file +on run argv + my do_submenu("Simulator", "Hardware", "Orientation", item 1 of argv) + return item 1 of argv +end run + +on do_submenu(app_name, menu_name, menu_item, submenu_item) + try + -- bring the target application to the front + tell application app_name + activate + end tell + tell application "System Events" + tell process app_name + tell menu bar 1 + tell menu bar item menu_name + tell menu menu_name + tell menu item menu_item + tell menu menu_item + click menu item submenu_item + end tell + end tell + end tell + end tell + end tell + end tell + end tell + return true + on error error_message + return false + end try +end do_submenu From 1e153face08353ee6c416c09e49b10117f6d174e Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Tue, 16 Jul 2019 19:01:52 -0700 Subject: [PATCH 04/23] Fixed typo --- lib/src/run.dart | 76 ++++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/lib/src/run.dart b/lib/src/run.dart index 7f5d9901..c4c82538 100644 --- a/lib/src/run.dart +++ b/lib/src/run.dart @@ -206,48 +206,48 @@ Future runTestsOnAll(DaemonClient daemonClient, List devices, List emulators, } } - // Change orientation if required - final deviceOrientation = configInfo['devices'] - [utils.getStringFromEnum(deviceType)][configDeviceName] - ['orientation']; - if (deviceOrientation.isNotEmpty) { - final orientation = orient.getOrientationEnum(deviceOrientation); - switch (deviceType) { - case DeviceType.android: - final currentDevice = - utils.getDeviceFromId(await daemonClient.devices, deviceId); - currentDevice == null - ? throw 'Error: device \'$configDeviceName\' not found in flutter daemon.' - : null; - if (currentDevice['emulator']) { + // Change orientation if required + final deviceOrientation = configInfo['devices'] + [utils.getStringFromEnum(deviceType)][configDeviceName] + ['orientation']; + if (deviceOrientation.isNotEmpty) { + final orientation = orient.getOrientationEnum(deviceOrientation); + switch (deviceType) { + case DeviceType.android: + final currentDevice = + utils.getDeviceFromId(await daemonClient.devices, deviceId); + currentDevice == null + ? throw 'Error: device \'$configDeviceName\' not found in flutter daemon.' + : null; + if (currentDevice['emulator']) { + orient.changeDeviceOrientation(deviceType, orientation, + deviceId: deviceId); + } else { + print( + 'Warning: cannot change orientation of a real android device.'); + } + break; + case DeviceType.ios: orient.changeDeviceOrientation(deviceType, orientation, - deviceId: deviceId); - } else { - print( - 'Warning: cannot change orientation of a real android device.'); - } - break; - case DeviceType.ios: - orient.changeDeviceOrientation(deviceType, orientation, - scriptDir: '$stagingDir/resources/script'); - break; + scriptDir: '$stagingDir/resources/script'); + break; + } } - } - - // run tests and process images - await runProcessTests(config, screens, configDeviceName, locale, - deviceType, testPaths, deviceId, imageProcessor, runMode, archive); - } + // run tests and process images + await runProcessTests(config, screens, configDeviceName, locale, + deviceType, testPaths, deviceId, imageProcessor, runMode, archive); + } - // if an emulator was started, revert locale if necessary and shut it down - if (emulator != null) { - await setEmulatorLocale(deviceId, origAndroidLocale, configDeviceName); - await shutdownAndroidEmulator(daemonClient, deviceId); - } - if (simulator != null) { - // todo: revert locale - shutdownSimulator(deviceId); + // if an emulator was started, revert locale if necessary and shut it down + if (emulator != null) { + await setEmulatorLocale(deviceId, origAndroidLocale, configDeviceName); + await shutdownAndroidEmulator(daemonClient, deviceId); + } + if (simulator != null) { + // todo: revert locale + shutdownSimulator(deviceId); + } } } } From 81ca719dcd154b3ad85784a0ab3daeb0d6ee745a Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Tue, 16 Jul 2019 19:43:07 -0700 Subject: [PATCH 05/23] Changed no-frame behavior to not process status bar or nav bar #66 --- lib/src/image_processor.dart | 40 ++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/lib/src/image_processor.dart b/lib/src/image_processor.dart index b0093a5b..9b09c4a4 100644 --- a/lib/src/image_processor.dart +++ b/lib/src/image_processor.dart @@ -40,32 +40,36 @@ class ImageProcessor { if (screenProps == null) { print('Warning: \'$deviceName\' images will not be processed'); } else { - final Map screenResources = screenProps['resources']; - final staging = _config['staging']; + // add frame if required + if (isFrameRequired(_config, deviceType, deviceName)) { + final Map screenResources = screenProps['resources']; + final staging = _config['staging']; // print('screenResources=$screenResources'); - print('Processing screenshots from test...'); + print('Processing screenshots from test...'); - // unpack images for screen from package to local staging area - await resources.unpackImages(screenResources, staging); + // unpack images for screen from package to local staging area + await resources.unpackImages(screenResources, staging); - // add status and nav bar and frame for each screenshot - final screenshots = Directory('$staging/$kTestScreenshotsDir').listSync(); - for (final screenshotPath in screenshots) { - // add status bar for each screenshot + // add status and nav bar and frame for each screenshot + final screenshots = + Directory('$staging/$kTestScreenshotsDir').listSync(); + for (final screenshotPath in screenshots) { + // add status bar for each screenshot // print('overlaying status bar over screenshot at $screenshotPath'); - await overlay(_config, screenResources, screenshotPath.path); + await overlay(_config, screenResources, screenshotPath.path); - if (deviceType == DeviceType.android) { - // add nav bar for each screenshot + if (deviceType == DeviceType.android) { + // add nav bar for each screenshot // print('appending navigation bar to screenshot at $screenshotPath'); - await append(_config, screenResources, screenshotPath.path); - } + await append(_config, screenResources, screenshotPath.path); + } - // add frame if required - if (isFrameRequired(_config, deviceType, deviceName)) { // print('placing $screenshotPath in frame'); - await frame(_config, screenProps, screenshotPath.path, deviceType, runMode); + await frame( + _config, screenProps, screenshotPath.path, deviceType, runMode); } + } else { + print('Warning: framing is not selected'); } } @@ -211,7 +215,7 @@ class ImageProcessor { // set the default background color String backgroundColor; - (deviceType == DeviceType.ios && runMode!=RunMode.archive) + (deviceType == DeviceType.ios && runMode != RunMode.archive) ? backgroundColor = kDefaultIosBackground : backgroundColor = kDefaultAndroidBackground; From 67c858759f38a011d03c252db959a2a3c1255479 Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Tue, 16 Jul 2019 19:45:24 -0700 Subject: [PATCH 06/23] Added extra time for locale change on emulator to allow for orientation change. Also added wait for simulator to boot #66 --- lib/src/daemon_client.dart | 14 ++++++++++++++ lib/src/run.dart | 13 ++++++++----- test/screenshots_test.dart | 21 ++++++--------------- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/lib/src/daemon_client.dart b/lib/src/daemon_client.dart index ea802ff9..6f71675e 100644 --- a/lib/src/daemon_client.dart +++ b/lib/src/daemon_client.dart @@ -199,3 +199,17 @@ List iosDevices() { return device; }).toList(); } + +/// Wait for emulator or simulator to start +Future waitForEmulatorToStart( + DaemonClient daemonClient, String simulatorName) async { + bool started = false; + while (!started) { + final devices = await daemonClient.devices; + final device = devices.firstWhere( + (device) => device['name'] == simulatorName && device['emulator'], + orElse: () => null); + started = device != null; + await Future.delayed(Duration(milliseconds: 1000)); + } +} diff --git a/lib/src/run.dart b/lib/src/run.dart index c4c82538..65c5dafd 100644 --- a/lib/src/run.dart +++ b/lib/src/run.dart @@ -189,19 +189,19 @@ Future runTestsOnAll(DaemonClient daemonClient, List devices, List emulators, device['emulator'])) { // an already running simulator await setSimulatorLocale( - deviceId, configDeviceName, locale, stagingDir); + deviceId, configDeviceName, locale, stagingDir, daemonClient); } else { if (device == null && simulator != null) { if (pendingIosLocaleChangeAtStart) { // a non-running simulator await setSimulatorLocale( - deviceId, configDeviceName, locale, stagingDir, + deviceId, configDeviceName, locale, stagingDir, daemonClient, running: false); pendingIosLocaleChangeAtStart = false; } else { // a running simulator await setSimulatorLocale( - deviceId, configDeviceName, locale, stagingDir); + deviceId, configDeviceName, locale, stagingDir, daemonClient); } } } @@ -323,8 +323,8 @@ Map _findDevice(List devices, List emulators, String deviceName) { } /// Set the locale for a running simulator. -Future setSimulatorLocale( - String deviceId, String deviceName, String testLocale, stagingDir, +Future setSimulatorLocale(String deviceId, String deviceName, String testLocale, + String stagingDir, DaemonClient daemonClient, {bool running = true}) async { // a running simulator final deviceLocale = utils.getIosSimulatorLocale(deviceId); @@ -336,6 +336,7 @@ Future setSimulatorLocale( await _changeSimulatorLocale(stagingDir, deviceId, testLocale); print('Starting $deviceName...'); startSimulator(deviceId); + await waitForEmulatorToStart(daemonClient, deviceName); } } @@ -352,6 +353,8 @@ Future setEmulatorLocale(String deviceId, testLocale, deviceName) async { changeAndroidLocale(deviceId, deviceLocale, testLocale); // daemonClient.verbose = false; await utils.waitAndroidLocaleChange(deviceId, testLocale); + // allow additional time before orientation change + await Future.delayed(Duration(milliseconds: 5000)); } } diff --git a/test/screenshots_test.dart b/test/screenshots_test.dart index 8deef59c..bc4e382a 100644 --- a/test/screenshots_test.dart +++ b/test/screenshots_test.dart @@ -391,11 +391,15 @@ void main() { // unpack resources await resources.unpackScripts(stagingDir); + final daemonClient = DaemonClient(); + await daemonClient.start; + // change locale final simulatorInfo = utils.getHighestIosSimulator(utils.getIosSimulators(), simulatorName); final deviceId = simulatorInfo['udid']; - await run.setSimulatorLocale(deviceId, simulatorName, locale, stagingDir, + await run.setSimulatorLocale( + deviceId, simulatorName, locale, stagingDir, daemonClient, running: false); // start simulator @@ -696,7 +700,7 @@ devices: final scriptDir = 'lib/resources/script'; final simulatorName = 'iPhone 7 Plus'; final simulatorInfo = - utils.getHighestIosSimulator(utils.getIosSimulators(), simulatorName); + utils.getHighestIosSimulator(utils.getIosSimulators(), simulatorName); final deviceId = simulatorInfo['udid']; run.startSimulator(deviceId); final daemonClient = DaemonClient(); @@ -734,16 +738,3 @@ devices: }); }); } - -Future waitForEmulatorToStart( - DaemonClient daemonClient, String simulatorName) async { - bool started = false; - while (!started) { - final devices = await daemonClient.devices; - final device = devices.firstWhere( - (device) => device['name'] == simulatorName && device['emulator'], - orElse: () => null); - started = device != null; - await Future.delayed(Duration(milliseconds: 1000)); - } -} \ No newline at end of file From f0022dcfa56787f36863eab228a7e6ea5d6843dd Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Tue, 16 Jul 2019 20:15:35 -0700 Subject: [PATCH 07/23] Added messaging for attempting to set orientation on real ios device. Also fixed messaging for running in archive mode. --- lib/src/run.dart | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/lib/src/run.dart b/lib/src/run.dart index 65c5dafd..517d0847 100644 --- a/lib/src/run.dart +++ b/lib/src/run.dart @@ -51,10 +51,10 @@ Future run( await resources.unpackScripts(stagingDir); final archiveDir = configInfo['archive']; Archive archive = Archive(stagingDir, archiveDir); - if (archiveDir == null) { - await fastlane.clearFastlaneDirs(configInfo, screens, runMode); - } else { + if (runMode==RunMode.archive) { print('Archiving screenshots to ${archive.archiveDirPrefix}...'); + } else { + await fastlane.clearFastlaneDirs(configInfo, screens, runMode); } // run integration tests in each real device (or emulator/simulator) for // each locale and process screenshots @@ -69,14 +69,14 @@ Future run( print(' $recordingDir/ios/fastlane/screenshots'); print(' $recordingDir/android/fastlane/metadata/android'); } else { - if (archiveDir == null) { + if (runMode==RunMode.archive) { + print(' ${archive.archiveDirPrefix}'); + } else { print(' ios/fastlane/screenshots'); print(' android/fastlane/metadata/android'); print('for upload to both Apple and Google consoles.'); print('\nFor uploading and other automation options see:'); print(' https://pub.dartlang.org/packages/fledge'); - } else { - print(' ${archive.archiveDirPrefix}'); } } print('\nscreenshots completed successfully.'); @@ -210,15 +210,15 @@ Future runTestsOnAll(DaemonClient daemonClient, List devices, List emulators, final deviceOrientation = configInfo['devices'] [utils.getStringFromEnum(deviceType)][configDeviceName] ['orientation']; - if (deviceOrientation.isNotEmpty) { + if (deviceOrientation != null) { final orientation = orient.getOrientationEnum(deviceOrientation); + final currentDevice = + utils.getDeviceFromId(await daemonClient.devices, deviceId); + currentDevice == null + ? throw 'Error: device \'$configDeviceName\' not found in flutter daemon.' + : null; switch (deviceType) { case DeviceType.android: - final currentDevice = - utils.getDeviceFromId(await daemonClient.devices, deviceId); - currentDevice == null - ? throw 'Error: device \'$configDeviceName\' not found in flutter daemon.' - : null; if (currentDevice['emulator']) { orient.changeDeviceOrientation(deviceType, orientation, deviceId: deviceId); @@ -228,8 +228,13 @@ Future runTestsOnAll(DaemonClient daemonClient, List devices, List emulators, } break; case DeviceType.ios: - orient.changeDeviceOrientation(deviceType, orientation, - scriptDir: '$stagingDir/resources/script'); + if (currentDevice['emulator']) { + orient.changeDeviceOrientation(deviceType, orientation, + scriptDir: '$stagingDir/resources/script'); + } else { + print( + 'Warning: cannot change orientation of a real iOS device.'); + } break; } } From 8f73cc76d13cc334b102321878cbfa760e19e5a5 Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Wed, 17 Jul 2019 09:10:23 -0700 Subject: [PATCH 08/23] Improved messaging --- lib/src/archive.dart | 5 +---- lib/src/image_processor.dart | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/src/archive.dart b/lib/src/archive.dart index ac8aa95a..60e2ab2a 100644 --- a/lib/src/archive.dart +++ b/lib/src/archive.dart @@ -5,12 +5,9 @@ import 'utils.dart' as utils; class Archive { static final _timeStamp = getTimestamp(); - final _stagingTestDir; final archiveDirPrefix; - Archive(String stagingDir, String archiveDir) - : _stagingTestDir = '$stagingDir/$kTestScreenshotsDir', - archiveDirPrefix = '$archiveDir/$_timeStamp'; + Archive(String archiveDir) : archiveDirPrefix = '$archiveDir/$_timeStamp'; String dstDir(DeviceType deviceType, String locale) => '$archiveDirPrefix/${utils.getStringFromEnum(deviceType)}/$locale'; diff --git a/lib/src/image_processor.dart b/lib/src/image_processor.dart index 9b09c4a4..c5a5cf7f 100644 --- a/lib/src/image_processor.dart +++ b/lib/src/image_processor.dart @@ -69,7 +69,7 @@ class ImageProcessor { _config, screenProps, screenshotPath.path, deviceType, runMode); } } else { - print('Warning: framing is not selected'); + print('Warning: framing is not enabled'); } } From 332ac93e8d6fbd6043213ac59850b4ae0b3b235d Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Wed, 17 Jul 2019 09:15:45 -0700 Subject: [PATCH 09/23] Improved management of setting locale and starting/stopping simulator --- lib/src/run.dart | 92 +++++++++++++++++++++++--------------- test/screenshots_test.dart | 56 +++++++---------------- 2 files changed, 72 insertions(+), 76 deletions(-) diff --git a/lib/src/run.dart b/lib/src/run.dart index 517d0847..45252c94 100644 --- a/lib/src/run.dart +++ b/lib/src/run.dart @@ -49,9 +49,8 @@ Future run( final stagingDir = configInfo['staging']; await Directory(stagingDir + '/$kTestScreenshotsDir').create(recursive: true); await resources.unpackScripts(stagingDir); - final archiveDir = configInfo['archive']; - Archive archive = Archive(stagingDir, archiveDir); - if (runMode==RunMode.archive) { + Archive archive = Archive(configInfo['archive']); + if (runMode == RunMode.archive) { print('Archiving screenshots to ${archive.archiveDirPrefix}...'); } else { await fastlane.clearFastlaneDirs(configInfo, screens, runMode); @@ -69,7 +68,7 @@ Future run( print(' $recordingDir/ios/fastlane/screenshots'); print(' $recordingDir/android/fastlane/metadata/android'); } else { - if (runMode==RunMode.archive) { + if (runMode == RunMode.archive) { print(' ${archive.archiveDirPrefix}'); } else { print(' ios/fastlane/screenshots'); @@ -147,10 +146,11 @@ Future runTestsOnAll(DaemonClient daemonClient, List devices, List emulators, // check if current device is pending a locale change if (locales[0] == utils.getIosSimulatorLocale(deviceId)) { print('Starting $configDeviceName...'); - startSimulator(deviceId); + await startSimulator(daemonClient, deviceId); } else { pendingIosLocaleChangeAtStart = true; - print('Not starting $configDeviceName due to pending locale change'); +// print( +// 'Postponing \'$configDeviceName\' startup due to pending locale change'); } } } @@ -161,23 +161,35 @@ Future runTestsOnAll(DaemonClient daemonClient, List devices, List emulators, final deviceType = getDeviceType(configInfo, configDeviceName); // if device is real ios or android, cannot change locale if (device != null && !device['emulator']) { - final defaultLocale = 'en-US'; // todo: need actual local + final defaultLocale = 'en-US'; // todo: need actual locale print('Warning: the locale of a real device cannot be changed.'); await runProcessTests(config, screens, configDeviceName, defaultLocale, deviceType, testPaths, deviceId, imageProcessor, runMode, archive); } else { - // Check for a running android device or emulator + // Function to check for a running android device or emulator bool isRunningAndroidDeviceOrEmulator(Map device, Map emulator) { return (device != null && device['platform'] != 'ios') || (device == null && emulator != null); } - // save original locale for reverting later if necessary + // save original android locale for reverting later if necessary String origAndroidLocale; if (isRunningAndroidDeviceOrEmulator(device, emulator)) { origAndroidLocale = utils.getAndroidDeviceLocale(deviceId); } + // Function to check for a running ios device or simulator. + bool isRunningIosDeviceOrSimulator(Map device, Map emulator) { + return (device != null && device['platform'] == 'ios') || + (device == null && simulator != null); + } + + // save original ios locale for reverting later if necessary + String origIosLocale; + if (isRunningIosDeviceOrSimulator(device, emulator)) { + origIosLocale = utils.getIosSimulatorLocale(deviceId); + } + for (final locale in locales) { // set locale if android device or emulator if (isRunningAndroidDeviceOrEmulator(device, emulator)) { @@ -185,26 +197,29 @@ Future runTestsOnAll(DaemonClient daemonClient, List devices, List emulators, } // set locale if ios simulator if ((device != null && - device['platform'] == 'ios' && - device['emulator'])) { - // an already running simulator - await setSimulatorLocale( + device['platform'] == 'ios' && + device['emulator']) || + (device == null && + simulator != null && + !pendingIosLocaleChangeAtStart)) { + // an already running simulator or a started simulator + final localeChanged = await setSimulatorLocale( deviceId, configDeviceName, locale, stagingDir, daemonClient); - } else { - if (device == null && simulator != null) { - if (pendingIosLocaleChangeAtStart) { - // a non-running simulator - await setSimulatorLocale( - deviceId, configDeviceName, locale, stagingDir, daemonClient, - running: false); - pendingIosLocaleChangeAtStart = false; - } else { - // a running simulator - await setSimulatorLocale( - deviceId, configDeviceName, locale, stagingDir, daemonClient); - } + if (localeChanged) { + // restart simulator + print('Restarting \'$configDeviceName\' due to locale change...'); + shutdownSimulator(deviceId); + await startSimulator(daemonClient, deviceId); } } + if (pendingIosLocaleChangeAtStart) { + // a non-running simulator + await setSimulatorLocale( + deviceId, configDeviceName, locale, stagingDir, daemonClient); + print('Starting $configDeviceName...'); + await startSimulator(daemonClient, deviceId); + pendingIosLocaleChangeAtStart = false; + } // Change orientation if required final deviceOrientation = configInfo['devices'] @@ -250,7 +265,8 @@ Future runTestsOnAll(DaemonClient daemonClient, List devices, List emulators, await shutdownAndroidEmulator(daemonClient, deviceId); } if (simulator != null) { - // todo: revert locale + await setSimulatorLocale(deviceId, configDeviceName, origIosLocale, + stagingDir, daemonClient); shutdownSimulator(deviceId); } } @@ -285,8 +301,9 @@ void shutdownSimulator(String deviceId) { cmd('xcrun', ['simctl', 'shutdown', deviceId]); } -void startSimulator(String deviceId) { +Future startSimulator(DaemonClient daemonClient, String deviceId) async { cmd('xcrun', ['simctl', 'boot', deviceId]); + await waitForEmulatorToStart(daemonClient, deviceId); } /// Start android emulator and return device id. @@ -327,22 +344,23 @@ Map _findDevice(List devices, List emulators, String deviceName) { return device; } -/// Set the locale for a running simulator. -Future setSimulatorLocale(String deviceId, String deviceName, String testLocale, - String stagingDir, DaemonClient daemonClient, - {bool running = true}) async { +/// Set the simulator locale. +/// (Startup managed elsewhere) +/// Returns true of locale changed. +Future setSimulatorLocale(String deviceId, String deviceName, + String testLocale, String stagingDir, DaemonClient daemonClient) async { // a running simulator final deviceLocale = utils.getIosSimulatorLocale(deviceId); -// print('simulator locale=$deviceLocale'); + print('simulator locale=$deviceLocale'); + bool localeChanged = false; if (testLocale != deviceLocale) { - if (running) shutdownSimulator(deviceId); +// if (running) shutdownSimulator(deviceId); print( 'Changing locale from $deviceLocale to $testLocale on \'$deviceName\'...'); await _changeSimulatorLocale(stagingDir, deviceId, testLocale); - print('Starting $deviceName...'); - startSimulator(deviceId); - await waitForEmulatorToStart(daemonClient, deviceName); + localeChanged = true; } + return localeChanged; } /// Set the locale of a running emulator. diff --git a/test/screenshots_test.dart b/test/screenshots_test.dart index bc4e382a..ff0adaa9 100644 --- a/test/screenshots_test.dart +++ b/test/screenshots_test.dart @@ -291,8 +291,7 @@ void main() { final daemonClient = DaemonClient(); daemonClient.verbose = true; await daemonClient.start; - run.startSimulator(deviceId); - await waitForEmulatorToStart(daemonClient, simulatorName); + await run.startSimulator(daemonClient, deviceId); run.shutdownSimulator(deviceId); await daemonClient.stop; }); @@ -315,26 +314,12 @@ void main() { ProcessStartMode.detached); }); - // reproduce https://github.com/flutter/flutter/issues/27785 - // on android (hangs during test) - // tested on android emulator in default locale (en-US) and it worked - // tested on android emulator in automatically changed to locale fr-CA and it hangs - // tested on android emulator booted in locale fr-CA and it hangs -// [trace] FlutterDriver: Isolate found with number: 939713595 -// [trace] FlutterDriver: Isolate is paused at start. -// [trace] FlutterDriver: Attempting to resume isolate -// [trace] FlutterDriver: Waiting for service extension -// [info ] FlutterDriver: Connected to Flutter application. -// 00:04 +0: end-to-end test tap on the floating action button; verify counter -// [warning] FlutterDriver: waitFor message is taking a long time to complete... -// hangs test('change locale on android and test', () async { final emulatorId = 'Nexus_6P_API_28'; final deviceName = 'any device name'; final stagingDir = '/tmp/tmp'; final origLocale = 'en-US'; - final newLocale = 'en-US'; // succeeds -// final newLocale = 'fr-CA'; // fails + final newLocale = 'fr-CA'; final testAppDir = 'example'; final testAppSrcPath = 'test_driver/main.dart'; @@ -343,6 +328,7 @@ void main() { final daemonClient = DaemonClient(); await daemonClient.start; + // start emulator final deviceId = await daemonClient.launchEmulator(emulatorId); @@ -352,12 +338,12 @@ void main() { // run test await utils.streamCmd('flutter', ['drive', testAppSrcPath], testAppDir); - // stop emulator + // restore orig locale await run.setEmulatorLocale(deviceId, origLocale, deviceName); + + // stop emulator expect(await run.shutdownAndroidEmulator(daemonClient, deviceId), deviceId); - }, - timeout: - Timeout(Duration(seconds: 90))); // increase time to get stacktrace + }, timeout: Timeout(Duration(seconds: 90))); test('get android device locale', () async { final emulatorId = 'Nexus_6P_API_28'; @@ -374,17 +360,11 @@ void main() { expect(deviceLocale, locale); }); - // reproduce https://github.com/flutter/flutter/issues/27785 - // on ios - // tested on ios device in default locale (en-US) and it worked - // tested on ios device in manually changed to locale fr-CA and it hangs - // tested on ios simulator in default locale (en-US) and it worked - // tested on ios simulator in automatically changed to locale fr-CA and it hangs test('change locale on iOS and test', () async { final simulatorName = 'iPhone X'; final stagingDir = '/tmp/tmp'; - final locale = 'en-US'; // default locale (works) -// final locale = 'fr-CA'; // fails + final origLocale = 'en-US'; + final locale = 'fr-CA'; final testAppDir = 'example'; final testAppSrcPath = 'test_driver/main.dart'; @@ -399,13 +379,10 @@ void main() { utils.getHighestIosSimulator(utils.getIosSimulators(), simulatorName); final deviceId = simulatorInfo['udid']; await run.setSimulatorLocale( - deviceId, simulatorName, locale, stagingDir, daemonClient, - running: false); + deviceId, simulatorName, locale, stagingDir, daemonClient); // start simulator -// final daemonClient = DaemonClient(); -// await daemonClient.start; - run.startSimulator(deviceId); + await run.startSimulator(daemonClient, deviceId); // run test await utils.streamCmd( @@ -413,9 +390,11 @@ void main() { // stop simulator run.shutdownSimulator(deviceId); - }, - // increase time to get stacktrace - timeout: Timeout(Duration(minutes: 2))); + + // restore orig locale + await run.setSimulatorLocale( + deviceId, simulatorName, origLocale, stagingDir, daemonClient); + }, timeout: Timeout(Duration(seconds: 90))); test('get ios simulator locale', () async { final udId = '03D4FC12-3927-4C8B-A226-17DE34AE9C18'; @@ -702,11 +681,10 @@ devices: final simulatorInfo = utils.getHighestIosSimulator(utils.getIosSimulators(), simulatorName); final deviceId = simulatorInfo['udid']; - run.startSimulator(deviceId); final daemonClient = DaemonClient(); daemonClient.verbose = true; await daemonClient.start; - await waitForEmulatorToStart(daemonClient, simulatorName); + await run.startSimulator(daemonClient, deviceId); await Future.delayed(Duration(milliseconds: 5000)); // finish booting orient.changeDeviceOrientation( DeviceType.ios, orient.Orientation.LandscapeRight, From 5afb5f339355e632a8d1f9f0f3b61afb49001f4f Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Wed, 17 Jul 2019 09:16:38 -0700 Subject: [PATCH 10/23] Changed waiting for emulator to start to use deviceId --- lib/src/daemon_client.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/daemon_client.dart b/lib/src/daemon_client.dart index 6f71675e..cb3426fa 100644 --- a/lib/src/daemon_client.dart +++ b/lib/src/daemon_client.dart @@ -202,12 +202,12 @@ List iosDevices() { /// Wait for emulator or simulator to start Future waitForEmulatorToStart( - DaemonClient daemonClient, String simulatorName) async { + DaemonClient daemonClient, String deviceId) async { bool started = false; while (!started) { final devices = await daemonClient.devices; final device = devices.firstWhere( - (device) => device['name'] == simulatorName && device['emulator'], + (device) => device['id'] == deviceId && device['emulator'], orElse: () => null); started = device != null; await Future.delayed(Duration(milliseconds: 1000)); From 1301a74cc3f083f8c330426c5547bfc85f0cade3 Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Wed, 17 Jul 2019 09:26:37 -0700 Subject: [PATCH 11/23] Allowed orientation to disable framing --- example/screenshots.yaml | 17 ++++++++--------- example/screenshots_CI.yaml | 2 +- lib/src/image_processor.dart | 9 ++++++--- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/example/screenshots.yaml b/example/screenshots.yaml index fa54a0bb..46e6aca3 100644 --- a/example/screenshots.yaml +++ b/example/screenshots.yaml @@ -8,22 +8,21 @@ staging: /tmp/screenshots # A list of locales supported in app locales: - - fr-CA - en-US + - fr-CA # A list of devices to run tests on devices: ios: - iPhone XS Max: - frame: false - orientation: LandscapeRight +# iPhone XS Max: +# frame: false +# orientation: LandscapeRight iPad Pro (12.9-inch) (2nd generation): - frame: false - orientation: LandscapeRight - android: - Nexus 6P: - frame: false orientation: LandscapeRight +# android: +# Nexus 6P: +# frame: false +# orientation: LandscapeRight # Frame screenshots frame: true diff --git a/example/screenshots_CI.yaml b/example/screenshots_CI.yaml index 8345aa4e..502f5189 100644 --- a/example/screenshots_CI.yaml +++ b/example/screenshots_CI.yaml @@ -14,7 +14,7 @@ locales: # A list of devices to run tests on devices: ios: - iPhone XS Max: +# iPhone XS Max: iPad Pro (12.9-inch) (2nd generation): frame: false android: diff --git a/lib/src/image_processor.dart b/lib/src/image_processor.dart index c5a5cf7f..e84854f7 100644 --- a/lib/src/image_processor.dart +++ b/lib/src/image_processor.dart @@ -194,9 +194,12 @@ class ImageProcessor { bool isFrameRequired = config['frame']; if (device != null) { final isDeviceFrameRequired = device['frame']; - if (isDeviceFrameRequired != null) { - isFrameRequired = isDeviceFrameRequired; - } + // device frame over-rides global frame + isDeviceFrameRequired != null + ? isFrameRequired = isDeviceFrameRequired + : null; + // orientation over-rides global and device frame setting + device['orientation'] != null ? isFrameRequired = false : null; } return isFrameRequired; } From 06028b6205db36984f0291166c4f570f79e41652 Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Wed, 17 Jul 2019 09:38:29 -0700 Subject: [PATCH 12/23] Added orientation --- example/screenshots.yaml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/example/screenshots.yaml b/example/screenshots.yaml index 46e6aca3..29f9312b 100644 --- a/example/screenshots.yaml +++ b/example/screenshots.yaml @@ -14,15 +14,13 @@ locales: # A list of devices to run tests on devices: ios: -# iPhone XS Max: -# frame: false -# orientation: LandscapeRight + iPhone XS Max: + frame: false iPad Pro (12.9-inch) (2nd generation): orientation: LandscapeRight -# android: -# Nexus 6P: -# frame: false -# orientation: LandscapeRight + android: + Nexus 6P: + orientation: LandscapeRight # Frame screenshots frame: true From 45f6f53770d883bb65bad5c4f11afd29a5b5035e Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Wed, 17 Jul 2019 09:38:57 -0700 Subject: [PATCH 13/23] Added-back device --- example/screenshots_CI.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/screenshots_CI.yaml b/example/screenshots_CI.yaml index 502f5189..3d90c347 100644 --- a/example/screenshots_CI.yaml +++ b/example/screenshots_CI.yaml @@ -8,13 +8,13 @@ staging: /tmp/screenshots # A list of locales supported in app locales: - - fr-CA - en-US + - fr-CA # A list of devices to run tests on devices: ios: -# iPhone XS Max: + iPhone XS Max: iPad Pro (12.9-inch) (2nd generation): frame: false android: From fc03804505746be1c956dc018b84ce2aabc5937d Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Wed, 17 Jul 2019 09:51:38 -0700 Subject: [PATCH 14/23] Added orientation to README --- README.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 563b9760..188a736e 100644 --- a/README.md +++ b/README.md @@ -145,23 +145,18 @@ _Screenshots_ uses a configuration file to configure a run. The default config filename is `screenshots.yaml`: ````yaml # A list of screen capture tests +# Note: flutter driver expects a pair of files eg, main1.dart and main1_test.dart tests: - test_driver/main1.dart - test_driver/main2.dart -# Note: flutter driver expects a pair of files for testing -# For example: -# main1.dart is the test app (that calls your app) -# main1_test.dart is the matching test that flutter driver -# expects to find. - # Interim location of screenshots from tests staging: /tmp/screenshots # A list of locales supported by the app locales: - - de-DE - en-US + - de-DE # A map of devices to emulate devices: @@ -169,6 +164,7 @@ devices: iPhone XS Max: frame: false iPad Pro (12.9-inch) (3rd generation): + orientation: LandscapeRight android: Nexus 6P: @@ -182,6 +178,8 @@ Individual devices can be configured in `screenshots.yaml` by specifying per dev | Parameter | Values | Required | Description | | --- | --- | --- | --- | |frame|true/false|optional|Controls whether screenshots generated on the device should be placed in a frame. Overrides the global frame setting (see example `screenshots.yaml` above). +|orientation|Portrait \| LandscapeRight \| PortraitUpsideDown \| LandscapeLeft|optional|Controls orientation of device during test. Currently disables framing resulting in a raw screenshot. Ignored for real devices. + Note: images generated for those devices where framing is disabled are probably not suitable for upload, but can be used for local review. From 8710d00f447b9e4435f4f587a7c5df6338581cd2 Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Wed, 17 Jul 2019 10:45:26 -0700 Subject: [PATCH 15/23] Cleanup --- lib/src/utils.dart | 8 ++++---- pubspec.yaml | 1 + test/env_test.dart | 2 +- test/screenshots_test.dart | 20 ++++++++++---------- test/screenshots_yaml_test.dart | 10 +++++----- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/lib/src/utils.dart b/lib/src/utils.dart index f92ecb00..71ef58eb 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -170,17 +170,17 @@ T getEnumFromString(List values, String value) { String getAndroidDeviceLocale(String deviceId) { // ro.product.locale is available on first boot but does not update, // persist.sys.locale is empty on first boot but updates with locale changes - String deviceLocale = run + String locale = run .cmd('adb', ['-s', deviceId, 'shell', 'getprop persist.sys.locale'], '.', true) .trim(); - if (deviceLocale.isEmpty) { - deviceLocale = run + if (locale.isEmpty) { + locale = run .cmd('adb', ['-s', deviceId, 'shell', 'getprop ro.product.locale'], '.', true) .trim(); } - return deviceLocale; + return locale; } /// Returns locale of simulator with udid [udId]. diff --git a/pubspec.yaml b/pubspec.yaml index 7c8eb32b..0118d985 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: platform: ^2.2.0 process: ^3.0.9 meta: ^1.1.6 + intl: ^0.15.8 dev_dependencies: test: ^1.5.1+1 diff --git a/test/env_test.dart b/test/env_test.dart index 1d17eab5..0ce63b18 100644 --- a/test/env_test.dart +++ b/test/env_test.dart @@ -10,7 +10,7 @@ void main() { final env = { 'screen_size': '1440x2560', - 'locale': 'en-US', + 'locale': 'en_US', 'device_name': 'Nexus 6P', 'device_type': 'android', }; diff --git a/test/screenshots_test.dart b/test/screenshots_test.dart index ff0adaa9..c379a6d1 100644 --- a/test/screenshots_test.dart +++ b/test/screenshots_test.dart @@ -266,8 +266,8 @@ void main() { test('change android locale', () async { final deviceName = 'Nexus 6P'; final emulatorId = 'Nexus_6P_API_28'; - final origLocale = 'en-US'; - final newLocale = 'fr-CA'; + final origLocale = 'en_US'; + final newLocale = 'fr_CA'; final daemonClient = DaemonClient(); await daemonClient.start; daemonClient.verbose = true; @@ -318,8 +318,8 @@ void main() { final emulatorId = 'Nexus_6P_API_28'; final deviceName = 'any device name'; final stagingDir = '/tmp/tmp'; - final origLocale = 'en-US'; - final newLocale = 'fr-CA'; + final origLocale = 'en_US'; + final newLocale = 'fr_CA'; final testAppDir = 'example'; final testAppSrcPath = 'test_driver/main.dart'; @@ -348,7 +348,7 @@ void main() { test('get android device locale', () async { final emulatorId = 'Nexus_6P_API_28'; final stagingDir = '/tmp/tmp'; - final locale = 'en-US'; + final locale = 'en_US'; await resources.unpackScripts(stagingDir); final daemonClient = DaemonClient(); @@ -363,8 +363,8 @@ void main() { test('change locale on iOS and test', () async { final simulatorName = 'iPhone X'; final stagingDir = '/tmp/tmp'; - final origLocale = 'en-US'; - final locale = 'fr-CA'; + final origLocale = 'en_US'; + final locale = 'fr_CA'; final testAppDir = 'example'; final testAppSrcPath = 'test_driver/main.dart'; @@ -399,7 +399,7 @@ void main() { test('get ios simulator locale', () async { final udId = '03D4FC12-3927-4C8B-A226-17DE34AE9C18'; var locale = utils.getIosSimulatorLocale(udId); - expect(locale, 'en-US'); + expect(locale, 'en_US'); }); test('get avd from a running emulator', () async { @@ -507,7 +507,7 @@ devices: }); test('scan syslog for string', () async { - final toLocale = 'en-US'; + final toLocale = 'en_US'; // final expected = // 'ContactsProvider: Locale has changed from [fr_CA] to [en_US]'; // final expected = RegExp('Locale has changed from'); @@ -524,7 +524,7 @@ devices: }); test('reg exp', () { - final locale = 'fr-CA'; + final locale = 'fr_CA'; final line = 'ContactsProvider: Locale has changed from [en_US] to [${locale.replaceFirst('-', '_')}]'; // final regExp = RegExp( diff --git a/test/screenshots_yaml_test.dart b/test/screenshots_yaml_test.dart index de9a172d..e719aed6 100644 --- a/test/screenshots_yaml_test.dart +++ b/test/screenshots_yaml_test.dart @@ -19,9 +19,9 @@ staging: /tmp/screenshots # A list of locales supported in app locales: -# - fr-CA - - en-US -# - de-DE +# - fr_CA + - en_US +# - de_DE # A list of devices to emulate devices: @@ -49,7 +49,7 @@ void main() { test('config info for app from string', () { final expected = { 'tests': ['example/test_driver/main.dart'], - 'locales': ['en-US'], + 'locales': ['en_US'], 'frame': true, 'devices': { 'android': {'Nexus 5X': null}, @@ -68,7 +68,7 @@ void main() { test('config info for app from file', () { final expected = { 'tests': ['example/test_driver/main.dart'], - 'locales': ['en-US'], + 'locales': ['en_US'], 'frame': true, 'devices': { 'android': {'Nexus 5X': null}, From 7aa55f003b2d7822f2287b9098df16426506f4a9 Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Wed, 17 Jul 2019 10:45:49 -0700 Subject: [PATCH 16/23] Improved messaging --- lib/src/orientation.dart | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/src/orientation.dart b/lib/src/orientation.dart index d57a564d..f28504e7 100644 --- a/lib/src/orientation.dart +++ b/lib/src/orientation.dart @@ -22,7 +22,7 @@ void changeDeviceOrientation(DeviceType deviceType, Orientation orientation, }; const sim_orientation_script = 'sim_orientation.scpt'; final _orientation = utils.getStringFromEnum(orientation); - print('_orientation=$_orientation'); + print('Setting orientation to $_orientation'); switch (deviceType) { case DeviceType.android: run.cmd('adb', [ @@ -38,10 +38,11 @@ void changeDeviceOrientation(DeviceType deviceType, Orientation orientation, break; case DeviceType.ios: // requires permission when run for first time - run.cmd('osascript', [ - '$scriptDir/$sim_orientation_script', - iosOrientations[_orientation] - ]); + run.cmd( + 'osascript', + ['$scriptDir/$sim_orientation_script', iosOrientations[_orientation]], + '.', + true); break; } } From f625d879734b73c470c81b50b7032316de18f9ed Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Wed, 17 Jul 2019 10:46:43 -0700 Subject: [PATCH 17/23] Corrected in case of canonicalization of locale --- lib/src/fastlane.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/fastlane.dart b/lib/src/fastlane.dart index 73ac9410..376e2b3d 100644 --- a/lib/src/fastlane.dart +++ b/lib/src/fastlane.dart @@ -50,6 +50,7 @@ Future _clearFastlaneDir(Screens screens, String deviceName, String locale, /// Generate fastlane dir path for ios or android. String getDirPath( DeviceType deviceType, String locale, String androidModelType) { + locale = locale.replaceAll('_', '-'); // in case canonicalized const androidPrefix = 'android/fastlane/metadata/android'; const iosPrefix = 'ios/fastlane/screenshots'; String dirPath; @@ -82,4 +83,4 @@ void deleteMatchingFiles(String dirPath, RegExp pattern) { } else { Directory(dirPath).createSync(recursive: true); } -} \ No newline at end of file +} From 36ba8f129b39d439d4890b66a469f2b4a6ec868f Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Wed, 17 Jul 2019 10:57:17 -0700 Subject: [PATCH 18/23] Added canonicalization to locale compares, added delay to simulator shutdown in case restarting, improved messaging. --- lib/src/run.dart | 28 +++++++++++++++++----------- test/screenshots_test.dart | 6 +++--- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/src/run.dart b/lib/src/run.dart index 45252c94..54d5641c 100644 --- a/lib/src/run.dart +++ b/lib/src/run.dart @@ -1,6 +1,8 @@ import 'dart:async'; import 'dart:io'; +import 'package:intl/intl.dart'; + import 'archive.dart'; import 'config.dart'; import 'daemon_client.dart'; @@ -144,7 +146,8 @@ Future runTestsOnAll(DaemonClient daemonClient, List devices, List emulators, utils.getIosSimulators(), configDeviceName); deviceId = simulator['udid']; // check if current device is pending a locale change - if (locales[0] == utils.getIosSimulatorLocale(deviceId)) { + if (Intl.canonicalizedLocale(locales[0]) == + Intl.canonicalizedLocale(utils.getIosSimulatorLocale(deviceId))) { print('Starting $configDeviceName...'); await startSimulator(daemonClient, deviceId); } else { @@ -161,7 +164,7 @@ Future runTestsOnAll(DaemonClient daemonClient, List devices, List emulators, final deviceType = getDeviceType(configInfo, configDeviceName); // if device is real ios or android, cannot change locale if (device != null && !device['emulator']) { - final defaultLocale = 'en-US'; // todo: need actual locale + final defaultLocale = 'en_US'; // todo: need actual locale of real device print('Warning: the locale of a real device cannot be changed.'); await runProcessTests(config, screens, configDeviceName, defaultLocale, deviceType, testPaths, deviceId, imageProcessor, runMode, archive); @@ -208,7 +211,7 @@ Future runTestsOnAll(DaemonClient daemonClient, List devices, List emulators, if (localeChanged) { // restart simulator print('Restarting \'$configDeviceName\' due to locale change...'); - shutdownSimulator(deviceId); + await shutdownSimulator(deviceId); await startSimulator(daemonClient, deviceId); } } @@ -267,7 +270,7 @@ Future runTestsOnAll(DaemonClient daemonClient, List devices, List emulators, if (simulator != null) { await setSimulatorLocale(deviceId, configDeviceName, origIosLocale, stagingDir, daemonClient); - shutdownSimulator(deviceId); + await shutdownSimulator(deviceId); } } } @@ -297,8 +300,10 @@ Future runProcessTests( } } -void shutdownSimulator(String deviceId) { +Future shutdownSimulator(String deviceId) async { cmd('xcrun', ['simctl', 'shutdown', deviceId]); + // shutdown apparently needs time when restarting + await Future.delayed(Duration(milliseconds: 2000)); } Future startSimulator(DaemonClient daemonClient, String deviceId) async { @@ -351,10 +356,10 @@ Future setSimulatorLocale(String deviceId, String deviceName, String testLocale, String stagingDir, DaemonClient daemonClient) async { // a running simulator final deviceLocale = utils.getIosSimulatorLocale(deviceId); - print('simulator locale=$deviceLocale'); + print('\'$deviceName\' locale: $deviceLocale, test locale: $testLocale'); bool localeChanged = false; - if (testLocale != deviceLocale) { -// if (running) shutdownSimulator(deviceId); + if (Intl.canonicalizedLocale(testLocale) != + Intl.canonicalizedLocale(deviceLocale)) { print( 'Changing locale from $deviceLocale to $testLocale on \'$deviceName\'...'); await _changeSimulatorLocale(stagingDir, deviceId, testLocale); @@ -366,10 +371,11 @@ Future setSimulatorLocale(String deviceId, String deviceName, /// Set the locale of a running emulator. Future setEmulatorLocale(String deviceId, testLocale, deviceName) async { final deviceLocale = utils.getAndroidDeviceLocale(deviceId); - print('emulator locale=$deviceLocale'); + print('\'$deviceName\' locale: $deviceLocale, test locale: $testLocale'); if (deviceLocale != null && deviceLocale != '' && - deviceLocale != testLocale) { + Intl.canonicalizedLocale(deviceLocale) != + Intl.canonicalizedLocale(testLocale)) { // daemonClient.verbose = true; print( 'Changing locale from $deviceLocale to $testLocale on \'$deviceName\'...'); @@ -393,7 +399,7 @@ void changeAndroidLocale( stdout.write( ' https://stackoverflow.com/questions/43923996/adb-root-is-not-working-on-emulator/45668555#45668555 for details.\n'); } - // adb shell "setprop persist.sys.locale fr-CA; setprop ctl.restart zygote" + // adb shell "setprop persist.sys.locale fr_CA; setprop ctl.restart zygote" cmd('adb', [ '-s', deviceId, diff --git a/test/screenshots_test.dart b/test/screenshots_test.dart index c379a6d1..f68391fb 100644 --- a/test/screenshots_test.dart +++ b/test/screenshots_test.dart @@ -292,7 +292,7 @@ void main() { daemonClient.verbose = true; await daemonClient.start; await run.startSimulator(daemonClient, deviceId); - run.shutdownSimulator(deviceId); + await run.shutdownSimulator(deviceId); await daemonClient.stop; }); @@ -389,7 +389,7 @@ void main() { 'flutter', ['-d', deviceId, 'drive', testAppSrcPath], testAppDir); // stop simulator - run.shutdownSimulator(deviceId); + await run.shutdownSimulator(deviceId); // restore orig locale await run.setSimulatorLocale( @@ -694,7 +694,7 @@ devices: DeviceType.ios, orient.Orientation.Portrait, scriptDir: scriptDir); await Future.delayed(Duration(milliseconds: 1000)); - run.shutdownSimulator(deviceId); + await run.shutdownSimulator(deviceId); await daemonClient.stop; }); From 842578705683158916241c1b44b73b6e39cd8b46 Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Wed, 17 Jul 2019 10:58:05 -0700 Subject: [PATCH 19/23] Added note to README about granting permission for AppleScript --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 188a736e..4287efec 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,8 @@ Individual devices can be configured in `screenshots.yaml` by specifying per dev Note: images generated for those devices where framing is disabled are probably not suitable for upload, but can be used for local review. +Note: orientation on iOS simulators is implemented using an AppleScript script which requires granting permission on first use. + # Record/Compare Mode _Screenshots_ can be used to monitor any unexpected changes to the UI by comparing the new screenshots to previously recorded screenshots. Any differences will be highlighted in a 'diff' image for review. From d228757bb18f0fc53c36f1b570d95e5b4c73c870 Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Wed, 17 Jul 2019 11:05:44 -0700 Subject: [PATCH 20/23] Added orientation to screenshots runtime env for tests --- lib/src/config.dart | 3 ++- lib/src/run.dart | 23 +++++++++++++++++------ test/env_test.dart | 4 +++- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/src/config.dart b/lib/src/config.dart index 3e5cc792..bb079bba 100644 --- a/lib/src/config.dart +++ b/lib/src/config.dart @@ -40,7 +40,7 @@ class Config { /// (called by screenshots) @visibleForTesting Future storeEnv(Screens screens, String emulatorName, String locale, - String deviceType) async { + String deviceType, String orientation) async { // store env for later use by tests final screenProps = screens.screenProps(emulatorName); final screenSize = screenProps == null ? null : screenProps['size']; @@ -49,6 +49,7 @@ class Config { 'locale': locale, 'device_name': emulatorName, 'device_type': deviceType, + 'orientation': orientation }; await _envStore.writeAsString(json.encode(currentEnv)); } diff --git a/lib/src/run.dart b/lib/src/run.dart index 54d5641c..15db7c50 100644 --- a/lib/src/run.dart +++ b/lib/src/run.dart @@ -167,7 +167,7 @@ Future runTestsOnAll(DaemonClient daemonClient, List devices, List emulators, final defaultLocale = 'en_US'; // todo: need actual locale of real device print('Warning: the locale of a real device cannot be changed.'); await runProcessTests(config, screens, configDeviceName, defaultLocale, - deviceType, testPaths, deviceId, imageProcessor, runMode, archive); + deviceType, testPaths, deviceId, imageProcessor, runMode, archive, 'unknown'); } else { // Function to check for a running android device or emulator bool isRunningAndroidDeviceOrEmulator(Map device, Map emulator) { @@ -258,8 +258,18 @@ Future runTestsOnAll(DaemonClient daemonClient, List devices, List emulators, } // run tests and process images - await runProcessTests(config, screens, configDeviceName, locale, - deviceType, testPaths, deviceId, imageProcessor, runMode, archive); + await runProcessTests( + config, + screens, + configDeviceName, + locale, + deviceType, + testPaths, + deviceId, + imageProcessor, + runMode, + archive, + deviceOrientation); } // if an emulator was started, revert locale if necessary and shut it down @@ -286,11 +296,12 @@ Future runProcessTests( String deviceId, ImageProcessor imageProcessor, RunMode runMode, - Archive archive) async { + Archive archive, + String orientation) async { // store env for later use by tests // ignore: invalid_use_of_visible_for_testing_member - await config.storeEnv( - screens, configDeviceName, locale, utils.getStringFromEnum(deviceType)); + await config.storeEnv(screens, configDeviceName, locale, + utils.getStringFromEnum(deviceType), orientation); for (final testPath in testPaths) { print('Running $testPath on \'$configDeviceName\' in locale $locale...'); await utils.streamCmd('flutter', ['-d', deviceId, 'drive', testPath]); diff --git a/test/env_test.dart b/test/env_test.dart index 0ce63b18..dbb68dc8 100644 --- a/test/env_test.dart +++ b/test/env_test.dart @@ -7,17 +7,19 @@ void main() { final Config config = Config(configPath: 'test/screenshots_test.yaml'); final screens = await Screens(); await screens.init(); + final orientation='orientation'; final env = { 'screen_size': '1440x2560', 'locale': 'en_US', 'device_name': 'Nexus 6P', 'device_type': 'android', + 'orientation': orientation }; // called by screenshots before test await config.storeEnv( - screens, env['device_name'], env['locale'], env['device_type']); + screens, env['device_name'], env['locale'], env['device_type'], orientation ); // called by test final Config testConfig = Config(configPath: 'test/screenshots_test.yaml'); From aa6b8b55ec66ef09353577694c139ed529c96cc4 Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Wed, 17 Jul 2019 12:44:07 -0700 Subject: [PATCH 21/23] Added device param validation --- lib/src/config.dart | 50 ++++++++++++++++++++++++++++++++ test/screenshots_test.dart | 59 ++++++++++++++++++++++++++++++++------ 2 files changed, 100 insertions(+), 9 deletions(-) diff --git a/lib/src/config.dart b/lib/src/config.dart index bb079bba..13210891 100644 --- a/lib/src/config.dart +++ b/lib/src/config.dart @@ -5,6 +5,7 @@ import 'dart:io'; import 'package:meta/meta.dart'; import 'image_processor.dart'; +import 'orientation.dart'; import 'screens.dart'; import 'package:yaml/yaml.dart'; import 'utils.dart' as utils; @@ -63,6 +64,33 @@ class Config { @visibleForTesting // config is exported in library Future validate( Screens screens, List allDevices, List allEmulators) async { + // validate params + final deviceNames = utils.getAllConfiguredDeviceNames(configInfo); + for (final devName in deviceNames) { + final deviceInfo = findDeviceInfo(configInfo, devName); + if (deviceInfo != null) { + final orientation = deviceInfo['orientation']; + if (orientation != null && !isValidOrientation(orientation)) { + stderr.writeln( + 'Invalid value for \'orientation\' for device \'$devName\': $orientation'); + stderr.writeln('Valid values:'); + for (final orientation in Orientation.values) { + stderr.writeln(' ${utils.getStringFromEnum(orientation)}'); + } + exit(1); + } + final frame = deviceInfo['frame']; + if (frame != null && !isValidFrame(frame)) { + stderr.writeln( + 'Invalid value for \'frame\' for device \'$devName\': $frame'); + stderr.writeln('Valid values:'); + stderr.writeln(' true'); + stderr.writeln(' false'); + exit(1); + } + } + } + final isDeviceAttached = (device) => device != null; final isEmulatorInstalled = (emulator) => emulator != null; @@ -210,3 +238,25 @@ class Config { simulators.forEach((simulator, _) => stdout.write(' $simulator\n')); } } + +bool isValidOrientation(String orientation) { + return Orientation.values.firstWhere( + (o) => utils.getStringFromEnum(o) == orientation, + orElse: () => null) != + null; +} + +bool isValidFrame(dynamic frame) { + return frame != null && (frame == true || frame == false); +} + +/// Find device info in config for device name. +Map findDeviceInfo(Map configInfo, String deviceName) { + Map deviceInfo; + configInfo['devices'].values.forEach((devices) { + devices.forEach((_deviceName, _deviceInfo) { + if (_deviceName == deviceName) deviceInfo = _deviceInfo; + }); + }); + return deviceInfo; +} diff --git a/test/screenshots_test.dart b/test/screenshots_test.dart index f68391fb..a691444d 100644 --- a/test/screenshots_test.dart +++ b/test/screenshots_test.dart @@ -8,6 +8,7 @@ import 'package:screenshots/src/daemon_client.dart'; import 'package:screenshots/src/globals.dart'; import 'package:screenshots/src/image_processor.dart'; import 'package:screenshots/src/orientation.dart' as orient; +import 'package:screenshots/src/orientation.dart'; import 'package:screenshots/src/screens.dart'; import 'package:screenshots/src/resources.dart' as resources; import 'package:screenshots/src/run.dart' as run; @@ -203,15 +204,6 @@ void main() { '/tmp/screenshots/$kTestScreenshotsDir', 'my_prefix'); }); - test('config guide', () async { - final Screens screens = Screens(); - await screens.init(); - final Config config = Config(configPath: 'test/screenshots_test.yaml'); - final daemonClient = DaemonClient(); - await daemonClient.start; - config.generateConfigGuide(screens, await daemonClient.devices); - }); - test('rooted emulator', () async { final emulatorId = 'Nexus_5X_API_27'; final stagingDir = '/tmp/tmp'; @@ -715,4 +707,53 @@ devices: await run.shutdownAndroidEmulator(daemonClient, deviceId), deviceId); }); }); + + group('config validate', () { + test('config guide', () async { + final Screens screens = Screens(); + await screens.init(); + final Config config = Config(configPath: 'test/screenshots_test.yaml'); + final daemonClient = DaemonClient(); + await daemonClient.start; + config.generateConfigGuide(screens, await daemonClient.devices); + }); + + test('validate device params', () { + final deviceName = 'ios device 1'; + final orientation = 'Portrait'; + final frame = true; + final params = ''' + devices: + ios: + $deviceName: + orientation: $orientation + frame: $frame + ios device 2: + android: + android device 1: + android device 2: + '''; + final configInfo = loadYaml(params); + final deviceNames = utils.getAllConfiguredDeviceNames(configInfo); + for (final devName in deviceNames) { + final deviceInfo = findDeviceInfo(configInfo, devName); + print('deviceInfo=$deviceInfo'); + if (deviceInfo != null) { + expect(deviceInfo['orientation'], orientation); + expect(isValidOrientation(orientation), isTrue); + expect(isValidOrientation('bad orientation'), isFalse); + expect(deviceInfo['frame'], frame); + expect(isValidFrame(frame), isTrue); + expect(isValidFrame('bad frame'), isFalse); + } + } + }); + + test('valid values for params', () { + print(Orientation.values); + for (final orientation in Orientation.values) { + print('${utils.getStringFromEnum(orientation)}'); + } + }); + }); } From 9e0fabdfe6553f2d952541b82bebb7d3ee3057a5 Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Wed, 17 Jul 2019 13:15:28 -0700 Subject: [PATCH 22/23] Fixed finding device info from config --- lib/src/config.dart | 10 ++++++---- test/screenshots_test.dart | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/src/config.dart b/lib/src/config.dart index 13210891..76669b97 100644 --- a/lib/src/config.dart +++ b/lib/src/config.dart @@ -253,10 +253,12 @@ bool isValidFrame(dynamic frame) { /// Find device info in config for device name. Map findDeviceInfo(Map configInfo, String deviceName) { Map deviceInfo; - configInfo['devices'].values.forEach((devices) { - devices.forEach((_deviceName, _deviceInfo) { - if (_deviceName == deviceName) deviceInfo = _deviceInfo; - }); + configInfo['devices'].forEach((deviceType, devices) { + if (devices != null) { + devices.forEach((_deviceName, _deviceInfo) { + if (_deviceName == deviceName) deviceInfo = _deviceInfo; + }); + } }); return deviceInfo; } diff --git a/test/screenshots_test.dart b/test/screenshots_test.dart index a691444d..0c111a6d 100644 --- a/test/screenshots_test.dart +++ b/test/screenshots_test.dart @@ -732,11 +732,13 @@ devices: android: android device 1: android device 2: + fuschia: '''; final configInfo = loadYaml(params); final deviceNames = utils.getAllConfiguredDeviceNames(configInfo); for (final devName in deviceNames) { final deviceInfo = findDeviceInfo(configInfo, devName); + print('devName=$devName'); print('deviceInfo=$deviceInfo'); if (deviceInfo != null) { expect(deviceInfo['orientation'], orientation); From a603f5ccb90bbc323c0673648d10ed599e71e39c Mon Sep 17 00:00:00 2001 From: Maurice McCabe Date: Wed, 17 Jul 2019 13:40:41 -0700 Subject: [PATCH 23/23] Fixed getting orientation from config when no orientation --- lib/src/run.dart | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/src/run.dart b/lib/src/run.dart index 15db7c50..1f7acc2b 100644 --- a/lib/src/run.dart +++ b/lib/src/run.dart @@ -166,8 +166,18 @@ Future runTestsOnAll(DaemonClient daemonClient, List devices, List emulators, if (device != null && !device['emulator']) { final defaultLocale = 'en_US'; // todo: need actual locale of real device print('Warning: the locale of a real device cannot be changed.'); - await runProcessTests(config, screens, configDeviceName, defaultLocale, - deviceType, testPaths, deviceId, imageProcessor, runMode, archive, 'unknown'); + await runProcessTests( + config, + screens, + configDeviceName, + defaultLocale, + deviceType, + testPaths, + deviceId, + imageProcessor, + runMode, + archive, + 'unknown'); } else { // Function to check for a running android device or emulator bool isRunningAndroidDeviceOrEmulator(Map device, Map emulator) { @@ -225,9 +235,12 @@ Future runTestsOnAll(DaemonClient daemonClient, List devices, List emulators, } // Change orientation if required - final deviceOrientation = configInfo['devices'] - [utils.getStringFromEnum(deviceType)][configDeviceName] - ['orientation']; + final deviceInfo = configInfo['devices'] + [utils.getStringFromEnum(deviceType)][configDeviceName]; + String deviceOrientation; + deviceInfo != null + ? deviceOrientation = deviceInfo['orientation'] + : null; if (deviceOrientation != null) { final orientation = orient.getOrientationEnum(deviceOrientation); final currentDevice =