Skip to content

Commit

Permalink
Ui tests (#54)
Browse files Browse the repository at this point in the history
* UI tests

* WIP

* Update curves_test.dart

* wip

* wip

* Update README.md

* wip

* Update restart_widget.dart
  • Loading branch information
tomaszpolanski authored Nov 8, 2019
1 parent c86a64c commit 0537786
Show file tree
Hide file tree
Showing 21 changed files with 647 additions and 24 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,13 @@ OR
Open in your ide `flutter-animations`
In IntelliJ/VS Code select appropriate device via device picker and run tha app

# Running Flutter Driver tests on Desktop
### Windows
```
dart .\lib\tests\test.dart -r 800x800
```

### MacOs/Linux
```
dart ./lib/tests/test.dart -r 800x800
```
5 changes: 5 additions & 0 deletions build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
targets:
$default:
sources:
exclude:
- lib/tests/**.dart
50 changes: 39 additions & 11 deletions lib/animations_cheat_sheet.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:animation_cheat_page/animated_widgets/all_animated_widgets.dart'
as animated;
import 'package:animation_cheat_page/config.dart';
import 'package:animation_cheat_page/curves/curves.dart' as curves;
import 'package:animation_cheat_page/curves/curves.dart';
import 'package:animation_cheat_page/curves/curves_page.dart';
Expand All @@ -17,7 +18,12 @@ import 'package:flutter/services.dart';
import 'package:universal_html/html.dart' as html;

class AnimationCheatSheet extends StatelessWidget {
const AnimationCheatSheet({Key key}) : super(key: key);
const AnimationCheatSheet({
Key key,
@required this.config,
}) : super(key: key);

final Configuration config;

static final GlobalKey gKey = GlobalKey<NavigatorState>();

Expand All @@ -35,11 +41,20 @@ class AnimationCheatSheet extends StatelessWidget {
theme: ThemeData(
fontFamily: 'CrimsonPro',
),
initialRoute: '/',
initialRoute: config.route,
onUnknownRoute: (settings) => MaterialPageRoute(
builder: (_) => const Placeholder(),
),
routes: {
'/': (_) => const _PresentationList(),
CurvesPage.route: (_) => const CurvesPage(),
SliverFillRemainingPage.route: (_) => const SliverFillRemainingPage(),
Routes.root: (_) => PresentationList(
repeatAnimations: config.repeatAnimations,
),
Routes.curves: (_) => CurvesPage(
repeatAnimations: config.repeatAnimations,
),
Routes.sliver_fill_remaining: (_) => SliverFillRemainingPage(
repeatAnimations: config.repeatAnimations,
),
},
);
}
Expand All @@ -55,19 +70,29 @@ class NoOverflow extends ScrollBehavior {
child;
}

class _PresentationList extends StatelessWidget {
const _PresentationList({Key key}) : super(key: key);
class PresentationList extends StatelessWidget {
const PresentationList({
Key key,
this.repeatAnimations = true,
}) : super(key: key);

final bool repeatAnimations;

@override
Widget build(BuildContext context) {
return const Scaffold(
body: _AnimationProvider(),
return Scaffold(
body: _AnimationProvider(repeatAnimations: repeatAnimations),
);
}
}

class _AnimationProvider extends StatefulWidget {
const _AnimationProvider({Key key}) : super(key: key);
const _AnimationProvider({
Key key,
@required this.repeatAnimations,
}) : super(key: key);

final bool repeatAnimations;

@override
__AnimationProviderState createState() => __AnimationProviderState();
Expand All @@ -83,7 +108,10 @@ class __AnimationProviderState extends State<_AnimationProvider>
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 3),
)..repeat(reverse: true);
);
if (widget.repeatAnimations) {
_controller.repeat(reverse: true);
}
_headerController = AnimationController(
vsync: this,
duration: const Duration(seconds: 3),
Expand Down
27 changes: 27 additions & 0 deletions lib/config.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class Configuration {
const Configuration({
this.route = '404',
this.repeatAnimations = true,
});

factory Configuration.fromJson(Map<String, dynamic> json) {
return Configuration(
route: json['route'],
repeatAnimations: json['enableAnimations'],
);
}

final String route;
final bool repeatAnimations;

Map<String, dynamic> toJson() => {
'route': route,
'enableAnimations': repeatAnimations,
};
}

class Routes {
static const root = '/';
static const curves = '/curves';
static const sliver_fill_remaining = '/sliver-fill-remaining';
}
9 changes: 5 additions & 4 deletions lib/curves/curves_page.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import 'package:animation_cheat_page/curves/curves.dart';
import 'package:animation_cheat_page/curves/curves.dart' as curves;
import 'package:animation_cheat_page/shared/header_page.dart';
import 'package:flutter/widgets.dart';
import 'package:animation_cheat_page/shared/ui/section.dart';
import 'package:flutter/widgets.dart';

class CurvesPage extends StatelessWidget {
const CurvesPage({Key key}) : super(key: key);

static const String route = '/curves';
const CurvesPage({Key key, @required this.repeatAnimations})
: super(key: key);
final bool repeatAnimations;

@override
Widget build(BuildContext context) {
return HeaderPage(
repeatAnimations: repeatAnimations,
builder: (animation, child) {
return Column(
children: [
Expand Down
7 changes: 6 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import 'package:animation_cheat_page/animations_cheat_sheet.dart';
import 'package:animation_cheat_page/config.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';

void main() {
debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
runApp(const AnimationCheatSheet());
runApp(
const AnimationCheatSheet(
config: Configuration(route: Routes.curves),
),
);
}
7 changes: 6 additions & 1 deletion lib/shared/header_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ class HeaderPage extends StatefulWidget {
const HeaderPage({
Key key,
@required this.builder,
@required this.repeatAnimations,
}) : super(key: key);

final AnimatedWidgetBuilder builder;
final bool repeatAnimations;

@override
_HeaderPageState createState() => _HeaderPageState();
Expand All @@ -23,7 +25,10 @@ class _HeaderPageState extends State<HeaderPage> with TickerProviderStateMixin {
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 3),
)..repeat(reverse: true);
);
if (widget.repeatAnimations) {
_controller.repeat(reverse: true);
}
_headerController = AnimationController(
vsync: this,
duration: const Duration(seconds: 3),
Expand Down
31 changes: 31 additions & 0 deletions lib/tests/src/commands.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import 'dart:io';

class Commands {
Flutter get flutter => const Flutter._();
}

class Flutter {
const Flutter._();

String run(String target) {
return 'flutter run -d $_device --target=$target';
}

String attach(String debugUri) {
return 'flutter attach -d $_device --debug-uri $debugUri';
}

String dart(String file, [List<String> arguments]) {
return 'dart $file ${arguments != null ? arguments.join(' ') : ''}';
}

String get _device {
if (Platform.isWindows) {
return 'windows';
} else if (Platform.isLinux) {
return 'linux';
} else {
return 'macos';
}
}
}
24 changes: 24 additions & 0 deletions lib/tests/src/file_system.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import 'dart:io';

String platformPath(String path) {
return Platform.isWindows ? path.replaceAll('/', '\\') : path;
}

bool exists(String path) =>
path != null && File(platformPath(path)).existsSync();

String get platformNativeFile {
return platformPath('${Directory.current.path}/$_nativeFile');
}

String get _nativeFile {
if (Platform.isWindows) {
return 'windows/window_configuration.cpp';
} else if (Platform.isLinux) {
return 'linux/main.cc';
} else if (Platform.isMacOS) {
return 'macos/Runner/MainFlutterWindow.swift';
}
assert(false);
return null;
}
40 changes: 40 additions & 0 deletions lib/tests/src/restart_widget.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

class RestartWidget<T> extends StatelessWidget {
const RestartWidget({
Key key,
@required this.stream,
@required this.builder,
@required this.initialData,
}) : super(key: key);

final T initialData;
final Stream<T> stream;
final Widget Function(BuildContext, T) builder;

Stream<T> _invalidate(T config) async* {
yield null;
await Future.delayed(const Duration(milliseconds: 16));
yield config;
}

@override
Widget build(BuildContext context) {
return StreamBuilder<T>(
stream: stream,
initialData: initialData,
builder: (context, snapshot) {
return StreamBuilder(
initialData: snapshot.data,
stream: _invalidate(snapshot.data),
builder: (context, snapshot) {
return snapshot.data != null
? builder(context, snapshot.data)
: const SizedBox();
},
);
},
);
}
}
70 changes: 70 additions & 0 deletions lib/tests/src/test_properties.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import 'dart:math';

import 'package:args/args.dart';

const url = 'url';
const resolutionArg = 'resolution';

ArgParser testParser = ArgParser()
..addOption(
url,
abbr: url[0],
help: 'Url for dartVmServiceUrl',
)
..addOption(
resolutionArg,
abbr: resolutionArg[0],
help: 'Resolution of device',
);

class TestProperties {
TestProperties(List<String> args) : _arguments = testParser.parse(args);

final ArgResults _arguments;

String get vmUrl => _arguments[url];

Resolution get resolution => Resolution.fromSize(_arguments[resolutionArg]);
}

enum DeviceOrientation {
portraitPhone,
landscapePhone,
portraitTablet,
landscapeTablet,
}

const wideLayoutThreshold = 550;

class Resolution {
const Resolution(this.width, this.height);

factory Resolution.fromSize(String screenResolution) {
final dimensions = screenResolution.split('x');
return Resolution(double.parse(dimensions[0]), double.parse(dimensions[1]));
}

final double width;
final double height;

double get shortestSide => min(width.abs(), height.abs());

bool get isTablet => shortestSide >= wideLayoutThreshold;

DeviceOrientation get orientation {
if (isTablet) {
return width < height
? DeviceOrientation.portraitTablet
: DeviceOrientation.landscapeTablet;
} else {
return width < height
? DeviceOrientation.portraitPhone
: DeviceOrientation.landscapePhone;
}
}

Map<String, dynamic> toJson() => {
'width': width.floor(),
'height': height.floor(),
};
}
Loading

0 comments on commit 0537786

Please sign in to comment.