live_smoketest.dart 4.19 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_test/flutter_test.dart';

13 14
import 'package:flutter_gallery/gallery/demos.dart';
import 'package:flutter_gallery/gallery/app.dart' show GalleryApp;
15

16
// Reports success or failure to the native code.
17
const MethodChannel _kTestChannel = MethodChannel('io.flutter.demo.gallery/TestLifecycleListener');
18

19 20
// We don't want to wait for animations to complete before tapping the
// back button in the demos with these titles.
21
const List<String> _kUnsynchronizedDemoTitles = <String>[
22 23 24 25 26 27 28
  'Progress indicators',
  'Activity Indicator',
  'Video',
];

// These demos can't be backed out of by tapping a button whose
// tooltip is 'Back'.
29
const List<String> _kSkippedDemoTitles = <String>[
30
  'Pull to refresh',
31 32 33
  'Progress indicators',
  'Activity Indicator',
  'Video',
34 35
];

36 37
Future<Null> main() async {
  try {
38 39
    // Verify that _kUnsynchronizedDemos and _kSkippedDemos identify
    // demos that actually exist.
40 41 42 43 44
    final List<String> allDemoTitles = kAllGalleryDemos.map((GalleryDemo demo) => demo.title).toList();
    if (!new Set<String>.from(allDemoTitles).containsAll(_kUnsynchronizedDemoTitles))
      fail('Unrecognized demo titles in _kUnsynchronizedDemosTitles: $_kUnsynchronizedDemoTitles');
    if (!new Set<String>.from(allDemoTitles).containsAll(_kSkippedDemoTitles))
      fail('Unrecognized demo names in _kSkippedDemoTitles: $_kSkippedDemoTitles');
45

46
    runApp(const GalleryApp(testMode: true));
47
    final _LiveWidgetController controller = new _LiveWidgetController();
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
    for (GalleryDemoCategory category in kAllGalleryDemoCategories) {
      await controller.tap(find.text(category.name));
      for (GalleryDemo demo in kGalleryCategoryToDemos[category]) {
        final Finder demoItem = find.text(demo.title);
        await controller.scrollIntoView(demoItem, alignment: 0.5);

        if (_kSkippedDemoTitles.contains(demo.title)) {
          print('> skipped $demo');
          continue;
        }

        for (int i = 0; i < 2; i += 1) {
          await controller.tap(demoItem); // Launch the demo
          controller.frameSync = !_kUnsynchronizedDemoTitles.contains(demo.title);
          await controller.tap(find.byTooltip('Back'));
          controller.frameSync = true;
        }
        print('Success');
66
      }
67
      await controller.tap(find.byTooltip('Back'));
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
    }

    _kTestChannel.invokeMethod('success');
  } catch (error) {
    _kTestChannel.invokeMethod('failure');
  }
}

class _LiveWidgetController {

  final WidgetController _controller = new WidgetController(WidgetsBinding.instance);

  /// 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<Null> _waitUntilFrame(bool condition(), [Completer<Null> completer]) {
    completer ??= new Completer<Null>();
    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(() => SchedulerBinding.instance.transientCallbackCount == 0);

    await _waitUntilFrame(() => finder.precache());

    if (frameSync)
      await _waitUntilFrame(() => SchedulerBinding.instance.transientCallbackCount == 0);

    return finder;
  }

  Future<Null> tap(Finder finder) async {
    await _controller.tap(await _waitForElement(finder));
  }

  Future<Null> 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);
  }
}