button_test.dart 12 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
import 'package:flutter/rendering.dart';
6 7 8 9
import 'package:flutter/cupertino.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_test/flutter_test.dart';

10 11
import '../widgets/semantics_tester.dart';

12
const TextStyle testStyle = TextStyle(
13 14
  fontFamily: 'Ahem',
  fontSize: 10.0,
xster's avatar
xster committed
15
  letterSpacing: 0.0,
16 17
);

18
void main() {
19
  testWidgets('Default layout minimum size', (WidgetTester tester) async {
20
    await tester.pumpWidget(
Ian Hickson's avatar
Ian Hickson committed
21
      boilerplate(child: const CupertinoButton(
22
        child: Text('X', style: testStyle),
23
        onPressed: null,
24
      )),
25
    );
26
    final RenderBox buttonBox = tester.renderObject(find.byType(CupertinoButton));
27 28
    expect(
      buttonBox.size,
29 30 31 32 33 34
      // 1 10px character + 16px * 2 is smaller than the default 44px minimum.
      const Size.square(44.0),
    );
  });

  testWidgets('Minimum size parameter', (WidgetTester tester) async {
35
    const double minSize = 60.0;
36
    await tester.pumpWidget(
37
      boilerplate(child: const CupertinoButton(
38
        child: Text('X', style: testStyle),
39 40
        onPressed: null,
        minSize: minSize,
41
      )),
42 43 44 45 46
    );
    final RenderBox buttonBox = tester.renderObject(find.byType(CupertinoButton));
    expect(
      buttonBox.size,
      // 1 10px character + 16px * 2 is smaller than defined 60.0px minimum
47
      const Size.square(minSize),
48 49 50 51 52
    );
  });

  testWidgets('Size grows with text', (WidgetTester tester) async {
    await tester.pumpWidget(
Ian Hickson's avatar
Ian Hickson committed
53
      boilerplate(child: const CupertinoButton(
54
        child: Text('XXXX', style: testStyle),
55
        onPressed: null,
56
      )),
57
    );
58
    final RenderBox buttonBox = tester.renderObject(find.byType(CupertinoButton));
59 60
    expect(
      buttonBox.size.width,
61 62
      // 4 10px character + 16px * 2 = 72.
      72.0,
63 64 65
    );
  });

66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
  // TODO(LongCatIsLoong): Uncomment once https://github.com/flutter/flutter/issues/44115
  // is fixed.
  /*
  testWidgets(
    'CupertinoButton.filled default color contrast meets guideline',
    (WidgetTester tester) async {
      // The native color combination systemBlue text over white background fails
      // to pass the color contrast guideline.
      //await tester.pumpWidget(
      //  CupertinoTheme(
      //    data: const CupertinoThemeData(),
      //    child: Directionality(
      //      textDirection: TextDirection.ltr,
      //      child: CupertinoButton.filled(
      //        child: const Text('Button'),
      //        onPressed: () {},
      //      ),
      //    ),
      //  ),
      //);
      //await expectLater(tester, meetsGuideline(textContrastGuideline));

      await tester.pumpWidget(
        CupertinoApp(
          theme: const CupertinoThemeData(brightness: Brightness.dark),
          home: CupertinoPageScaffold(
            child: CupertinoButton.filled(
              child: const Text('Button'),
              onPressed: () {},
            ),
          ),
        ),
      );

      await expectLater(tester, meetsGuideline(textContrastGuideline));
  });
  */

104
  testWidgets('Button with background is wider', (WidgetTester tester) async {
Ian Hickson's avatar
Ian Hickson committed
105
    await tester.pumpWidget(boilerplate(child: const CupertinoButton(
106
      child: Text('X', style: testStyle),
107
      onPressed: null,
108
      color: Color(0xFFFFFFFF),
109
    )));
110
    final RenderBox buttonBox = tester.renderObject(find.byType(CupertinoButton));
111 112
    expect(
      buttonBox.size.width,
113 114
      // 1 10px character + 64 * 2 = 138 for buttons with background.
      138.0,
115 116 117 118
    );
  });

  testWidgets('Custom padding', (WidgetTester tester) async {
Ian Hickson's avatar
Ian Hickson committed
119
    await tester.pumpWidget(boilerplate(child: const CupertinoButton(
120
      child: Text('X', style: testStyle),
121
      onPressed: null,
122
      padding: EdgeInsets.all(100.0),
123
    )));
124
    final RenderBox buttonBox = tester.renderObject(find.byType(CupertinoButton));
125 126
    expect(
      buttonBox.size,
127
      const Size.square(210.0),
128 129 130 131 132 133
    );
  });

  testWidgets('Button takes taps', (WidgetTester tester) async {
    bool value = false;
    await tester.pumpWidget(
134
      StatefulBuilder(
135
        builder: (BuildContext context, StateSetter setState) {
Ian Hickson's avatar
Ian Hickson committed
136
          return boilerplate(
137
            child: CupertinoButton(
138
              child: const Text('Tap me'),
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
              onPressed: () {
                setState(() {
                  value = true;
                });
              },
            ),
          );
        },
      ),
    );

    expect(value, isFalse);
    // No animating by default.
    expect(SchedulerBinding.instance.transientCallbackCount, equals(0));
    await tester.tap(find.byType(CupertinoButton));
    expect(value, isTrue);
    // Animates.
    expect(SchedulerBinding.instance.transientCallbackCount, equals(1));
  });

  testWidgets('Disabled button doesn\'t animate', (WidgetTester tester) async {
Ian Hickson's avatar
Ian Hickson committed
160
    await tester.pumpWidget(boilerplate(child: const CupertinoButton(
161
      child: Text('Tap me'),
162 163 164 165 166 167 168
      onPressed: null,
    )));
    expect(SchedulerBinding.instance.transientCallbackCount, equals(0));
    await tester.tap(find.byType(CupertinoButton));
    // Still doesn't animate.
    expect(SchedulerBinding.instance.transientCallbackCount, equals(0));
  });
169 170

  testWidgets('pressedOpacity defaults to 0.1', (WidgetTester tester) async {
171
    await tester.pumpWidget(boilerplate(child: CupertinoButton(
172
      child: const Text('Tap me'),
173 174 175 176
      onPressed: () { },
    )));

    // Keep a "down" gesture on the button
177
    final Offset center = tester.getCenter(find.byType(CupertinoButton));
178 179 180 181
    await tester.startGesture(center);
    await tester.pumpAndSettle();

    // Check opacity
182
    final FadeTransition opacity = tester.widget(find.descendant(
183
      of: find.byType(CupertinoButton),
184
      matching: find.byType(FadeTransition),
185
    ));
186
    expect(opacity.opacity.value, 0.4);
187 188 189
  });

  testWidgets('pressedOpacity parameter', (WidgetTester tester) async {
190
    const double pressedOpacity = 0.5;
191
    await tester.pumpWidget(boilerplate(child: CupertinoButton(
192
      pressedOpacity: pressedOpacity,
193
      child: const Text('Tap me'),
194 195 196 197
      onPressed: () { },
    )));

    // Keep a "down" gesture on the button
198
    final Offset center = tester.getCenter(find.byType(CupertinoButton));
199 200 201 202
    await tester.startGesture(center);
    await tester.pumpAndSettle();

    // Check opacity
203
    final FadeTransition opacity = tester.widget(find.descendant(
204
      of: find.byType(CupertinoButton),
205
      matching: find.byType(FadeTransition),
206
    ));
207
    expect(opacity.opacity.value, pressedOpacity);
208
  });
209 210

  testWidgets('Cupertino button is semantically a button', (WidgetTester tester) async {
211
    final SemanticsTester semantics = SemanticsTester(tester);
212 213
    await tester.pumpWidget(
      boilerplate(
214 215
          child: Center(
            child: CupertinoButton(
216
              onPressed: () { },
217
              child: const Text('ABC'),
218 219 220 221 222 223
            ),
          ),
      ),
    );

    expect(semantics, hasSemantics(
224
      TestSemantics.root(
225
        children: <TestSemantics>[
226
          TestSemantics.rootChild(
227 228
            actions: SemanticsAction.tap.index,
            label: 'ABC',
229
            flags: SemanticsFlag.isButton.index,
230
          ),
231 232 233 234 235 236 237 238 239
        ],
      ),
      ignoreId: true,
      ignoreRect: true,
      ignoreTransform: true,
    ));

    semantics.dispose();
  });
240 241

  testWidgets('Can specify colors', (WidgetTester tester) async {
242
    await tester.pumpWidget(boilerplate(child: CupertinoButton(
243
      child: const Text('Skeuomorph me'),
244 245
      color: const Color(0x000000FF),
      disabledColor: const Color(0x0000FF00),
246 247 248 249 250
      onPressed: () { },
    )));

    BoxDecoration boxDecoration = tester.widget<DecoratedBox>(
        find.widgetWithText(DecoratedBox, 'Skeuomorph me')
251
      ).decoration as BoxDecoration;
252

253
    expect(boxDecoration.color, const Color(0x000000FF));
254 255

    await tester.pumpWidget(boilerplate(child: const CupertinoButton(
256
      child: Text('Skeuomorph me'),
257 258
      color: Color(0x000000FF),
      disabledColor: Color(0x0000FF00),
259 260 261 262 263
      onPressed: null,
    )));

    boxDecoration = tester.widget<DecoratedBox>(
        find.widgetWithText(DecoratedBox, 'Skeuomorph me')
264
      ).decoration as BoxDecoration;
265

266
    expect(boxDecoration.color, const Color(0x0000FF00));
267
  });
xster's avatar
xster committed
268

269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
  testWidgets('Can specify dynamic colors', (WidgetTester tester) async {
    const Color bgColor = CupertinoDynamicColor.withBrightness(
      color: Color(0xFF123456),
      darkColor: Color(0xFF654321),
    );

    const Color inactive = CupertinoDynamicColor.withBrightness(
      color: Color(0xFF111111),
      darkColor: Color(0xFF222222),
    );

    await tester.pumpWidget(
      MediaQuery(
        data: const MediaQueryData(platformBrightness: Brightness.dark),
        child: boilerplate(child: CupertinoButton(
          child: const Text('Skeuomorph me'),
          color: bgColor,
          disabledColor: inactive,
          onPressed: () { },
288
        )),
289 290 291 292 293
      ),
    );

    BoxDecoration boxDecoration = tester.widget<DecoratedBox>(
      find.widgetWithText(DecoratedBox, 'Skeuomorph me')
294
    ).decoration as BoxDecoration;
295 296 297 298 299 300 301 302 303 304 305

    expect(boxDecoration.color.value, 0xFF654321);

    await tester.pumpWidget(
      MediaQuery(
        data: const MediaQueryData(platformBrightness: Brightness.light),
        child: boilerplate(child: const CupertinoButton(
          child: Text('Skeuomorph me'),
          color: bgColor,
          disabledColor: inactive,
          onPressed: null,
306
        )),
307 308 309 310 311
      ),
    );

    boxDecoration = tester.widget<DecoratedBox>(
      find.widgetWithText(DecoratedBox, 'Skeuomorph me')
312
    ).decoration as BoxDecoration;
313 314 315 316 317

    // Disabled color.
    expect(boxDecoration.color.value, 0xFF111111);
  });

318
  testWidgets('Button respects themes', (WidgetTester tester) async {
xster's avatar
xster committed
319 320 321 322 323
    TextStyle textStyle;

    await tester.pumpWidget(
      CupertinoApp(
        home: CupertinoButton(
324
          onPressed: () { },
xster's avatar
xster committed
325 326 327 328 329 330 331 332 333 334 335 336 337
          child: Builder(builder: (BuildContext context) {
            textStyle = DefaultTextStyle.of(context).style;
            return const Placeholder();
          }),
        ),
      ),
    );

    expect(textStyle.color, CupertinoColors.activeBlue);

    await tester.pumpWidget(
      CupertinoApp(
        home: CupertinoButton.filled(
338
          onPressed: () { },
xster's avatar
xster committed
339 340 341 342 343 344 345 346
          child: Builder(builder: (BuildContext context) {
            textStyle = DefaultTextStyle.of(context).style;
            return const Placeholder();
          }),
        ),
      ),
    );

347
    expect(textStyle.color, isSameColorAs(CupertinoColors.white));
xster's avatar
xster committed
348 349 350
    BoxDecoration decoration = tester.widget<DecoratedBox>(
      find.descendant(
        of: find.byType(CupertinoButton),
351
        matching: find.byType(DecoratedBox),
352
      ),
353
    ).decoration as BoxDecoration;
xster's avatar
xster committed
354 355 356 357 358 359
    expect(decoration.color, CupertinoColors.activeBlue);

    await tester.pumpWidget(
      CupertinoApp(
        theme: const CupertinoThemeData(brightness: Brightness.dark),
        home: CupertinoButton(
360
          onPressed: () { },
xster's avatar
xster committed
361 362 363 364 365 366 367
          child: Builder(builder: (BuildContext context) {
            textStyle = DefaultTextStyle.of(context).style;
            return const Placeholder();
          }),
        ),
      ),
    );
368
    expect(textStyle.color, isSameColorAs(CupertinoColors.systemBlue.darkColor));
xster's avatar
xster committed
369 370 371 372 373

    await tester.pumpWidget(
      CupertinoApp(
        theme: const CupertinoThemeData(brightness: Brightness.dark),
        home: CupertinoButton.filled(
374
          onPressed: () { },
xster's avatar
xster committed
375 376 377 378 379 380 381
          child: Builder(builder: (BuildContext context) {
            textStyle = DefaultTextStyle.of(context).style;
            return const Placeholder();
          }),
        ),
      ),
    );
382
    expect(textStyle.color, isSameColorAs(CupertinoColors.black));
xster's avatar
xster committed
383 384 385
    decoration = tester.widget<DecoratedBox>(
      find.descendant(
        of: find.byType(CupertinoButton),
386
        matching: find.byType(DecoratedBox),
387
      ),
388
    ).decoration as BoxDecoration;
389
    expect(decoration.color, isSameColorAs(CupertinoColors.systemBlue.darkColor));
xster's avatar
xster committed
390
  });
391
}
Ian Hickson's avatar
Ian Hickson committed
392 393

Widget boilerplate({ Widget child }) {
394
  return Directionality(
Ian Hickson's avatar
Ian Hickson committed
395
    textDirection: TextDirection.ltr,
396
    child: Center(child: child),
Ian Hickson's avatar
Ian Hickson committed
397
  );
398
}