ink_sparkle_test.dart 5.25 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This file is run as part of a reduced test set in CI on Mac and Windows
// machines.
@Tags(<String>['reduced-test-set'])
8
library;
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

import 'package:flutter/material.dart';
import 'package:flutter/src/foundation/constants.dart';
import 'package:flutter_test/flutter_test.dart';

import '../rendering/mock_canvas.dart';

void main() {
  testWidgets('InkSparkle in a Button compiles and does not crash', (WidgetTester tester) async {
    await tester.pumpWidget(MaterialApp(
      home: Scaffold(
        body: Center(
          child: ElevatedButton(
            style: ElevatedButton.styleFrom(splashFactory: InkSparkle.splashFactory),
            child: const Text('Sparkle!'),
            onPressed: () { },
          ),
        ),
      ),
    ));
    final Finder buttonFinder = find.text('Sparkle!');
    await tester.tap(buttonFinder);
    await tester.pump();
    await tester.pumpAndSettle();
  },
34
    skip: kIsWeb, // [intended] shaders are not yet supported for web.
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
  );

  testWidgets('InkSparkle default splashFactory paints with drawRect when bounded', (WidgetTester tester) async {
    await tester.pumpWidget(MaterialApp(
      home: Scaffold(
        body: Center(
          child: InkWell(
            splashFactory: InkSparkle.splashFactory,
            child: const Text('Sparkle!'),
            onTap: () { },
          ),
        ),
      ),
    ));
    final Finder buttonFinder = find.text('Sparkle!');
    await tester.tap(buttonFinder);
    await tester.pump();
52
    await tester.pump(const Duration(milliseconds: 200));
53

54
    final MaterialInkController material = Material.of(tester.element(buttonFinder));
55
    expect(material, paintsExactlyCountTimes(#drawRect, 1));
56 57 58 59 60 61 62 63

    // ignore: avoid_dynamic_calls
    expect((material as dynamic).debugInkFeatures, hasLength(1));

    await tester.pumpAndSettle();
    // ink feature is disposed.
    // ignore: avoid_dynamic_calls
    expect((material as dynamic).debugInkFeatures, isEmpty);
64
  },
65
    skip: kIsWeb, // [intended] shaders are not yet supported for web.
66 67
  );

68
  testWidgets('InkSparkle default splashFactory paints with drawPaint when unbounded', (WidgetTester tester) async {
69 70 71 72 73 74 75 76 77 78 79 80 81 82
    await tester.pumpWidget(MaterialApp(
      home: Scaffold(
        body: Center(
          child: InkResponse(
            splashFactory: InkSparkle.splashFactory,
            child: const Text('Sparkle!'),
            onTap: () { },
          ),
        ),
      ),
    ));
    final Finder buttonFinder = find.text('Sparkle!');
    await tester.tap(buttonFinder);
    await tester.pump();
83
    await tester.pump(const Duration(milliseconds: 200));
84

85
    final MaterialInkController material = Material.of(tester.element(buttonFinder));
86 87
    expect(material, paintsExactlyCountTimes(#drawPaint, 1));
  },
88
    skip: kIsWeb, // [intended] shaders are not yet supported for web.
89 90 91 92 93 94 95 96 97
  );

  /////////////
  // Goldens //
  /////////////

  testWidgets('InkSparkle renders with sparkles when top left of button is tapped', (WidgetTester tester) async {
    await _runTest(tester, 'top_left', 0.2);
  },
98
    skip: kIsWeb, // [intended] shaders are not yet supported for web.
99 100 101 102 103
  );

  testWidgets('InkSparkle renders with sparkles when center of button is tapped', (WidgetTester tester) async {
    await _runTest(tester, 'center', 0.5);
  },
104
    skip: kIsWeb, // [intended] shaders are not yet supported for web.
105 106 107 108 109
  );

  testWidgets('InkSparkle renders with sparkles when bottom right of button is tapped', (WidgetTester tester) async {
    await _runTest(tester, 'bottom_right', 0.8);
  },
110
    skip: kIsWeb, // [intended] shaders are not yet supported for web.
111 112 113 114 115 116 117 118
  );
}

Future<void> _runTest(WidgetTester tester, String positionName, double distanceFromTopLeft) async {
  final Key repaintKey = UniqueKey();
  final Key buttonKey = UniqueKey();

  await tester.pumpWidget(MaterialApp(
119
    theme: ThemeData(useMaterial3: false),
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
    home: Scaffold(
      body: Center(
        child: RepaintBoundary(
          key: repaintKey,
          child: ElevatedButton(
            key: buttonKey,
            style: ElevatedButton.styleFrom(splashFactory: InkSparkle.constantTurbulenceSeedSplashFactory),
            child: const Text('Sparkle!'),
            onPressed: () { },
          ),
        ),
      ),
    ),
  ));

  final Finder buttonFinder = find.byKey(buttonKey);
  final Finder repaintFinder = find.byKey(repaintKey);
  final Offset topLeft = tester.getTopLeft(buttonFinder);
  final Offset bottomRight = tester.getBottomRight(buttonFinder);

140 141
  await _warmUpShader(tester, buttonFinder);

142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
  final Offset target = topLeft + (bottomRight - topLeft) * distanceFromTopLeft;
  await tester.tapAt(target);
  for (int i = 0; i <= 5; i++) {
    await tester.pump(const Duration(milliseconds: 50));
    await expectLater(
      repaintFinder,
      matchesGoldenFile('ink_sparkle.$positionName.$i.png'),
    );
  }
}

// Warm up shader. Compilation is of the order of 10 milliseconds and
// Animation is < 1000 milliseconds. Use 2000 milliseconds as a safety
// net to prevent flakiness.
Future<void> _warmUpShader(WidgetTester tester, Finder buttonFinder) async {
  await tester.tap(buttonFinder);
  await tester.pumpAndSettle(const Duration(milliseconds: 2000));
}