mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
145 lines
5.6 KiB
Dart
145 lines
5.6 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// ATTENTION!
|
|
//
|
|
// This file is not named "*_test.dart", and as such will not run when you run
|
|
// "flutter test". It is only intended to be run as part of the
|
|
// flutter_gallery_instrumentation_test devicelab test.
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/scheduler.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter/widgets.dart';
|
|
import 'package:flutter/gestures.dart' show kPrimaryButton;
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import 'package:flutter_gallery/gallery/demos.dart';
|
|
import 'package:flutter_gallery/gallery/app.dart' show GalleryApp;
|
|
|
|
// Reports success or failure to the native code.
|
|
const MethodChannel _kTestChannel = MethodChannel('io.flutter.demo.gallery/TestLifecycleListener');
|
|
|
|
// We don't want to wait for animations to complete before tapping the
|
|
// back button in the demos with these titles.
|
|
const List<String> _kUnsynchronizedDemoTitles = <String>[
|
|
'Progress indicators',
|
|
'Activity Indicator',
|
|
'Video',
|
|
];
|
|
|
|
// These demos can't be backed out of by tapping a button whose
|
|
// tooltip is 'Back'.
|
|
const List<String> _kSkippedDemoTitles = <String>[
|
|
'Progress indicators',
|
|
'Activity Indicator',
|
|
'Video',
|
|
];
|
|
|
|
// There are 3 places where the Gallery demos are traversed.
|
|
// 1- In widget tests such as examples/flutter_gallery/test/smoke_test.dart
|
|
// 2- In driver tests such as examples/flutter_gallery/test_driver/transitions_perf_test.dart
|
|
// 3- In on-device instrumentation tests such as examples/flutter_gallery/test/live_smoketest.dart
|
|
//
|
|
// If you change navigation behavior in the Gallery or in the framework, make
|
|
// sure all 3 are covered.
|
|
|
|
Future<void> main() async {
|
|
try {
|
|
// Verify that _kUnsynchronizedDemos and _kSkippedDemos identify
|
|
// demos that actually exist.
|
|
final List<String> allDemoTitles = kAllGalleryDemos.map((GalleryDemo demo) => demo.title).toList();
|
|
if (!Set<String>.from(allDemoTitles).containsAll(_kUnsynchronizedDemoTitles))
|
|
fail('Unrecognized demo titles in _kUnsynchronizedDemosTitles: $_kUnsynchronizedDemoTitles');
|
|
if (!Set<String>.from(allDemoTitles).containsAll(_kSkippedDemoTitles))
|
|
fail('Unrecognized demo names in _kSkippedDemoTitles: $_kSkippedDemoTitles');
|
|
|
|
print('Starting app...');
|
|
runApp(const GalleryApp(testMode: true));
|
|
final _LiveWidgetController controller = _LiveWidgetController(WidgetsBinding.instance);
|
|
for (final GalleryDemoCategory category in kAllGalleryDemoCategories) {
|
|
print('Tapping "${category.name}" section...');
|
|
await controller.tap(find.text(category.name));
|
|
for (final GalleryDemo demo in kGalleryCategoryToDemos[category]) {
|
|
final Finder demoItem = find.text(demo.title);
|
|
print('Scrolling to "${demo.title}"...');
|
|
await controller.scrollIntoView(demoItem, alignment: 0.5);
|
|
if (_kSkippedDemoTitles.contains(demo.title))
|
|
continue;
|
|
for (int i = 0; i < 2; i += 1) {
|
|
print('Tapping "${demo.title}"...');
|
|
await controller.tap(demoItem); // Launch the demo
|
|
controller.frameSync = !_kUnsynchronizedDemoTitles.contains(demo.title);
|
|
print('Going back to demo list...');
|
|
await controller.tap(backFinder);
|
|
controller.frameSync = true;
|
|
}
|
|
}
|
|
print('Going back to home screen...');
|
|
await controller.tap(find.byTooltip('Back'));
|
|
}
|
|
print('Finished successfully!');
|
|
_kTestChannel.invokeMethod<void>('success');
|
|
} catch (error, stack) {
|
|
print('Caught error: $error\n$stack');
|
|
_kTestChannel.invokeMethod<void>('failure');
|
|
}
|
|
}
|
|
|
|
final Finder backFinder = find.byElementPredicate(
|
|
(Element element) {
|
|
final Widget widget = element.widget;
|
|
if (widget is Tooltip)
|
|
return widget.message == 'Back';
|
|
if (widget is CupertinoNavigationBarBackButton)
|
|
return true;
|
|
return false;
|
|
},
|
|
description: 'Material or Cupertino back button',
|
|
);
|
|
|
|
class _LiveWidgetController extends LiveWidgetController {
|
|
_LiveWidgetController(WidgetsBinding binding) : super(binding);
|
|
|
|
/// With [frameSync] enabled, Flutter Driver will wait to perform an action
|
|
/// until there are no pending frames in the app under test.
|
|
bool frameSync = true;
|
|
|
|
/// Waits until at the end of a frame the provided [condition] is [true].
|
|
Future<void> _waitUntilFrame(bool condition(), [Completer<void> completer]) {
|
|
completer ??= Completer<void>();
|
|
if (!condition()) {
|
|
SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) {
|
|
_waitUntilFrame(condition, completer);
|
|
});
|
|
} else {
|
|
completer.complete();
|
|
}
|
|
return completer.future;
|
|
}
|
|
|
|
/// Runs `finder` repeatedly until it finds one or more [Element]s.
|
|
Future<Finder> _waitForElement(Finder finder) async {
|
|
if (frameSync)
|
|
await _waitUntilFrame(() => binding.transientCallbackCount == 0);
|
|
await _waitUntilFrame(() => finder.precache());
|
|
if (frameSync)
|
|
await _waitUntilFrame(() => binding.transientCallbackCount == 0);
|
|
return finder;
|
|
}
|
|
|
|
@override
|
|
Future<void> tap(Finder finder, { int pointer, int buttons = kPrimaryButton }) async {
|
|
await super.tap(await _waitForElement(finder), pointer: pointer, buttons: buttons);
|
|
}
|
|
|
|
Future<void> scrollIntoView(Finder finder, {double alignment}) async {
|
|
final Finder target = await _waitForElement(finder);
|
|
await Scrollable.ensureVisible(target.evaluate().single, duration: const Duration(milliseconds: 100), alignment: alignment);
|
|
}
|
|
}
|