dismissable_test.dart 11.2 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
import 'package:test/test.dart';

10
const double itemExtent = 100.0;
11
Axis scrollDirection = Axis.vertical;
12
DismissDirection dismissDirection = DismissDirection.horizontal;
13
DismissDirection reportedDismissDirection;
14
List<int> dismissedItems = <int>[];
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 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(
    padding: const EdgeDims.all(10.0),
43
    child: new ScrollableList(
44
      scrollDirection: scrollDirection,
45 46 47 48
      itemExtent: itemExtent,
      children: <int>[0, 1, 2, 3, 4].where(
        (int i) => !dismissedItems.contains(i)
      ).map(buildDismissableItem)
49 50 51 52
    )
  );
}

Hixie's avatar
Hixie committed
53 54
void dismissElement(WidgetTester tester, Element itemElement, { DismissDirection gestureDirection }) {
  assert(itemElement != null);
55 56 57 58 59 60 61
  assert(gestureDirection != DismissDirection.horizontal);
  assert(gestureDirection != DismissDirection.vertical);

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

Adam Barth's avatar
Adam Barth committed
87 88 89
  TestGesture gesture = tester.startGesture(downLocation, pointer: 5);
  gesture.moveTo(upLocation);
  gesture.up();
Hixie's avatar
Hixie committed
90 91 92 93 94 95 96 97 98 99
}

void dismissItem(WidgetTester tester, int item, { DismissDirection gestureDirection }) {
  assert(gestureDirection != DismissDirection.horizontal);
  assert(gestureDirection != DismissDirection.vertical);

  Element itemElement = tester.findText(item.toString());
  expect(itemElement, isNotNull);

  dismissElement(tester, itemElement, gestureDirection: gestureDirection);
100

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

Hixie's avatar
Hixie committed
108 109 110 111 112
class Test1215DismissableComponent extends StatelessComponent {
  Test1215DismissableComponent(this.text);
  final String text;
  Widget build(BuildContext context) {
    return new Dismissable(
113
      key: new ObjectKey(text),
Hixie's avatar
Hixie committed
114 115 116 117 118 119 120 121
      child: new AspectRatio(
        aspectRatio: 1.0,
        child: new Text(this.text)
      )
    );
  }
}

122
void main() {
123
  test('Horizontal drag triggers dismiss scrollDirection=vertical', () {
124
    testWidgets((WidgetTester tester) {
125
      scrollDirection = Axis.vertical;
126
      dismissDirection = DismissDirection.horizontal;
127
      dismissedItems = <int>[];
128 129 130 131 132 133 134

      tester.pumpWidget(widgetBuilder());
      expect(dismissedItems, isEmpty);

      dismissItem(tester, 0, gestureDirection: DismissDirection.right);
      expect(tester.findText('0'), isNull);
      expect(dismissedItems, equals([0]));
135
      expect(reportedDismissDirection, DismissDirection.right);
136 137 138 139

      dismissItem(tester, 1, gestureDirection: DismissDirection.left);
      expect(tester.findText('1'), isNull);
      expect(dismissedItems, equals([0, 1]));
140
      expect(reportedDismissDirection, DismissDirection.left);
141
    });
142 143 144
  });

  test('Vertical drag triggers dismiss scrollDirection=horizontal', () {
145
    testWidgets((WidgetTester tester) {
146
      scrollDirection = Axis.horizontal;
147
      dismissDirection = DismissDirection.vertical;
148
      dismissedItems = <int>[];
149 150 151 152 153 154 155

      tester.pumpWidget(widgetBuilder());
      expect(dismissedItems, isEmpty);

      dismissItem(tester, 0, gestureDirection: DismissDirection.up);
      expect(tester.findText('0'), isNull);
      expect(dismissedItems, equals([0]));
156
      expect(reportedDismissDirection, DismissDirection.up);
157 158 159 160

      dismissItem(tester, 1, gestureDirection: DismissDirection.down);
      expect(tester.findText('1'), isNull);
      expect(dismissedItems, equals([0, 1]));
161
      expect(reportedDismissDirection, DismissDirection.down);
162
    });
163 164 165
  });

  test('drag-left with DismissDirection.left triggers dismiss', () {
166
    testWidgets((WidgetTester tester) {
167
      scrollDirection = Axis.vertical;
168
      dismissDirection = DismissDirection.left;
169
      dismissedItems = <int>[];
170 171 172 173 174 175 176

      tester.pumpWidget(widgetBuilder());
      expect(dismissedItems, isEmpty);

      dismissItem(tester, 0, gestureDirection: DismissDirection.right);
      expect(tester.findText('0'), isNotNull);
      expect(dismissedItems, isEmpty);
177
      dismissItem(tester, 1, gestureDirection: DismissDirection.right);
178 179 180 181

      dismissItem(tester, 0, gestureDirection: DismissDirection.left);
      expect(tester.findText('0'), isNull);
      expect(dismissedItems, equals([0]));
182
      dismissItem(tester, 1, gestureDirection: DismissDirection.left);
183
    });
184 185 186
  });

  test('drag-right with DismissDirection.right triggers dismiss', () {
187
    testWidgets((WidgetTester tester) {
188
      scrollDirection = Axis.vertical;
189
      dismissDirection = DismissDirection.right;
190
      dismissedItems = <int>[];
191 192 193 194 195 196 197 198 199 200 201 202

      tester.pumpWidget(widgetBuilder());
      expect(dismissedItems, isEmpty);

      dismissItem(tester, 0, gestureDirection: DismissDirection.left);
      expect(tester.findText('0'), isNotNull);
      expect(dismissedItems, isEmpty);

      dismissItem(tester, 0, gestureDirection: DismissDirection.right);
      expect(tester.findText('0'), isNull);
      expect(dismissedItems, equals([0]));
    });
203 204 205
  });

  test('drag-up with DismissDirection.up triggers dismiss', () {
206
    testWidgets((WidgetTester tester) {
207
      scrollDirection = Axis.horizontal;
208
      dismissDirection = DismissDirection.up;
209
      dismissedItems = <int>[];
210 211 212 213 214 215 216 217 218 219 220 221

      tester.pumpWidget(widgetBuilder());
      expect(dismissedItems, isEmpty);

      dismissItem(tester, 0, gestureDirection: DismissDirection.down);
      expect(tester.findText('0'), isNotNull);
      expect(dismissedItems, isEmpty);

      dismissItem(tester, 0, gestureDirection: DismissDirection.up);
      expect(tester.findText('0'), isNull);
      expect(dismissedItems, equals([0]));
    });
222 223 224
  });

  test('drag-down with DismissDirection.down triggers dismiss', () {
225
    testWidgets((WidgetTester tester) {
226
      scrollDirection = Axis.horizontal;
227
      dismissDirection = DismissDirection.down;
228
      dismissedItems = <int>[];
229 230 231 232 233 234 235 236 237 238 239 240

      tester.pumpWidget(widgetBuilder());
      expect(dismissedItems, isEmpty);

      dismissItem(tester, 0, gestureDirection: DismissDirection.up);
      expect(tester.findText('0'), isNotNull);
      expect(dismissedItems, isEmpty);

      dismissItem(tester, 0, gestureDirection: DismissDirection.down);
      expect(tester.findText('0'), isNull);
      expect(dismissedItems, equals([0]));
    });
241
  });
242

243 244 245 246 247 248
  // 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...
249
  test('Verify that drag-move events do not assert', () {
250
    testWidgets((WidgetTester tester) {
251
      scrollDirection = Axis.horizontal;
252
      dismissDirection = DismissDirection.down;
253
      dismissedItems = <int>[];
254 255 256 257 258 259

      tester.pumpWidget(widgetBuilder());
      Element itemElement = tester.findText('0');

      Point location = tester.getTopLeft(itemElement);
      Offset offset = new Offset(0.0, 5.0);
Adam Barth's avatar
Adam Barth committed
260 261
      TestGesture gesture = tester.startGesture(location, pointer: 5);
      gesture.moveBy(offset);
262
      tester.pumpWidget(widgetBuilder());
Adam Barth's avatar
Adam Barth committed
263
      gesture.moveBy(offset);
264
      tester.pumpWidget(widgetBuilder());
Adam Barth's avatar
Adam Barth committed
265
      gesture.moveBy(offset);
266
      tester.pumpWidget(widgetBuilder());
Adam Barth's avatar
Adam Barth committed
267
      gesture.moveBy(offset);
268
      tester.pumpWidget(widgetBuilder());
Adam Barth's avatar
Adam Barth committed
269
      gesture.up();
270
    });
271
  });
Hixie's avatar
Hixie committed
272

273 274 275 276 277 278
  // This one is for a case where dssmissing a component above a previously
  // dismissed component threw an exception, which was documented at the
  // 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.
Hixie's avatar
Hixie committed
279 280 281 282 283 284
  test('dismissing bottom then top (smoketest)', () {
    testWidgets((WidgetTester tester) {
      tester.pumpWidget(new Center(
        child: new Container(
          width: 100.0,
          height: 1000.0,
285 286 287 288 289 290
          child: new Column(
            children: <Widget>[
              new Test1215DismissableComponent('1'),
              new Test1215DismissableComponent('2')
            ]
          )
Hixie's avatar
Hixie committed
291 292 293 294 295
        )
      ));
      expect(tester.findText('1'), isNotNull);
      expect(tester.findText('2'), isNotNull);
      dismissElement(tester, tester.findText('2'), gestureDirection: DismissDirection.right);
296 297
      tester.pump(); // start the slide away
      tester.pump(new Duration(seconds: 1)); // finish the slide away
Hixie's avatar
Hixie committed
298 299 300
      expect(tester.findText('1'), isNotNull);
      expect(tester.findText('2'), isNull);
      dismissElement(tester, tester.findText('1'), gestureDirection: DismissDirection.right);
301 302
      tester.pump(); // start the slide away
      tester.pump(new Duration(seconds: 1)); // finish the slide away (at which point the child is no longer included in the tree)
Hixie's avatar
Hixie committed
303 304 305 306
      expect(tester.findText('1'), isNull);
      expect(tester.findText('2'), isNull);
    });
  });
307
}