blob: 27417e21797bc48dfeba127f203204c5e6532d89 [file] [log] [blame]
// Copyright 2018 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 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Can open and close search', (WidgetTester tester) async {
final _TestSearchDelegate delegate = new _TestSearchDelegate();
final List<String> selectedResults = <String>[];
await tester.pumpWidget(new TestHomePage(
delegate: delegate,
results: selectedResults,
));
// We are on the homepage
expect(find.text('HomeBody'), findsOneWidget);
expect(find.text('HomeTitle'), findsOneWidget);
expect(find.text('Suggestions'), findsNothing);
// Open search
await tester.tap(find.byTooltip('Search'));
await tester.pumpAndSettle();
expect(find.text('HomeBody'), findsNothing);
expect(find.text('HomeTitle'), findsNothing);
expect(find.text('Suggestions'), findsOneWidget);
expect(selectedResults, hasLength(0));
final TextField textField = tester.widget(find.byType(TextField));
expect(textField.focusNode.hasFocus, isTrue);
// Close search
await tester.tap(find.byTooltip('Back'));
await tester.pumpAndSettle();
expect(find.text('HomeBody'), findsOneWidget);
expect(find.text('HomeTitle'), findsOneWidget);
expect(find.text('Suggestions'), findsNothing);
expect(selectedResults, <String>['Result']);
});
testWidgets('Can close search with system back button to return null', (WidgetTester tester) async {
// regression test for https://github.com/flutter/flutter/issues/18145
final _TestSearchDelegate delegate = new _TestSearchDelegate();
final List<String> selectedResults = <String>[];
await tester.pumpWidget(new TestHomePage(
delegate: delegate,
results: selectedResults,
));
// We are on the homepage
expect(find.text('HomeBody'), findsOneWidget);
expect(find.text('HomeTitle'), findsOneWidget);
expect(find.text('Suggestions'), findsNothing);
// Open search
await tester.tap(find.byTooltip('Search'));
await tester.pumpAndSettle();
expect(find.text('HomeBody'), findsNothing);
expect(find.text('HomeTitle'), findsNothing);
expect(find.text('Suggestions'), findsOneWidget);
// Simulate system back button
final ByteData message = const JSONMethodCodec().encodeMethodCall(const MethodCall('popRoute'));
await BinaryMessages.handlePlatformMessage('flutter/navigation', message, (_) {});
await tester.pumpAndSettle();
expect(selectedResults, <void>[null]);
// We are on the homepage again
expect(find.text('HomeBody'), findsOneWidget);
expect(find.text('HomeTitle'), findsOneWidget);
expect(find.text('Suggestions'), findsNothing);
// Open search again
await tester.tap(find.byTooltip('Search'));
await tester.pumpAndSettle();
expect(find.text('HomeBody'), findsNothing);
expect(find.text('HomeTitle'), findsNothing);
expect(find.text('Suggestions'), findsOneWidget);
});
testWidgets('Requests suggestions', (WidgetTester tester) async {
final _TestSearchDelegate delegate = new _TestSearchDelegate();
await tester.pumpWidget(new TestHomePage(
delegate: delegate,
));
await tester.tap(find.byTooltip('Search'));
await tester.pumpAndSettle();
expect(delegate.query, '');
expect(delegate.querysForSuggestions.last, '');
expect(delegate.querysForResults, hasLength(0));
// Type W o w into search field
delegate.querysForSuggestions.clear();
await tester.enterText(find.byType(TextField), 'W');
await tester.pumpAndSettle();
expect(delegate.query, 'W');
await tester.enterText(find.byType(TextField), 'Wo');
await tester.pumpAndSettle();
expect(delegate.query, 'Wo');
await tester.enterText(find.byType(TextField), 'Wow');
await tester.pumpAndSettle();
expect(delegate.query, 'Wow');
expect(delegate.querysForSuggestions, <String>['W', 'Wo', 'Wow']);
expect(delegate.querysForResults, hasLength(0));
});
testWidgets('Shows Results and closes search', (WidgetTester tester) async {
final _TestSearchDelegate delegate = new _TestSearchDelegate();
final List<String> selectedResults = <String>[];
await tester.pumpWidget(new TestHomePage(
delegate: delegate,
results: selectedResults,
));
await tester.tap(find.byTooltip('Search'));
await tester.pumpAndSettle();
await tester.enterText(find.byType(TextField), 'Wow');
await tester.pumpAndSettle();
await tester.tap(find.text('Suggestions'));
await tester.pumpAndSettle();
// We are on the results page for Wow
expect(find.text('HomeBody'), findsNothing);
expect(find.text('HomeTitle'), findsNothing);
expect(find.text('Suggestions'), findsNothing);
expect(find.text('Results'), findsOneWidget);
final TextField textField = tester.widget(find.byType(TextField));
expect(textField.focusNode.hasFocus, isFalse);
expect(delegate.querysForResults, <String>['Wow']);
// Close search
await tester.tap(find.byTooltip('Back'));
await tester.pumpAndSettle();
expect(find.text('HomeBody'), findsOneWidget);
expect(find.text('HomeTitle'), findsOneWidget);
expect(find.text('Suggestions'), findsNothing);
expect(find.text('Results'), findsNothing);
expect(selectedResults, <String>['Result']);
});
testWidgets('Can switch between results and suggestions',
(WidgetTester tester) async {
final _TestSearchDelegate delegate = new _TestSearchDelegate();
await tester.pumpWidget(new TestHomePage(
delegate: delegate,
));
await tester.tap(find.byTooltip('Search'));
await tester.pumpAndSettle();
// Showing suggestions
expect(find.text('Suggestions'), findsOneWidget);
expect(find.text('Results'), findsNothing);
// Typing query Wow
delegate.querysForSuggestions.clear();
await tester.enterText(find.byType(TextField), 'Wow');
await tester.pumpAndSettle();
expect(delegate.query, 'Wow');
expect(delegate.querysForSuggestions, <String>['Wow']);
expect(delegate.querysForResults, hasLength(0));
await tester.tap(find.text('Suggestions'));
await tester.pumpAndSettle();
// Showing Results
expect(find.text('Suggestions'), findsNothing);
expect(find.text('Results'), findsOneWidget);
expect(delegate.query, 'Wow');
expect(delegate.querysForSuggestions, <String>['Wow']);
expect(delegate.querysForResults, <String>['Wow']);
TextField textField = tester.widget(find.byType(TextField));
expect(textField.focusNode.hasFocus, isFalse);
// Taping search field to go back to suggestions
await tester.tap(find.byType(TextField));
await tester.pumpAndSettle();
textField = tester.widget(find.byType(TextField));
expect(textField.focusNode.hasFocus, isTrue);
expect(find.text('Suggestions'), findsOneWidget);
expect(find.text('Results'), findsNothing);
expect(delegate.querysForSuggestions, <String>['Wow', 'Wow']);
expect(delegate.querysForResults, <String>['Wow']);
await tester.enterText(find.byType(TextField), 'Foo');
await tester.pumpAndSettle();
expect(delegate.query, 'Foo');
expect(delegate.querysForSuggestions, <String>['Wow', 'Wow', 'Foo']);
expect(delegate.querysForResults, <String>['Wow']);
// Go to results again
await tester.tap(find.text('Suggestions'));
await tester.pumpAndSettle();
expect(find.text('Suggestions'), findsNothing);
expect(find.text('Results'), findsOneWidget);
expect(delegate.query, 'Foo');
expect(delegate.querysForSuggestions, <String>['Wow', 'Wow', 'Foo']);
expect(delegate.querysForResults, <String>['Wow', 'Foo']);
textField = tester.widget(find.byType(TextField));
expect(textField.focusNode.hasFocus, isFalse);
});
testWidgets('Fresh search allways starts with empty query',
(WidgetTester tester) async {
final _TestSearchDelegate delegate = new _TestSearchDelegate();
await tester.pumpWidget(new TestHomePage(
delegate: delegate,
));
await tester.tap(find.byTooltip('Search'));
await tester.pumpAndSettle();
expect(delegate.query, '');
delegate.query = 'Foo';
await tester.tap(find.byTooltip('Back'));
await tester.pumpAndSettle();
await tester.tap(find.byTooltip('Search'));
await tester.pumpAndSettle();
expect(delegate.query, '');
});
testWidgets('Initial queries are honored', (WidgetTester tester) async {
final _TestSearchDelegate delegate = new _TestSearchDelegate();
expect(delegate.query, '');
await tester.pumpWidget(new TestHomePage(
delegate: delegate,
passInInitialQuery: true,
initialQuery: 'Foo',
));
await tester.tap(find.byTooltip('Search'));
await tester.pumpAndSettle();
expect(delegate.query, 'Foo');
});
testWidgets('Initial query null re-used previous query', (WidgetTester tester) async {
final _TestSearchDelegate delegate = new _TestSearchDelegate();
delegate.query = 'Foo';
await tester.pumpWidget(new TestHomePage(
delegate: delegate,
passInInitialQuery: true,
initialQuery: null,
));
await tester.tap(find.byTooltip('Search'));
await tester.pumpAndSettle();
expect(delegate.query, 'Foo');
});
testWidgets('Changing query shows up in search field', (WidgetTester tester) async {
final _TestSearchDelegate delegate = new _TestSearchDelegate();
await tester.pumpWidget(new TestHomePage(
delegate: delegate,
passInInitialQuery: true,
initialQuery: null,
));
await tester.tap(find.byTooltip('Search'));
await tester.pumpAndSettle();
delegate.query = 'Foo';
expect(find.text('Foo'), findsOneWidget);
expect(find.text('Bar'), findsNothing);
delegate.query = 'Bar';
expect(find.text('Foo'), findsNothing);
expect(find.text('Bar'), findsOneWidget);
});
testWidgets('transitionAnimation runs while search fades in/out', (WidgetTester tester) async {
final _TestSearchDelegate delegate = new _TestSearchDelegate();
await tester.pumpWidget(new TestHomePage(
delegate: delegate,
passInInitialQuery: true,
initialQuery: null,
));
// runs while search fades in
expect(delegate.transitionAnimation.status, AnimationStatus.dismissed);
await tester.tap(find.byTooltip('Search'));
expect(delegate.transitionAnimation.status, AnimationStatus.forward);
await tester.pumpAndSettle();
expect(delegate.transitionAnimation.status, AnimationStatus.completed);
// does not run while switching to results
await tester.tap(find.text('Suggestions'));
expect(delegate.transitionAnimation.status, AnimationStatus.completed);
await tester.pumpAndSettle();
expect(delegate.transitionAnimation.status, AnimationStatus.completed);
// runs while search fades out
await tester.tap(find.byTooltip('Back'));
expect(delegate.transitionAnimation.status, AnimationStatus.reverse);
await tester.pumpAndSettle();
expect(delegate.transitionAnimation.status, AnimationStatus.dismissed);
});
testWidgets('Closing nested search returns to search', (WidgetTester tester) async {
final List<String> nestedSearchResults = <String>[];
final _TestSearchDelegate nestedSearchDelegate = new _TestSearchDelegate(
suggestions: 'Nested Suggestions',
result: 'Nested Result',
);
final List<String> selectedResults = <String>[];
final _TestSearchDelegate delegate = new _TestSearchDelegate(
actions: <Widget>[
new Builder(
builder: (BuildContext context) {
return new IconButton(
tooltip: 'Nested Search',
icon: const Icon(Icons.search),
onPressed: () async {
final String result = await showSearch(
context: context,
delegate: nestedSearchDelegate,
);
nestedSearchResults.add(result);
},
);
},
)
],
);
await tester.pumpWidget(new TestHomePage(
delegate: delegate,
results: selectedResults,
));
expect(find.text('HomeBody'), findsOneWidget);
await tester.tap(find.byTooltip('Search'));
await tester.pumpAndSettle();
expect(find.text('HomeBody'), findsNothing);
expect(find.text('Suggestions'), findsOneWidget);
expect(find.text('Nested Suggestions'), findsNothing);
await tester.tap(find.byTooltip('Nested Search'));
await tester.pumpAndSettle();
expect(find.text('HomeBody'), findsNothing);
expect(find.text('Suggestions'), findsNothing);
expect(find.text('Nested Suggestions'), findsOneWidget);
await tester.tap(find.byTooltip('Back'));
await tester.pumpAndSettle();
expect(nestedSearchResults, <String>['Nested Result']);
expect(find.text('HomeBody'), findsNothing);
expect(find.text('Suggestions'), findsOneWidget);
expect(find.text('Nested Suggestions'), findsNothing);
await tester.tap(find.byTooltip('Back'));
await tester.pumpAndSettle();
expect(find.text('HomeBody'), findsOneWidget);
expect(find.text('Suggestions'), findsNothing);
expect(find.text('Nested Suggestions'), findsNothing);
expect(selectedResults, <String>['Result']);
});
testWidgets('Closing search with nested search shown goes back to underlying route', (WidgetTester tester) async {
_TestSearchDelegate delegate;
final List<String> nestedSearchResults = <String>[];
final _TestSearchDelegate nestedSearchDelegate = new _TestSearchDelegate(
suggestions: 'Nested Suggestions',
result: 'Nested Result',
actions: <Widget>[
new Builder(
builder: (BuildContext context) {
return new IconButton(
tooltip: 'Close Search',
icon: const Icon(Icons.close),
onPressed: () async {
delegate.close(context, 'Result Foo');
},
);
},
)
],
);
final List<String> selectedResults = <String>[];
delegate = new _TestSearchDelegate(
actions: <Widget>[
new Builder(
builder: (BuildContext context) {
return new IconButton(
tooltip: 'Nested Search',
icon: const Icon(Icons.search),
onPressed: () async {
final String result = await showSearch(
context: context,
delegate: nestedSearchDelegate,
);
nestedSearchResults.add(result);
},
);
},
)
],
);
await tester.pumpWidget(new TestHomePage(
delegate: delegate,
results: selectedResults,
));
expect(find.text('HomeBody'), findsOneWidget);
await tester.tap(find.byTooltip('Search'));
await tester.pumpAndSettle();
expect(find.text('HomeBody'), findsNothing);
expect(find.text('Suggestions'), findsOneWidget);
expect(find.text('Nested Suggestions'), findsNothing);
await tester.tap(find.byTooltip('Nested Search'));
await tester.pumpAndSettle();
expect(find.text('HomeBody'), findsNothing);
expect(find.text('Suggestions'), findsNothing);
expect(find.text('Nested Suggestions'), findsOneWidget);
await tester.tap(find.byTooltip('Close Search'));
await tester.pumpAndSettle();
expect(find.text('HomeBody'), findsOneWidget);
expect(find.text('Suggestions'), findsNothing);
expect(find.text('Nested Suggestions'), findsNothing);
expect(nestedSearchResults, <String>[null]);
expect(selectedResults, <String>['Result Foo']);
});
}
class TestHomePage extends StatelessWidget {
const TestHomePage({
this.results,
this.delegate,
this.passInInitialQuery = false,
this.initialQuery,
});
final List<String> results;
final SearchDelegate<String> delegate;
final bool passInInitialQuery;
final String initialQuery;
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: Builder(builder: (BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: const Text('HomeTitle'),
actions: <Widget>[
new IconButton(
tooltip: 'Search',
icon: const Icon(Icons.search),
onPressed: () async {
String selectedResult;
if (passInInitialQuery) {
selectedResult = await showSearch<String>(
context: context,
delegate: delegate,
query: initialQuery,
);
} else {
selectedResult = await showSearch<String>(
context: context,
delegate: delegate,
);
}
results?.add(selectedResult);
},
),
],
),
body: const Text('HomeBody'),
);
}),
);
}
}
class _TestSearchDelegate extends SearchDelegate<String> {
_TestSearchDelegate({
this.suggestions = 'Suggestions',
this.result = 'Result',
this.actions = const <Widget>[],
});
final String suggestions;
final String result;
final List<Widget> actions;
@override
Widget buildLeading(BuildContext context) {
return new IconButton(
tooltip: 'Back',
icon: const Icon(Icons.arrow_back),
onPressed: () {
close(context, result);
},
);
}
final List<String> querysForSuggestions = <String>[];
final List<String> querysForResults = <String>[];
@override
Widget buildSuggestions(BuildContext context) {
querysForSuggestions.add(query);
return new MaterialButton(
onPressed: () {
showResults(context);
},
child: new Text(suggestions),
);
}
@override
Widget buildResults(BuildContext context) {
querysForResults.add(query);
return const Text('Results');
}
@override
List<Widget> buildActions(BuildContext context) {
return actions;
}
}