slider_test.dart 11.1 KB
Newer Older
1 2 3 4 5 6
// Copyright 2016 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.

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
7
import 'package:flutter/rendering.dart';
8 9 10
import 'package:flutter/scheduler.dart';
import 'package:flutter_test/flutter_test.dart';

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

13
void main() {
14

15
  Future<void> _dragSlider(WidgetTester tester, Key sliderKey) {
16 17 18 19 20 21
    final Offset topLeft = tester.getTopLeft(find.byKey(sliderKey));
    const double unit = CupertinoThumbPainter.radius;
    const double delta = 3.0 * unit;
    return tester.dragFrom(topLeft + const Offset(unit, unit), const Offset(delta, 0.0));
  }

22
  testWidgets('Slider does not move when tapped (LTR)', (WidgetTester tester) async {
23
    final Key sliderKey = UniqueKey();
24 25
    double value = 0.0;

26
    await tester.pumpWidget(Directionality(
27
      textDirection: TextDirection.ltr,
28
      child: StatefulBuilder(
29
        builder: (BuildContext context, StateSetter setState) {
30 31 32
          return Material(
            child: Center(
              child: CupertinoSlider(
33 34 35 36 37 38 39 40 41 42 43 44
                key: sliderKey,
                value: value,
                onChanged: (double newValue) {
                  setState(() {
                    value = newValue;
                  });
                },
              ),
            ),
          );
        },
      ),
45 46 47 48 49 50 51 52 53 54 55 56
    ));

    expect(value, equals(0.0));
    await tester.tap(find.byKey(sliderKey));
    expect(value, equals(0.0));
    await tester.pump(); // No animation should start.
    // Check the transientCallbackCount before tearing down the widget to ensure
    // that no animation is running.
    expect(SchedulerBinding.instance.transientCallbackCount, equals(0));
  });

  testWidgets('Slider does not move when tapped (RTL)', (WidgetTester tester) async {
57
    final Key sliderKey = UniqueKey();
58 59
    double value = 0.0;

60
    await tester.pumpWidget(Directionality(
61
      textDirection: TextDirection.rtl,
62
      child: StatefulBuilder(
63
        builder: (BuildContext context, StateSetter setState) {
64 65 66
          return Material(
            child: Center(
              child: CupertinoSlider(
67 68 69 70 71 72 73 74 75 76 77 78 79
                key: sliderKey,
                value: value,
                onChanged: (double newValue) {
                  setState(() {
                    value = newValue;
                  });
                },
              ),
            ),
          );
        },
      ),
    ));
80 81 82 83 84 85 86 87 88 89

    expect(value, equals(0.0));
    await tester.tap(find.byKey(sliderKey));
    expect(value, equals(0.0));
    await tester.pump(); // No animation should start.
    // Check the transientCallbackCount before tearing down the widget to ensure
    // that no animation is running.
    expect(SchedulerBinding.instance.transientCallbackCount, equals(0));
  });

90
  testWidgets('Slider calls onChangeStart once when interaction begins', (WidgetTester tester) async {
91
    final Key sliderKey = UniqueKey();
92 93 94
    double value = 0.0;
    int numberOfTimesOnChangeStartIsCalled = 0;

95
    await tester.pumpWidget(Directionality(
96
      textDirection: TextDirection.ltr,
97
      child: StatefulBuilder(
98
        builder: (BuildContext context, StateSetter setState) {
99 100 101
          return Material(
            child: Center(
              child: CupertinoSlider(
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
                key: sliderKey,
                value: value,
                onChanged: (double newValue) {
                  setState(() {
                    value = newValue;
                  });
                },
                onChangeStart: (double value) {
                  numberOfTimesOnChangeStartIsCalled++;
                }
              ),
            ),
          );
        },
      ),
    ));

    await _dragSlider(tester, sliderKey);
120

121 122 123 124 125 126 127 128 129
    expect(numberOfTimesOnChangeStartIsCalled, equals(1));

    await tester.pump(); // No animation should start.
    // Check the transientCallbackCount before tearing down the widget to ensure
    // that no animation is running.
    expect(SchedulerBinding.instance.transientCallbackCount, equals(0));
  });

  testWidgets('Slider calls onChangeEnd once after interaction has ended', (WidgetTester tester) async {
130
    final Key sliderKey = UniqueKey();
131 132 133
    double value = 0.0;
    int numberOfTimesOnChangeEndIsCalled = 0;

134
    await tester.pumpWidget(Directionality(
135
      textDirection: TextDirection.ltr,
136
      child: StatefulBuilder(
137
        builder: (BuildContext context, StateSetter setState) {
138 139 140
          return Material(
            child: Center(
              child: CupertinoSlider(
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
                key: sliderKey,
                value: value,
                onChanged: (double newValue) {
                  setState(() {
                    value = newValue;
                  });
                },
                onChangeEnd: (double value) {
                  numberOfTimesOnChangeEndIsCalled++;
                }
              ),
            ),
          );
        },
      ),
    ));
157

158
    await _dragSlider(tester, sliderKey);
159

160 161 162 163 164 165 166
    expect(numberOfTimesOnChangeEndIsCalled, equals(1));

    await tester.pump(); // No animation should start.
    // Check the transientCallbackCount before tearing down the widget to ensure
    // that no animation is running.
    expect(SchedulerBinding.instance.transientCallbackCount, equals(0));
  });
167 168

  testWidgets('Slider moves when dragged (LTR)', (WidgetTester tester) async {
169
    final Key sliderKey = UniqueKey();
170
    double value = 0.0;
171 172
    double startValue;
    double endValue;
173

174
    await tester.pumpWidget(Directionality(
175
      textDirection: TextDirection.ltr,
176
      child: StatefulBuilder(
177
        builder: (BuildContext context, StateSetter setState) {
178 179 180
          return Material(
            child: Center(
              child: CupertinoSlider(
181 182 183 184 185 186 187
                key: sliderKey,
                value: value,
                onChanged: (double newValue) {
                  setState(() {
                    value = newValue;
                  });
                },
188 189 190 191 192 193
                onChangeStart: (double value) {
                  startValue = value;
                },
                onChangeEnd: (double value) {
                  endValue = value;
                }
194 195 196 197 198
              ),
            ),
          );
        },
      ),
199
    ));
200 201

    expect(value, equals(0.0));
202

203
    final Offset topLeft = tester.getTopLeft(find.byKey(sliderKey));
204 205
    const double unit = CupertinoThumbPainter.radius;
    const double delta = 3.0 * unit;
206
    await tester.dragFrom(topLeft + const Offset(unit, unit), const Offset(delta, 0.0));
207

208
    final Size size = tester.getSize(find.byKey(sliderKey));
209 210 211 212
    final double finalValue = delta / (size.width - 2.0 * (8.0 + CupertinoThumbPainter.radius));
    expect(startValue, equals(0.0));
    expect(value, equals(finalValue));
    expect(endValue, equals(finalValue));
213

214 215 216 217 218
    await tester.pump(); // No animation should start.
    // Check the transientCallbackCount before tearing down the widget to ensure
    // that no animation is running.
    expect(SchedulerBinding.instance.transientCallbackCount, equals(0));
  });
219

220
  testWidgets('Slider moves when dragged (RTL)', (WidgetTester tester) async {
221
    final Key sliderKey = UniqueKey();
222
    double value = 0.0;
223 224
    double startValue;
    double endValue;
225

226
    await tester.pumpWidget(Directionality(
227
      textDirection: TextDirection.rtl,
228
      child: StatefulBuilder(
229
        builder: (BuildContext context, StateSetter setState) {
230 231 232
          return Material(
            child: Center(
              child: CupertinoSlider(
233 234 235 236 237 238 239
                key: sliderKey,
                value: value,
                onChanged: (double newValue) {
                  setState(() {
                    value = newValue;
                  });
                },
240 241 242 243 244 245 246 247 248 249
                onChangeStart: (double value) {
                  setState(() {
                    startValue = value;
                  });
                },
                onChangeEnd: (double value) {
                  setState(() {
                    endValue = value;
                  });
                }
250 251 252 253 254 255 256 257
              ),
            ),
          );
        },
      ),
    ));

    expect(value, equals(0.0));
258

259 260 261 262
    final Offset bottomRight = tester.getBottomRight(find.byKey(sliderKey));
    const double unit = CupertinoThumbPainter.radius;
    const double delta = 3.0 * unit;
    await tester.dragFrom(bottomRight - const Offset(unit, unit), const Offset(-delta, 0.0));
263

264
    final Size size = tester.getSize(find.byKey(sliderKey));
265 266 267 268
    final double finalValue = delta / (size.width - 2.0 * (8.0 + CupertinoThumbPainter.radius));
    expect(startValue, equals(0.0));
    expect(value, equals(finalValue));
    expect(endValue, equals(finalValue));
269

270 271 272 273 274 275
    await tester.pump(); // No animation should start.
    // Check the transientCallbackCount before tearing down the widget to ensure
    // that no animation is running.
    expect(SchedulerBinding.instance.transientCallbackCount, equals(0));
  });

276
  testWidgets('Slider Semantics', (WidgetTester tester) async {
277
    final SemanticsTester semantics = SemanticsTester(tester);
278

279
    await tester.pumpWidget(Directionality(
280
      textDirection: TextDirection.ltr,
281
      child: CupertinoSlider(
282 283 284
        value: 0.5,
        onChanged: (double v) {},
      ),
285
    ));
286 287

    expect(semantics, hasSemantics(
288
      TestSemantics.root(
289
        children: <TestSemantics>[
290
          TestSemantics.rootChild(
291
            id: 1,
292 293 294 295
            value: '50%',
            increasedValue: '60%',
            decreasedValue: '40%',
            textDirection: TextDirection.ltr,
296 297 298 299 300 301 302 303 304
            actions: SemanticsAction.decrease.index | SemanticsAction.increase.index,
          ),
        ]
      ),
      ignoreRect: true,
      ignoreTransform: true,
    ));

    // Disable slider
305
    await tester.pumpWidget(const Directionality(
306
      textDirection: TextDirection.ltr,
307
      child: CupertinoSlider(
308 309 310
        value: 0.5,
        onChanged: null,
      ),
311
    ));
312 313

    expect(semantics, hasSemantics(
314
      TestSemantics.root(),
315 316 317 318 319 320
      ignoreRect: true,
      ignoreTransform: true,
    ));

    semantics.dispose();
  });
321 322 323 324

  testWidgets('Slider Semantics can be updated', (WidgetTester tester) async {
    final SemanticsHandle handle = tester.ensureSemantics();
    double value = 0.5;
325
    await tester.pumpWidget(Directionality(
326
      textDirection: TextDirection.ltr,
327
      child: CupertinoSlider(
328 329 330 331 332
        value: value,
        onChanged: (double v) { },
      ),
    ));

333
    expect(tester.getSemantics(find.byType(CupertinoSlider)), matchesSemantics(
334 335 336 337 338 339 340 341 342
      hasIncreaseAction: true,
      hasDecreaseAction: true,
      value: '50%',
      increasedValue: '60%',
      decreasedValue: '40%',
      textDirection: TextDirection.ltr,
    ));

    value = 0.6;
343
    await tester.pumpWidget(Directionality(
344
      textDirection: TextDirection.ltr,
345
      child: CupertinoSlider(
346 347 348 349 350
        value: value,
        onChanged: (double v) { },
      ),
    ));

351
    expect(tester.getSemantics(find.byType(CupertinoSlider)), matchesSemantics(
352 353 354 355 356 357 358 359 360 361
      hasIncreaseAction: true,
      hasDecreaseAction: true,
      value: '60%',
      increasedValue: '70%',
      decreasedValue: '50%',
      textDirection: TextDirection.ltr,
    ));

    handle.dispose();
  });
362
}