animated_icons_test.dart 8.66 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 8 9 10 11
import 'dart:math' as math show pi;

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

12
import '../flutter_test_alternative.dart' show Fake;
13 14
import '../widgets/semantics_tester.dart';

15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
class MockCanvas extends Fake implements Canvas {
  Path capturedPath;
  Paint capturedPaint;

  @override
  void drawPath(Path path, Paint paint) {
    capturedPath = path;
    capturedPaint = paint;
  }

  double capturedSx;
  double capturedSy;

  @override
  void scale(double sx, [double sy]) {
    capturedSx = sx;
    capturedSy = sy;
  }

  final List<RecordedCanvasCall> invocations = <RecordedCanvasCall>[];

  @override
  void rotate(double radians) {
    invocations.add(RecordedRotate(radians));
  }

  @override
  void translate(double dx, double dy) {
    invocations.add(RecordedTranslate(dx, dy));
  }
}

@immutable
abstract class RecordedCanvasCall {
  const RecordedCanvasCall();
}

class RecordedRotate extends RecordedCanvasCall {
  const RecordedRotate(this.radians);

  final double radians;

  @override
  bool operator ==(Object other) {
    return other is RecordedRotate && other.radians == radians;
  }

  @override
  int get hashCode => radians.hashCode;
}

class RecordedTranslate extends RecordedCanvasCall {
  const RecordedTranslate(this.dx, this.dy);

  final double dx;
  final double dy;

  @override
  bool operator ==(Object other) {
    return other is RecordedTranslate && other.dx == dx && other.dy == dy;
  }

  @override
  int get hashCode => hashValues(dx, dy);
}
80 81 82 83 84 85

void main() {
  testWidgets('IconTheme color', (WidgetTester tester) async {
    await tester.pumpWidget(
      const Directionality(
        textDirection: TextDirection.ltr,
86 87 88
        child: IconTheme(
          data: IconThemeData(
            color: Color(0xFF666666),
89
          ),
90 91
          child: AnimatedIcon(
            progress: AlwaysStoppedAnimation<double>(0.0),
92
            icon: AnimatedIcons.arrow_menu,
93
          ),
94 95 96 97
        ),
      ),
    );
    final CustomPaint customPaint = tester.widget(find.byType(CustomPaint));
98
    final MockCanvas canvas = MockCanvas();
99
    customPaint.painter.paint(canvas, const Size(48.0, 48.0));
100
    expect(canvas.capturedPaint, hasColor(0xFF666666));
101 102 103 104 105 106
  });

  testWidgets('IconTheme opacity', (WidgetTester tester) async {
    await tester.pumpWidget(
      const Directionality(
        textDirection: TextDirection.ltr,
107 108 109
        child: IconTheme(
          data: IconThemeData(
            color: Color(0xFF666666),
110
            opacity: 0.5,
111
          ),
112 113
          child: AnimatedIcon(
            progress: AlwaysStoppedAnimation<double>(0.0),
114
            icon: AnimatedIcons.arrow_menu,
115
          ),
116 117 118 119
        ),
      ),
    );
    final CustomPaint customPaint = tester.widget(find.byType(CustomPaint));
120
    final MockCanvas canvas = MockCanvas();
121
    customPaint.painter.paint(canvas, const Size(48.0, 48.0));
122
    expect(canvas.capturedPaint, hasColor(0x80666666));
123 124 125 126 127 128
  });

  testWidgets('color overrides IconTheme color', (WidgetTester tester) async {
    await tester.pumpWidget(
      const Directionality(
        textDirection: TextDirection.ltr,
129 130 131
        child: IconTheme(
          data: IconThemeData(
            color: Color(0xFF666666),
132
          ),
133 134
          child: AnimatedIcon(
            progress: AlwaysStoppedAnimation<double>(0.0),
135
            icon: AnimatedIcons.arrow_menu,
136
            color: Color(0xFF0000FF),
137
          ),
138 139 140 141
        ),
      ),
    );
    final CustomPaint customPaint = tester.widget(find.byType(CustomPaint));
142
    final MockCanvas canvas = MockCanvas();
143
    customPaint.painter.paint(canvas, const Size(48.0, 48.0));
144
    expect(canvas.capturedPaint, hasColor(0xFF0000FF));
145 146 147 148 149 150
  });

  testWidgets('IconTheme size', (WidgetTester tester) async {
    await tester.pumpWidget(
      const Directionality(
        textDirection: TextDirection.ltr,
151 152 153
        child: IconTheme(
          data: IconThemeData(
            color: Color(0xFF666666),
154 155
            size: 12.0,
          ),
156 157
          child: AnimatedIcon(
            progress: AlwaysStoppedAnimation<double>(0.0),
158
            icon: AnimatedIcons.arrow_menu,
159
          ),
160 161 162 163
        ),
      ),
    );
    final CustomPaint customPaint = tester.widget(find.byType(CustomPaint));
164
    final MockCanvas canvas = MockCanvas();
165 166
    customPaint.painter.paint(canvas, const Size(12.0, 12.0));
    // arrow_menu default size is 48x48 so we expect it to be scaled by 0.25.
167 168
    expect(canvas.capturedSx, 0.25);
    expect(canvas.capturedSy, 0.25);
169 170 171 172 173 174
  });

  testWidgets('size overridesIconTheme size', (WidgetTester tester) async {
    await tester.pumpWidget(
      const Directionality(
        textDirection: TextDirection.ltr,
175 176 177
        child: IconTheme(
          data: IconThemeData(
            color: Color(0xFF666666),
178 179
            size: 12.0,
          ),
180 181
          child: AnimatedIcon(
            progress: AlwaysStoppedAnimation<double>(0.0),
182 183
            icon: AnimatedIcons.arrow_menu,
            size: 96.0,
184
          ),
185 186 187 188
        ),
      ),
    );
    final CustomPaint customPaint = tester.widget(find.byType(CustomPaint));
189
    final MockCanvas canvas = MockCanvas();
190 191
    customPaint.painter.paint(canvas, const Size(12.0, 12.0));
    // arrow_menu default size is 48x48 so we expect it to be scaled by 2.
192 193
    expect(canvas.capturedSx, 2);
    expect(canvas.capturedSy, 2);
194 195 196
  });

  testWidgets('Semantic label', (WidgetTester tester) async {
197
    final SemanticsTester semantics = SemanticsTester(tester);
198 199 200 201

    await tester.pumpWidget(
      const Directionality(
        textDirection: TextDirection.ltr,
202 203
        child: AnimatedIcon(
          progress: AlwaysStoppedAnimation<double>(0.0),
204 205 206 207 208 209 210 211
          icon: AnimatedIcons.arrow_menu,
          size: 96.0,
          semanticLabel: 'a label',
        ),
      ),
    );

    expect(semantics, includesNodeWith(label: 'a label'));
212 213

    semantics.dispose();
214 215 216 217 218 219
  });

  testWidgets('Inherited text direction rtl', (WidgetTester tester) async {
    await tester.pumpWidget(
      const Directionality(
        textDirection: TextDirection.rtl,
220 221 222
        child: IconTheme(
          data: IconThemeData(
            color: Color(0xFF666666),
223
          ),
224 225
          child: AnimatedIcon(
            progress: AlwaysStoppedAnimation<double>(0.0),
226
            icon: AnimatedIcons.arrow_menu,
227
          ),
228 229 230 231
        ),
      ),
    );
    final CustomPaint customPaint = tester.widget(find.byType(CustomPaint));
232
    final MockCanvas canvas = MockCanvas();
233
    customPaint.painter.paint(canvas, const Size(48.0, 48.0));
234 235 236
    expect(canvas.invocations, const <RecordedCanvasCall>[
      RecordedRotate(math.pi),
      RecordedTranslate(-48, -48),
237 238 239 240 241 242 243
    ]);
  });

  testWidgets('Inherited text direction ltr', (WidgetTester tester) async {
    await tester.pumpWidget(
      const Directionality(
        textDirection: TextDirection.ltr,
244 245 246
        child: IconTheme(
          data: IconThemeData(
            color: Color(0xFF666666),
247
          ),
248 249
          child: AnimatedIcon(
            progress: AlwaysStoppedAnimation<double>(0.0),
250
            icon: AnimatedIcons.arrow_menu,
251
          ),
252 253 254 255
        ),
      ),
    );
    final CustomPaint customPaint = tester.widget(find.byType(CustomPaint));
256
    final MockCanvas canvas = MockCanvas();
257
    customPaint.painter.paint(canvas, const Size(48.0, 48.0));
258
    expect(canvas.invocations, isEmpty);
259 260 261 262 263 264
  });

  testWidgets('Inherited text direction overridden', (WidgetTester tester) async {
    await tester.pumpWidget(
      const Directionality(
        textDirection: TextDirection.ltr,
265 266 267
        child: IconTheme(
          data: IconThemeData(
            color: Color(0xFF666666),
268
          ),
269 270
          child: AnimatedIcon(
            progress: AlwaysStoppedAnimation<double>(0.0),
271 272
            icon: AnimatedIcons.arrow_menu,
            textDirection: TextDirection.rtl,
273
          ),
274 275 276 277
        ),
      ),
    );
    final CustomPaint customPaint = tester.widget(find.byType(CustomPaint));
278
    final MockCanvas canvas = MockCanvas();
279
    customPaint.painter.paint(canvas, const Size(48.0, 48.0));
280 281 282
    expect(canvas.invocations, const <RecordedCanvasCall>[
      RecordedRotate(math.pi),
      RecordedTranslate(-48, -48),
283 284 285 286
    ]);
  });
}

287
PaintColorMatcher hasColor(int color) {
288
  return PaintColorMatcher(color);
289 290 291 292 293 294 295 296 297 298 299 300 301
}

class PaintColorMatcher extends Matcher {
  const PaintColorMatcher(this.expectedColor);

  final int expectedColor;

  @override
  Description describe(Description description) =>
    description.add('color was not $expectedColor');

  @override
  bool matches(dynamic item, Map<dynamic, dynamic> matchState) {
302
    final Paint actualPaint = item as Paint;
303
    return actualPaint.color == Color(expectedColor);
304 305
  }
}