dismissable_test.dart 11.7 KB
Newer Older
Hixie's avatar
Hixie committed
1 2 3 4
// Copyright 2015 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.

Adam Barth's avatar
Adam Barth committed
5
import 'package:flutter_test/flutter_test.dart';
6 7
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
8

9
const double itemExtent = 100.0;
10
Axis scrollDirection = Axis.vertical;
11
DismissDirection dismissDirection = DismissDirection.horizontal;
12
DismissDirection reportedDismissDirection;
13
List<int> dismissedItems = <int>[];
14
Widget background;
15

16
void handleOnResize(int item) {
17 18 19
  expect(dismissedItems.contains(item), isFalse);
}

20 21
void handleOnDismissed(DismissDirection direction, int item) {
  reportedDismissDirection = direction;
22 23 24 25
  expect(dismissedItems.contains(item), isFalse);
  dismissedItems.add(item);
}

26
Widget buildDismissableItem(int item) {
27 28 29
  return new Dismissable(
    key: new ValueKey<int>(item),
    direction: dismissDirection,
30
    onDismissed: (DismissDirection direction) { handleOnDismissed(direction, item); },
31
    onResize: () { handleOnResize(item); },
32
    background: background,
33 34 35 36 37 38 39 40 41 42
    child: new Container(
      width: itemExtent,
      height: itemExtent,
      child: new Text(item.toString())
    )
  );
}

Widget widgetBuilder() {
  return new Container(
43
    padding: const EdgeInsets.all(10.0),
44
    child: new ScrollableList(
45
      scrollDirection: scrollDirection,
46 47 48 49
      itemExtent: itemExtent,
      children: <int>[0, 1, 2, 3, 4].where(
        (int i) => !dismissedItems.contains(i)
      ).map(buildDismissableItem)
50 51 52 53
    )
  );
}

54
Future<Null> dismissElement(WidgetTester tester, Finder finder, { DismissDirection gestureDirection }) async {
55
  assert(tester.any(finder));
56 57 58 59 60 61
  assert(gestureDirection != DismissDirection.horizontal);
  assert(gestureDirection != DismissDirection.vertical);

  Point downLocation;
  Point upLocation;
  switch(gestureDirection) {
62
    case DismissDirection.endToStart:
63 64
      // getTopRight() returns a point that's just beyond itemWidget's right
      // edge and outside the Dismissable event listener's bounds.
65 66
      downLocation = tester.getTopRight(finder) + const Offset(-0.1, 0.0);
      upLocation = tester.getTopLeft(finder);
67
      break;
68
    case DismissDirection.startToEnd:
69
      // we do the same thing here to keep the test symmetric
70 71
      downLocation = tester.getTopLeft(finder) + const Offset(0.1, 0.0);
      upLocation = tester.getTopRight(finder);
72 73
      break;
    case DismissDirection.up:
74 75
      // getBottomLeft() returns a point that's just below itemWidget's bottom
      // edge and outside the Dismissable event listener's bounds.
76 77
      downLocation = tester.getBottomLeft(finder) + const Offset(0.0, -0.1);
      upLocation = tester.getTopLeft(finder);
78 79
      break;
    case DismissDirection.down:
80
      // again with doing the same here for symmetry
81 82
      downLocation = tester.getTopLeft(finder) + const Offset(0.1, 0.0);
      upLocation = tester.getBottomLeft(finder);
83
      break;
84 85
    default:
      fail("unsupported gestureDirection");
86 87
  }

88 89 90
  TestGesture gesture = await tester.startGesture(downLocation, pointer: 5);
  await gesture.moveTo(upLocation);
  await gesture.up();
Hixie's avatar
Hixie committed
91 92
}

93
Future<Null> dismissItem(WidgetTester tester, int item, { DismissDirection gestureDirection }) async {
Hixie's avatar
Hixie committed
94 95 96
  assert(gestureDirection != DismissDirection.horizontal);
  assert(gestureDirection != DismissDirection.vertical);

97
  Finder itemFinder = find.text(item.toString());
98
  expect(itemFinder, findsOneWidget);
Hixie's avatar
Hixie committed
99

100
  await dismissElement(tester, itemFinder, gestureDirection: gestureDirection);
101

102 103 104 105 106
  await tester.pumpWidget(widgetBuilder()); // start the slide
  await tester.pumpWidget(widgetBuilder(), const Duration(seconds: 1)); // finish the slide and start shrinking...
  await tester.pumpWidget(widgetBuilder()); // first frame of shrinking animation
  await tester.pumpWidget(widgetBuilder(), const Duration(seconds: 1)); // finish the shrinking and call the callback...
  await tester.pumpWidget(widgetBuilder()); // rebuild after the callback removes the entry
107 108
}

109 110
class Test1215DismissableWidget extends StatelessWidget {
  Test1215DismissableWidget(this.text);
111

Hixie's avatar
Hixie committed
112
  final String text;
113 114

  @override
Hixie's avatar
Hixie committed
115 116
  Widget build(BuildContext context) {
    return new Dismissable(
117
      key: new ObjectKey(text),
Hixie's avatar
Hixie committed
118 119 120 121 122 123 124 125
      child: new AspectRatio(
        aspectRatio: 1.0,
        child: new Text(this.text)
      )
    );
  }
}

126
void main() {
127 128 129 130 131
  setUp(() {
    dismissedItems = <int>[];
    background = null;
  });

132
  testWidgets('Horizontal drag triggers dismiss scrollDirection=vertical', (WidgetTester tester) async {
133 134
    scrollDirection = Axis.vertical;
    dismissDirection = DismissDirection.horizontal;
135

136
    await tester.pumpWidget(widgetBuilder());
137
    expect(dismissedItems, isEmpty);
138

139
    await dismissItem(tester, 0, gestureDirection: DismissDirection.startToEnd);
140
    expect(find.text('0'), findsNothing);
141
    expect(dismissedItems, equals(<int>[0]));
142
    expect(reportedDismissDirection, DismissDirection.startToEnd);
143

144
    await dismissItem(tester, 1, gestureDirection: DismissDirection.endToStart);
145
    expect(find.text('1'), findsNothing);
146
    expect(dismissedItems, equals(<int>[0, 1]));
147
    expect(reportedDismissDirection, DismissDirection.endToStart);
148 149
  });

150
  testWidgets('Vertical drag triggers dismiss scrollDirection=horizontal', (WidgetTester tester) async {
151 152
    scrollDirection = Axis.horizontal;
    dismissDirection = DismissDirection.vertical;
153

154
    await tester.pumpWidget(widgetBuilder());
155
    expect(dismissedItems, isEmpty);
156

157
    await dismissItem(tester, 0, gestureDirection: DismissDirection.up);
158
    expect(find.text('0'), findsNothing);
159
    expect(dismissedItems, equals(<int>[0]));
160
    expect(reportedDismissDirection, DismissDirection.up);
161

162
    await dismissItem(tester, 1, gestureDirection: DismissDirection.down);
163
    expect(find.text('1'), findsNothing);
164
    expect(dismissedItems, equals(<int>[0, 1]));
165
    expect(reportedDismissDirection, DismissDirection.down);
166 167
  });

168
  testWidgets('drag-left with DismissDirection.left triggers dismiss', (WidgetTester tester) async {
169 170
    scrollDirection = Axis.vertical;
    dismissDirection = DismissDirection.endToStart;
171

172
    await tester.pumpWidget(widgetBuilder());
173
    expect(dismissedItems, isEmpty);
174

175
    await dismissItem(tester, 0, gestureDirection: DismissDirection.startToEnd);
176 177
    expect(find.text('0'), findsOneWidget);
    expect(dismissedItems, isEmpty);
178
    await dismissItem(tester, 1, gestureDirection: DismissDirection.startToEnd);
179

180
    await dismissItem(tester, 0, gestureDirection: DismissDirection.endToStart);
181
    expect(find.text('0'), findsNothing);
182
    expect(dismissedItems, equals(<int>[0]));
183
    await dismissItem(tester, 1, gestureDirection: DismissDirection.endToStart);
184 185
  });

186
  testWidgets('drag-right with DismissDirection.right triggers dismiss', (WidgetTester tester) async {
187 188
    scrollDirection = Axis.vertical;
    dismissDirection = DismissDirection.startToEnd;
189

190
    await tester.pumpWidget(widgetBuilder());
191
    expect(dismissedItems, isEmpty);
192

193
    await dismissItem(tester, 0, gestureDirection: DismissDirection.endToStart);
194 195
    expect(find.text('0'), findsOneWidget);
    expect(dismissedItems, isEmpty);
196

197
    await dismissItem(tester, 0, gestureDirection: DismissDirection.startToEnd);
198
    expect(find.text('0'), findsNothing);
199
    expect(dismissedItems, equals(<int>[0]));
200 201
  });

202
  testWidgets('drag-up with DismissDirection.up triggers dismiss', (WidgetTester tester) async {
203 204
    scrollDirection = Axis.horizontal;
    dismissDirection = DismissDirection.up;
205

206
    await tester.pumpWidget(widgetBuilder());
207
    expect(dismissedItems, isEmpty);
208

209
    await dismissItem(tester, 0, gestureDirection: DismissDirection.down);
210 211
    expect(find.text('0'), findsOneWidget);
    expect(dismissedItems, isEmpty);
212

213
    await dismissItem(tester, 0, gestureDirection: DismissDirection.up);
214
    expect(find.text('0'), findsNothing);
215
    expect(dismissedItems, equals(<int>[0]));
216 217
  });

218
  testWidgets('drag-down with DismissDirection.down triggers dismiss', (WidgetTester tester) async {
219 220
    scrollDirection = Axis.horizontal;
    dismissDirection = DismissDirection.down;
221

222
    await tester.pumpWidget(widgetBuilder());
223
    expect(dismissedItems, isEmpty);
224

225
    await dismissItem(tester, 0, gestureDirection: DismissDirection.up);
226 227
    expect(find.text('0'), findsOneWidget);
    expect(dismissedItems, isEmpty);
228

229
    await dismissItem(tester, 0, gestureDirection: DismissDirection.down);
230
    expect(find.text('0'), findsNothing);
231
    expect(dismissedItems, equals(<int>[0]));
232
  });
233

234 235 236 237 238 239
  // This is a regression test for an fn2 bug where dragging a card caused an
  // assert "'!_disqualifiedFromEverAppearingAgain' is not true". The old URL
  // was https://github.com/domokit/sky_engine/issues/1068 but that issue is 404
  // now since we migrated to the new repo. The bug was fixed by
  // https://github.com/flutter/engine/pull/1134 at the time, and later made
  // irrelevant by fn3, but just in case...
240
  testWidgets('Verify that drag-move events do not assert', (WidgetTester tester) async {
241 242 243
    scrollDirection = Axis.horizontal;
    dismissDirection = DismissDirection.down;

244
    await tester.pumpWidget(widgetBuilder());
245 246
    Point location = tester.getTopLeft(find.text('0'));
    Offset offset = new Offset(0.0, 5.0);
247 248 249 250 251 252 253 254 255 256
    TestGesture gesture = await tester.startGesture(location, pointer: 5);
    await gesture.moveBy(offset);
    await tester.pumpWidget(widgetBuilder());
    await gesture.moveBy(offset);
    await tester.pumpWidget(widgetBuilder());
    await gesture.moveBy(offset);
    await tester.pumpWidget(widgetBuilder());
    await gesture.moveBy(offset);
    await tester.pumpWidget(widgetBuilder());
    await gesture.up();
257
  });
Hixie's avatar
Hixie committed
258

259 260
  // This one is for a case where dssmissing a widget above a previously
  // dismissed widget threw an exception, which was documented at the
261 262 263 264
  // now-obsolete URL https://github.com/flutter/engine/issues/1215 (the URL
  // died in the migration to the new repo). Don't copy this test; it doesn't
  // actually remove the dismissed widget, which is a violation of the
  // Dismissable contract. This is not an example of good practice.
265 266
  testWidgets('dismissing bottom then top (smoketest)', (WidgetTester tester) async {
    await tester.pumpWidget(new Center(
267 268 269 270 271 272 273 274
      child: new Container(
        width: 100.0,
        height: 1000.0,
        child: new Column(
          children: <Widget>[
            new Test1215DismissableWidget('1'),
            new Test1215DismissableWidget('2')
          ]
Hixie's avatar
Hixie committed
275
        )
276 277 278 279
      )
    ));
    expect(find.text('1'), findsOneWidget);
    expect(find.text('2'), findsOneWidget);
280 281 282
    await dismissElement(tester, find.text('2'), gestureDirection: DismissDirection.startToEnd);
    await tester.pump(); // start the slide away
    await tester.pump(new Duration(seconds: 1)); // finish the slide away
283 284
    expect(find.text('1'), findsOneWidget);
    expect(find.text('2'), findsNothing);
285 286 287
    await dismissElement(tester, find.text('1'), gestureDirection: DismissDirection.startToEnd);
    await tester.pump(); // start the slide away
    await tester.pump(new Duration(seconds: 1)); // finish the slide away (at which point the child is no longer included in the tree)
288 289
    expect(find.text('1'), findsNothing);
    expect(find.text('2'), findsNothing);
Hixie's avatar
Hixie committed
290
  });
291

292
  testWidgets('Dismissable starts from the full size when collapsing', (WidgetTester tester) async {
293 294 295
    scrollDirection = Axis.vertical;
    dismissDirection = DismissDirection.horizontal;
    background = new Text('background');
296

297
    await tester.pumpWidget(widgetBuilder());
298
    expect(dismissedItems, isEmpty);
299

300 301
    Finder itemFinder = find.text('0');
    expect(itemFinder, findsOneWidget);
302 303
    await dismissElement(tester, itemFinder, gestureDirection: DismissDirection.startToEnd);
    await tester.pump();
304

305 306 307
    expect(find.text('background'), findsNWidgets(5));
    RenderBox backgroundBox = tester.firstRenderObject(find.text('background'));
    expect(backgroundBox.size.height, equals(100.0));
308
  });
309
}