diff --git a/lib/widgets/album/app_bar.dart b/lib/widgets/album/app_bar.dart index af6226ebf..0ad3967a1 100644 --- a/lib/widgets/album/app_bar.dart +++ b/lib/widgets/album/app_bar.dart @@ -186,7 +186,7 @@ class _CollectionAppBarState extends State with SingleTickerPr )), Builder( builder: (context) => PopupMenuButton( - key: Key('menu-button'), + key: Key('appbar-menu-button'), itemBuilder: (context) { final hasSelection = collection.selection.isNotEmpty; return [ diff --git a/lib/widgets/fullscreen/fullscreen_body.dart b/lib/widgets/fullscreen/fullscreen_body.dart index 0e32a87b9..88289d961 100644 --- a/lib/widgets/fullscreen/fullscreen_body.dart +++ b/lib/widgets/fullscreen/fullscreen_body.dart @@ -499,6 +499,7 @@ class _FullscreenVerticalPageViewState extends State child: child, ), child: PageView( + key: Key('vertical-pageview'), scrollDirection: Axis.vertical, controller: widget.verticalPager, physics: PhotoViewPageViewScrollPhysics(parent: PageScrollPhysics()), diff --git a/lib/widgets/fullscreen/image_page.dart b/lib/widgets/fullscreen/image_page.dart index c40291ee5..49f7ecefe 100644 --- a/lib/widgets/fullscreen/image_page.dart +++ b/lib/widgets/fullscreen/image_page.dart @@ -37,6 +37,7 @@ class MultiImagePageState extends State with AutomaticKeepAliveC return PhotoViewGestureDetectorScope( axis: [Axis.horizontal, Axis.vertical], child: PageView.builder( + key: Key('horizontal-pageview'), scrollDirection: Axis.horizontal, controller: widget.pageController, physics: PhotoViewPageViewScrollPhysics(parent: BouncingScrollPhysics()), @@ -45,6 +46,7 @@ class MultiImagePageState extends State with AutomaticKeepAliveC final entry = entries[index]; return ClipRect( child: ImageView( + key: Key('imageview'), entry: entry, heroTag: widget.collection.heroTag(entry), onScaleChanged: widget.onScaleChanged, diff --git a/lib/widgets/fullscreen/image_view.dart b/lib/widgets/fullscreen/image_view.dart index 91f84b82a..0dfba04a8 100644 --- a/lib/widgets/fullscreen/image_view.dart +++ b/lib/widgets/fullscreen/image_view.dart @@ -20,12 +20,13 @@ class ImageView extends StatelessWidget { final List> videoControllers; const ImageView({ + Key key, this.entry, this.heroTag, this.onScaleChanged, this.onTap, this.videoControllers, - }); + }) : super(key: key); @override Widget build(BuildContext context) { diff --git a/lib/widgets/fullscreen/overlay/top.dart b/lib/widgets/fullscreen/overlay/top.dart index 0d01f4acd..e46490484 100644 --- a/lib/widgets/fullscreen/overlay/top.dart +++ b/lib/widgets/fullscreen/overlay/top.dart @@ -133,6 +133,7 @@ class _TopOverlayRow extends StatelessWidget { OverlayButton( scale: scale, child: PopupMenuButton( + key: Key('entry-menu-button'), itemBuilder: (context) => [ ...inAppActions.map(_buildPopupMenuItem), PopupMenuDivider(), diff --git a/test_driver/app_test.dart b/test_driver/app_test.dart index 32945ebea..51474292d 100644 --- a/test_driver/app_test.dart +++ b/test_driver/app_test.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:aves/model/source/enums.dart'; import 'package:flutter_driver/flutter_driver.dart'; import 'package:path/path.dart' as path; @@ -14,6 +12,7 @@ void main() { FlutterDriver driver; setUpAll(() async { + print('adb=${[adb, ...adbDeviceParam].join(' ')}'); await copyContent(sourcePicturesDir, targetPicturesDir); await grantPermissions('deckers.thibault.aves.debug', [ 'android.permission.READ_EXTERNAL_STORAGE', @@ -30,8 +29,6 @@ void main() { } }); - final appBarTitleFinder = find.byValueKey('appbar-title'); - test('agree to terms and reach home', () async { await driver.scroll(find.text('Terms of Service'), 0, -300, Duration(milliseconds: 500)); @@ -39,16 +36,17 @@ void main() { expect(await isEnabled(driver, buttonFinder), equals(false)); await driver.tap(find.byValueKey('agree-checkbox')); - await driver.waitUntilNoTransientCallbacks(); + await Future.delayed(Duration(seconds: 1)); expect(await isEnabled(driver, buttonFinder), equals(true)); await driver.tap(buttonFinder); await driver.waitUntilNoTransientCallbacks(); - expect(await driver.getText(appBarTitleFinder), 'Aves'); + + expect(await driver.getText(find.byValueKey('appbar-title')), 'Aves'); }); - test('sort and group', () async { - await driver.tap(find.byValueKey('menu-button')); + test('sort collection', () async { + await driver.tap(find.byValueKey('appbar-menu-button')); await driver.waitUntilNoTransientCallbacks(); await driver.tap(find.byValueKey('menu-sort')); @@ -56,8 +54,10 @@ void main() { await driver.tap(find.byValueKey(SortFactor.date.toString())); await driver.tap(find.byValueKey('apply-button')); + }); - await driver.tap(find.byValueKey('menu-button')); + test('group collection', () async { + await driver.tap(find.byValueKey('appbar-menu-button')); await driver.waitUntilNoTransientCallbacks(); await driver.tap(find.byValueKey('menu-group')); @@ -81,7 +81,32 @@ void main() { }); test('show fullscreen', () async { - sleep(Duration(seconds: 5)); + await driver.tap(find.byType('DecoratedThumbnail')); + await driver.waitUntilNoTransientCallbacks(); + await Future.delayed(Duration(seconds: 2)); + + final imageViewFinder = find.byValueKey('imageview'); + + print('* hide overlay'); + await driver.tap(imageViewFinder); + await Future.delayed(Duration(seconds: 1)); + + print('* show overlay'); + await driver.tap(imageViewFinder); + await Future.delayed(Duration(seconds: 1)); + }); + + test('show info', () async { + final verticalPageViewFinder = find.byValueKey('vertical-pageview'); + + print('* scroll to info'); + await driver.scroll(verticalPageViewFinder, 0, -600, Duration(milliseconds: 400)); + await Future.delayed(Duration(seconds: 1)); + + // TODO TLAD find.pageBack +// print('* back to image'); +// await driver.tap(find.pageBack()); +// await Future.delayed(Duration(seconds: 5)); }); }, timeout: Timeout(Duration(seconds: 10))); } diff --git a/test_driver/utils.dart b/test_driver/utils.dart index 38a33010b..62d5b5d8d 100644 --- a/test_driver/utils.dart +++ b/test_driver/utils.dart @@ -3,18 +3,40 @@ import 'dart:io'; import 'package:flutter_driver/flutter_driver.dart'; import 'package:path/path.dart' as path; +// conditions + +Future isEnabled(FlutterDriver driver, SerializableFinder widgetFinder) async { + Map widgetDiagnostics = await driver.getWidgetDiagnostics(widgetFinder); + return widgetDiagnostics['properties'].firstWhere((property) => property['name'] == 'enabled')['value']; +} + +// ADB + String get adb { final env = Platform.environment; final sdkDir = env['ANDROID_SDK_ROOT'] ?? env['ANDROID_SDK']; return path.join(sdkDir, 'platform-tools', Platform.isWindows ? 'adb.exe' : 'adb'); } +/* + If there's only one emulator running or only one device connected, + the adb command is sent to that device by default. + If multiple emulators are running and/or multiple devices are attached, + you need to use the -d, -e, or -s option to specify the target device + to which the command should be directed. + */ +const List adbDeviceParam = ['-e']; // '[-d]', '[-e]', or '[-s, ]' + +Future runAdb(List args) async { + await Process.runSync(adb, [...adbDeviceParam, ...args]); +} + Future createDirectory(String dir) async { - await Process.run(adb, ['shell', 'mkdir -p', dir.replaceAll(' ', '\\ ')]); + await runAdb(['shell', 'mkdir -p', dir.replaceAll(' ', '\\ ')]); } Future removeDirectory(String dir) async { - await Process.run(adb, ['shell', 'rm -r', dir.replaceAll(' ', '\\ ')]); + await runAdb(['shell', 'rm -r', dir.replaceAll(' ', '\\ ')]); } Future copyContent(String sourceDir, String targetDir) async { @@ -22,14 +44,9 @@ Future copyContent(String sourceDir, String targetDir) async { // `push source/* target/` works only when the target directory exists, and fails when `target` contains spaces // `push source/ target/` works only when the target directory does not exist await removeDirectory(targetDir); - await Process.run(adb, ['push', sourceDir, targetDir]); + await runAdb(['push', sourceDir, targetDir]); } Future grantPermissions(String packageName, Iterable permissions) async { - await Future.forEach(permissions, (permission) => Process.run(adb, ['shell', 'pm', 'grant', packageName, permission])); -} - -Future isEnabled(FlutterDriver driver, SerializableFinder widgetFinder) async { - Map widgetDiagnostics = await driver.getWidgetDiagnostics(widgetFinder); - return widgetDiagnostics['properties'].firstWhere((property) => property['name'] == 'enabled')['value']; + await Future.forEach(permissions, (permission) => runAdb(['shell', 'pm', 'grant', packageName, permission])); }