blob: c7143562024543b10d0dda6c97b5823d6956bc17 [file] [log] [blame]
// 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';
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_test/flutter_test.dart';
import '../rendering/mock_canvas.dart';
import '../widgets/semantics_tester.dart';
void main() {
Future<void> _dragSlider(WidgetTester tester, Key sliderKey) {
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));
}
testWidgets('Slider does not move when tapped (LTR)', (WidgetTester tester) async {
final Key sliderKey = UniqueKey();
double value = 0.0;
await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Material(
child: Center(
child: CupertinoSlider(
key: sliderKey,
value: value,
onChanged: (double newValue) {
setState(() {
value = newValue;
});
},
),
),
);
},
),
));
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 {
final Key sliderKey = UniqueKey();
double value = 0.0;
await tester.pumpWidget(Directionality(
textDirection: TextDirection.rtl,
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Material(
child: Center(
child: CupertinoSlider(
key: sliderKey,
value: value,
onChanged: (double newValue) {
setState(() {
value = newValue;
});
},
),
),
);
},
),
));
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 calls onChangeStart once when interaction begins', (WidgetTester tester) async {
final Key sliderKey = UniqueKey();
double value = 0.0;
int numberOfTimesOnChangeStartIsCalled = 0;
await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Material(
child: Center(
child: CupertinoSlider(
key: sliderKey,
value: value,
onChanged: (double newValue) {
setState(() {
value = newValue;
});
},
onChangeStart: (double value) {
numberOfTimesOnChangeStartIsCalled++;
},
),
),
);
},
),
));
await _dragSlider(tester, sliderKey);
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 {
final Key sliderKey = UniqueKey();
double value = 0.0;
int numberOfTimesOnChangeEndIsCalled = 0;
await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Material(
child: Center(
child: CupertinoSlider(
key: sliderKey,
value: value,
onChanged: (double newValue) {
setState(() {
value = newValue;
});
},
onChangeEnd: (double value) {
numberOfTimesOnChangeEndIsCalled++;
},
),
),
);
},
),
));
await _dragSlider(tester, sliderKey);
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));
});
testWidgets('Slider moves when dragged (LTR)', (WidgetTester tester) async {
final Key sliderKey = UniqueKey();
double value = 0.0;
double startValue;
double endValue;
await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Material(
child: Center(
child: CupertinoSlider(
key: sliderKey,
value: value,
onChanged: (double newValue) {
setState(() {
value = newValue;
});
},
onChangeStart: (double value) {
startValue = value;
},
onChangeEnd: (double value) {
endValue = value;
},
),
),
);
},
),
));
expect(value, equals(0.0));
final Offset topLeft = tester.getTopLeft(find.byKey(sliderKey));
const double unit = CupertinoThumbPainter.radius;
const double delta = 3.0 * unit;
await tester.dragFrom(topLeft + const Offset(unit, unit), const Offset(delta, 0.0));
final Size size = tester.getSize(find.byKey(sliderKey));
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));
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 moves when dragged (RTL)', (WidgetTester tester) async {
final Key sliderKey = UniqueKey();
double value = 0.0;
double startValue;
double endValue;
await tester.pumpWidget(Directionality(
textDirection: TextDirection.rtl,
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Material(
child: Center(
child: CupertinoSlider(
key: sliderKey,
value: value,
onChanged: (double newValue) {
setState(() {
value = newValue;
});
},
onChangeStart: (double value) {
setState(() {
startValue = value;
});
},
onChangeEnd: (double value) {
setState(() {
endValue = value;
});
},
),
),
);
},
),
));
expect(value, equals(0.0));
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));
final Size size = tester.getSize(find.byKey(sliderKey));
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));
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 Semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: CupertinoSlider(
value: 0.5,
onChanged: (double v) { },
),
));
expect(semantics, hasSemantics(
TestSemantics.root(
children: <TestSemantics>[
TestSemantics.rootChild(
id: 1,
value: '50%',
increasedValue: '60%',
decreasedValue: '40%',
textDirection: TextDirection.ltr,
actions: SemanticsAction.decrease.index | SemanticsAction.increase.index,
),
]
),
ignoreRect: true,
ignoreTransform: true,
));
// Disable slider
await tester.pumpWidget(const Directionality(
textDirection: TextDirection.ltr,
child: CupertinoSlider(
value: 0.5,
onChanged: null,
),
));
expect(semantics, hasSemantics(
TestSemantics.root(),
ignoreRect: true,
ignoreTransform: true,
));
semantics.dispose();
});
testWidgets('Slider Semantics can be updated', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
double value = 0.5;
await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: CupertinoSlider(
value: value,
onChanged: (double v) { },
),
));
expect(tester.getSemantics(find.byType(CupertinoSlider)), matchesSemantics(
hasIncreaseAction: true,
hasDecreaseAction: true,
value: '50%',
increasedValue: '60%',
decreasedValue: '40%',
textDirection: TextDirection.ltr,
));
value = 0.6;
await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: CupertinoSlider(
value: value,
onChanged: (double v) { },
),
));
expect(tester.getSemantics(find.byType(CupertinoSlider)), matchesSemantics(
hasIncreaseAction: true,
hasDecreaseAction: true,
value: '60%',
increasedValue: '70%',
decreasedValue: '50%',
textDirection: TextDirection.ltr,
));
handle.dispose();
});
testWidgets('Slider respects themes', (WidgetTester tester) async {
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoSlider(
onChanged: (double value) { },
value: 0.5,
),
),
),
);
expect(
find.byType(CupertinoSlider),
// First line it paints is blue.
paints..rrect(color: CupertinoColors.activeBlue),
);
await tester.pumpWidget(
CupertinoApp(
theme: const CupertinoThemeData(brightness: Brightness.dark),
home: Center(
child: CupertinoSlider(
onChanged: (double value) { },
value: 0.5,
),
),
),
);
expect(
find.byType(CupertinoSlider),
paints..rrect(color: CupertinoColors.activeOrange),
);
});
testWidgets('Themes can be overridden', (WidgetTester tester) async {
await tester.pumpWidget(
CupertinoApp(
theme: const CupertinoThemeData(brightness: Brightness.dark),
home: Center(
child: CupertinoSlider(
activeColor: CupertinoColors.activeGreen,
onChanged: (double value) { },
value: 0.5,
),
),
),
);
expect(
find.byType(CupertinoSlider),
paints..rrect(color: CupertinoColors.activeGreen),
);
});
}