stepper_test.dart 18.6 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
// @dart = 2.8

7
import 'package:flutter/foundation.dart';
8 9 10 11 12 13 14 15
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('Stepper tap callback test', (WidgetTester tester) async {
    int index = 0;

    await tester.pumpWidget(
16 17 18
      MaterialApp(
        home: Material(
          child: Stepper(
19 20 21
            onStepTapped: (int i) {
              index = i;
            },
22
            steps: const <Step>[
23 24 25
              Step(
                title: Text('Step 1'),
                content: SizedBox(
26
                  width: 100.0,
27 28
                  height: 100.0,
                ),
29
              ),
30 31 32
              Step(
                title: Text('Step 2'),
                content: SizedBox(
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
                  width: 100.0,
                  height: 100.0,
                ),
              ),
            ],
          ),
        ),
      ),
    );
    await tester.tap(find.text('Step 2'));
    expect(index, 1);
  });

  testWidgets('Stepper expansion test', (WidgetTester tester) async {
    await tester.pumpWidget(
48 49 50 51
      MaterialApp(
        home: Center(
          child: Material(
            child: Stepper(
52
              steps: const <Step>[
53 54 55
                Step(
                  title: Text('Step 1'),
                  content: SizedBox(
56 57 58 59
                    width: 100.0,
                    height: 100.0,
                  ),
                ),
60 61 62
                Step(
                  title: Text('Step 2'),
                  content: SizedBox(
63 64 65 66 67 68 69 70 71
                    width: 200.0,
                    height: 200.0,
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
72 73 74 75 76 77
    );

    RenderBox box = tester.renderObject(find.byType(Stepper));
    expect(box.size.height, 332.0);

    await tester.pumpWidget(
78 79 80 81
      MaterialApp(
        home: Center(
          child: Material(
            child: Stepper(
82
              currentStep: 1,
83
              steps: const <Step>[
84 85 86
                Step(
                  title: Text('Step 1'),
                  content: SizedBox(
87 88 89 90
                    width: 100.0,
                    height: 100.0,
                  ),
                ),
91 92 93
                Step(
                  title: Text('Step 2'),
                  content: SizedBox(
94 95 96 97 98 99 100 101 102
                    width: 200.0,
                    height: 200.0,
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
103 104 105 106 107 108 109 110 111 112 113 114
    );

    await tester.pump(const Duration(milliseconds: 100));
    box = tester.renderObject(find.byType(Stepper));
    expect(box.size.height, greaterThan(332.0));
    await tester.pump(const Duration(milliseconds: 100));
    box = tester.renderObject(find.byType(Stepper));
    expect(box.size.height, 432.0);
  });

  testWidgets('Stepper horizontal size test', (WidgetTester tester) async {
    await tester.pumpWidget(
115 116 117 118
      MaterialApp(
        home: Center(
          child: Material(
            child: Stepper(
119
              type: StepperType.horizontal,
120
              steps: const <Step>[
121 122 123
                Step(
                  title: Text('Step 1'),
                  content: SizedBox(
124 125 126 127 128 129 130 131 132
                    width: 100.0,
                    height: 100.0,
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
133 134
    );

135
    final RenderBox box = tester.renderObject(find.byType(Stepper));
136 137 138 139 140
    expect(box.size.height, 600.0);
  });

  testWidgets('Stepper visibility test', (WidgetTester tester) async {
    await tester.pumpWidget(
141 142 143
      MaterialApp(
        home: Material(
          child: Stepper(
144
            type: StepperType.horizontal,
145
            steps: const <Step>[
146 147 148
              Step(
                title: Text('Step 1'),
                content: Text('A'),
149
              ),
150 151 152
              Step(
                title: Text('Step 2'),
                content: Text('B'),
153 154 155 156 157
              ),
            ],
          ),
        ),
      ),
158 159 160 161 162 163
    );

    expect(find.text('A'), findsOneWidget);
    expect(find.text('B'), findsNothing);

    await tester.pumpWidget(
164 165 166
      MaterialApp(
        home: Material(
          child: Stepper(
167 168
            currentStep: 1,
            type: StepperType.horizontal,
169
            steps: const <Step>[
170 171 172
              Step(
                title: Text('Step 1'),
                content: Text('A'),
173
              ),
174 175 176
              Step(
                title: Text('Step 2'),
                content: Text('B'),
177 178 179 180 181
              ),
            ],
          ),
        ),
      ),
182 183 184 185 186 187 188 189 190 191 192
    );

    expect(find.text('A'), findsNothing);
    expect(find.text('B'), findsOneWidget);
  });

  testWidgets('Stepper button test', (WidgetTester tester) async {
    bool continuePressed = false;
    bool cancelPressed = false;

    await tester.pumpWidget(
193 194 195
      MaterialApp(
        home: Material(
          child: Stepper(
196 197 198 199 200 201 202
            type: StepperType.horizontal,
            onStepContinue: () {
              continuePressed = true;
            },
            onStepCancel: () {
              cancelPressed = true;
            },
203
            steps: const <Step>[
204 205 206
              Step(
                title: Text('Step 1'),
                content: SizedBox(
207 208 209 210
                  width: 100.0,
                  height: 100.0,
                ),
              ),
211 212 213
              Step(
                title: Text('Step 2'),
                content: SizedBox(
214 215 216 217 218 219 220 221
                  width: 200.0,
                  height: 200.0,
                ),
              ),
            ],
          ),
        ),
      ),
222 223 224 225 226 227 228 229 230 231 232 233 234
    );

    await tester.tap(find.text('CONTINUE'));
    await tester.tap(find.text('CANCEL'));

    expect(continuePressed, isTrue);
    expect(cancelPressed, isTrue);
  });

  testWidgets('Stepper disabled step test', (WidgetTester tester) async {
    int index = 0;

    await tester.pumpWidget(
235 236 237
      MaterialApp(
        home: Material(
          child: Stepper(
238 239 240
            onStepTapped: (int i) {
              index = i;
            },
241
            steps: const <Step>[
242 243 244
              Step(
                title: Text('Step 1'),
                content: SizedBox(
245 246 247 248
                  width: 100.0,
                  height: 100.0,
                ),
              ),
249 250
              Step(
                title: Text('Step 2'),
251
                state: StepState.disabled,
252
                content: SizedBox(
253 254 255 256 257 258 259 260
                  width: 100.0,
                  height: 100.0,
                ),
              ),
            ],
          ),
        ),
      ),
261 262 263 264 265 266 267 268
    );

    await tester.tap(find.text('Step 2'));
    expect(index, 0);
  });

  testWidgets('Stepper scroll test', (WidgetTester tester) async {
    await tester.pumpWidget(
269 270 271
      MaterialApp(
        home: Material(
          child: Stepper(
272
            steps: const <Step>[
273 274 275
              Step(
                title: Text('Step 1'),
                content: SizedBox(
276 277 278 279
                  width: 100.0,
                  height: 300.0,
                ),
              ),
280 281 282
              Step(
                title: Text('Step 2'),
                content: SizedBox(
283 284 285 286
                  width: 100.0,
                  height: 300.0,
                ),
              ),
287 288 289
              Step(
                title: Text('Step 3'),
                content: SizedBox(
290 291 292 293 294 295 296 297
                  width: 100.0,
                  height: 100.0,
                ),
              ),
            ],
          ),
        ),
      ),
298 299
    );

300
    final ScrollableState scrollableState = tester.firstState(find.byType(Scrollable));
301
    expect(scrollableState.position.pixels, 0.0);
302 303 304

    await tester.tap(find.text('Step 3'));
    await tester.pumpWidget(
305 306 307
      MaterialApp(
        home: Material(
          child: Stepper(
308
            currentStep: 2,
309
            steps: const <Step>[
310 311 312
              Step(
                title: Text('Step 1'),
                content: SizedBox(
313
                  width: 100.0,
314 315
                  height: 300.0,
                ),
316
              ),
317 318 319
              Step(
                title: Text('Step 2'),
                content: SizedBox(
320
                  width: 100.0,
321 322 323
                  height: 300.0,
                ),
              ),
324 325 326
              Step(
                title: Text('Step 3'),
                content: SizedBox(
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
                  width: 100.0,
                  height: 100.0,
                ),
              ),
            ],
          ),
        ),
      ),
    );

    await tester.pump(const Duration(milliseconds: 100));
    expect(scrollableState.position.pixels, greaterThan(0.0));
  });

  testWidgets('Stepper index test', (WidgetTester tester) async {
    await tester.pumpWidget(
343 344 345 346
      MaterialApp(
        home: Center(
          child: Material(
            child: Stepper(
347
              steps: const <Step>[
348 349
                Step(
                  title: Text('A'),
350
                  state: StepState.complete,
351
                  content: SizedBox(
352 353 354 355
                    width: 100.0,
                    height: 100.0,
                  ),
                ),
356 357 358
                Step(
                  title: Text('B'),
                  content: SizedBox(
359 360 361 362 363 364 365 366 367
                    width: 100.0,
                    height: 100.0,
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
368 369 370 371 372 373
    );

    expect(find.text('1'), findsNothing);
    expect(find.text('2'), findsOneWidget);
  });

374 375 376 377 378 379 380 381 382 383 384 385
  testWidgets('Stepper custom controls test', (WidgetTester tester) async {
    bool continuePressed = false;
    void setContinue() {
      continuePressed = true;
    }

    bool canceledPressed = false;
    void setCanceled() {
      canceledPressed = true;
    }

    final ControlsWidgetBuilder builder =
386
      (BuildContext context, { VoidCallback onStepContinue, VoidCallback onStepCancel }) {
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
        return Container(
          margin: const EdgeInsets.only(top: 16.0),
          child: ConstrainedBox(
            constraints: const BoxConstraints.tightFor(height: 48.0),
            child: Row(
              children: <Widget>[
                FlatButton(
                  onPressed: onStepContinue,
                  color: Colors.blue,
                  textColor: Colors.white,
                  textTheme: ButtonTextTheme.normal,
                  child: const Text('Let us continue!'),
                ),
                Container(
                  margin: const EdgeInsetsDirectional.only(start: 8.0),
                  child: FlatButton(
                    onPressed: onStepCancel,
                    textColor: Colors.red,
                    textTheme: ButtonTextTheme.normal,
                    child: const Text('Cancel This!'),
                  ),
                ),
              ],
            ),
          ),
        );
      };

    await tester.pumpWidget(
      MaterialApp(
        home: Center(
          child: Material(
            child: Stepper(
              controlsBuilder: builder,
              onStepCancel: setCanceled,
              onStepContinue: setContinue,
              steps: const <Step>[
                Step(
                  title: Text('A'),
                  state: StepState.complete,
                  content: SizedBox(
                    width: 100.0,
                    height: 100.0,
                  ),
                ),
                Step(
                  title: Text('B'),
                  content: SizedBox(
                    width: 100.0,
                    height: 100.0,
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );

    // 2 because stepper creates a set of controls for each step
    expect(find.text('Let us continue!'), findsNWidgets(2));
    expect(find.text('Cancel This!'), findsNWidgets(2));

    await tester.tap(find.text('Cancel This!').first);
    await tester.pumpAndSettle();
    await tester.tap(find.text('Let us continue!').first);
    await tester.pumpAndSettle();

    expect(canceledPressed, isTrue);
    expect(continuePressed, isTrue);
  });

459 460
  testWidgets('Stepper error test', (WidgetTester tester) async {
    await tester.pumpWidget(
461 462 463 464
      MaterialApp(
        home: Center(
          child: Material(
            child: Stepper(
465
              steps: const <Step>[
466 467
                Step(
                  title: Text('A'),
468
                  state: StepState.error,
469
                  content: SizedBox(
470 471 472 473 474 475 476 477 478
                    width: 100.0,
                    height: 100.0,
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
479 480 481 482
    );

    expect(find.text('!'), findsOneWidget);
  });
483

484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
  testWidgets('Nested stepper error test', (WidgetTester tester) async {
    FlutterErrorDetails errorDetails;
    final FlutterExceptionHandler oldHandler = FlutterError.onError;
    FlutterError.onError = (FlutterErrorDetails details) {
      errorDetails = details;
    };
    try {
      await tester.pumpWidget(
        MaterialApp(
          home: Material(
            child: Stepper(
              type: StepperType.horizontal,
              steps: <Step>[
                Step(
                  title: const Text('Step 2'),
                  content:  Stepper(
                    type: StepperType.vertical,
                    steps: const <Step>[
                      Step(
                        title: Text('Nested step 1'),
                        content: Text('A'),
                      ),
                      Step(
                        title: Text('Nested step 2'),
                        content: Text('A'),
                      ),
                    ],
511
                  ),
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
                ),
                const Step(
                  title: Text('Step 1'),
                  content: Text('A'),
                ),
              ],
            ),
          ),
        ),
      );
    } finally {
      FlutterError.onError = oldHandler;
    }

    expect(errorDetails, isNotNull);
    expect(errorDetails.stack, isNotNull);
    // Check the ErrorDetails without the stack trace
529 530
    final String fullErrorMessage = errorDetails.toString();
    final List<String> lines = fullErrorMessage.split('\n');
531 532
    // The lines in the middle of the error message contain the stack trace
    // which will change depending on where the test is run.
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
    final String errorMessage = lines.takeWhile(
      (String line) => line != '',
    ).join('\n');
    expect(errorMessage.length, lessThan(fullErrorMessage.length));
    expect(errorMessage, startsWith(
      '══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞════════════════════════\n'
      'The following assertion was thrown building Stepper('
    ));
    // The description string of the stepper looks slightly different depending
    // on the platform and is omitted here.
    expect(errorMessage, endsWith(
      '):\n'
      'Steppers must not be nested.\n'
      'The material specification advises that one should avoid\n'
      'embedding steppers within steppers.\n'
      'https://material.io/archive/guidelines/components/steppers.html#steppers-usage'
    ));
550 551
  });

552 553 554
  ///https://github.com/flutter/flutter/issues/16920
  testWidgets('Stepper icons size test', (WidgetTester tester) async {
    await tester.pumpWidget(
555
      MaterialApp(
556
        home: Material(
557
          child: Stepper(
558
            steps: const <Step>[
559 560
              Step(
                title: Text('A'),
561
                state: StepState.editing,
562
                content: SizedBox(width: 100.0, height: 100.0),
563
              ),
564 565
              Step(
                title: Text('B'),
566
                state: StepState.complete,
567
                content: SizedBox(width: 100.0, height: 100.0),
568 569 570 571 572 573 574 575 576 577 578 579 580
              ),
            ],
          ),
        ),
      ),
    );

    RenderBox renderObject = tester.renderObject(find.byIcon(Icons.edit));
    expect(renderObject.size, equals(const Size.square(18.0)));

    renderObject = tester.renderObject(find.byIcon(Icons.check));
    expect(renderObject.size, equals(const Size.square(18.0)));
  });
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613

  testWidgets('Stepper physics scroll error test', (WidgetTester tester) async {
    await tester.pumpWidget(
      MaterialApp(
        home: Material(
          child: ListView(
            children: <Widget>[
              Stepper(
                steps: const <Step>[
                  Step(title: Text('Step 1'), content: Text('Text 1')),
                  Step(title: Text('Step 2'), content: Text('Text 2')),
                  Step(title: Text('Step 3'), content: Text('Text 3')),
                  Step(title: Text('Step 4'), content: Text('Text 4')),
                  Step(title: Text('Step 5'), content: Text('Text 5')),
                  Step(title: Text('Step 6'), content: Text('Text 6')),
                  Step(title: Text('Step 7'), content: Text('Text 7')),
                  Step(title: Text('Step 8'), content: Text('Text 8')),
                  Step(title: Text('Step 9'), content: Text('Text 9')),
                  Step(title: Text('Step 10'), content: Text('Text 10')),
                ],
              ),
              const Text('Text After Stepper'),
            ],
          ),
        ),
      ),
    );

    await tester.fling(find.byType(Stepper), const Offset(0.0, -100.0), 1000.0);
    await tester.pumpAndSettle();

    expect(find.text('Text After Stepper'), findsNothing);
  });
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665

  testWidgets("Vertical Stepper can't be focused when disabled.", (WidgetTester tester) async {
    await tester.pumpWidget(
      MaterialApp(
        home: Material(
          child: Stepper(
            currentStep: 0,
            type: StepperType.vertical,
            steps: const <Step>[
              Step(
                title: Text('Step 0'),
                state: StepState.disabled,
                content: Text('Text 0'),
              ),
            ],
          ),
        ),
      ),
    );
    await tester.pump();

    final FocusNode disabledNode = Focus.of(tester.element(find.text('Step 0')), nullOk: true, scopeOk: true);
    disabledNode.requestFocus();
    await tester.pump();
    expect(disabledNode.hasPrimaryFocus, isFalse);
  });

  testWidgets("Horizontal Stepper can't be focused when disabled.", (WidgetTester tester) async {
    await tester.pumpWidget(
      MaterialApp(
        home: Material(
          child: Stepper(
            currentStep: 0,
            type: StepperType.horizontal,
            steps: const <Step>[
              Step(
                title: Text('Step 0'),
                state: StepState.disabled,
                content: Text('Text 0'),
              ),
            ],
          ),
        ),
      ),
    );
    await tester.pump();

    final FocusNode disabledNode = Focus.of(tester.element(find.text('Step 0')), nullOk: true, scopeOk: true);
    disabledNode.requestFocus();
    await tester.pump();
    expect(disabledNode.hasPrimaryFocus, isFalse);
  });
666
}