Test Material buttons against a11y contrast guidelines (#32050)
This commit is contained in:
parent
1349be95a3
commit
73957b94e7
@ -9,7 +9,7 @@ import 'package:flutter/rendering.dart';
|
|||||||
import '../rendering/mock_canvas.dart';
|
import '../rendering/mock_canvas.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('FlatButton implements debugFillDescription', (WidgetTester tester) async {
|
testWidgets('FlatButton implements debugFillProperties', (WidgetTester tester) async {
|
||||||
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
||||||
FlatButton(
|
FlatButton(
|
||||||
onPressed: () { },
|
onPressed: () { },
|
||||||
@ -32,6 +32,33 @@ void main() {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Default FlatButton meets a11y contrast guidelines', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: FlatButton(
|
||||||
|
child: const Text('FlatButton'),
|
||||||
|
onPressed: () { },
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Default, not disabled.
|
||||||
|
await expectLater(tester, meetsGuideline(textContrastGuideline));
|
||||||
|
|
||||||
|
// Highlighted (pressed).
|
||||||
|
final Offset center = tester.getCenter(find.byType(FlatButton));
|
||||||
|
await tester.startGesture(center);
|
||||||
|
await tester.pump(); // Start the splash and highlight animations.
|
||||||
|
await tester.pump(const Duration(milliseconds: 800)); // Wait for splash and highlight to be well under way.
|
||||||
|
await expectLater(tester, meetsGuideline(textContrastGuideline));
|
||||||
|
},
|
||||||
|
semanticsEnabled: true,
|
||||||
|
);
|
||||||
|
|
||||||
testWidgets('FlatButton has no clip by default', (WidgetTester tester) async {
|
testWidgets('FlatButton has no clip by default', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
|
37
packages/flutter/test/material/material_button_test.dart
Normal file
37
packages/flutter/test/material/material_button_test.dart
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright 2019 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/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('Default MaterialButton meets a11y contrast guidelines', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: MaterialButton(
|
||||||
|
child: const Text('MaterialButton'),
|
||||||
|
onPressed: () { },
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Default, not disabled.
|
||||||
|
await expectLater(tester, meetsGuideline(textContrastGuideline));
|
||||||
|
|
||||||
|
// Highlighted (pressed).
|
||||||
|
final Offset center = tester.getCenter(find.byType(MaterialButton));
|
||||||
|
await tester.startGesture(center);
|
||||||
|
await tester.pump(); // Start the splash and highlight animations.
|
||||||
|
await tester.pump(const Duration(milliseconds: 800)); // Wait for splash and highlight to be well under way.
|
||||||
|
await expectLater(tester, meetsGuideline(textContrastGuideline));
|
||||||
|
},
|
||||||
|
semanticsEnabled: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
@ -10,27 +10,58 @@ import '../rendering/mock_canvas.dart';
|
|||||||
import '../widgets/semantics_tester.dart';
|
import '../widgets/semantics_tester.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
PhysicalModelLayer findPhysicalLayer(Element element) {
|
testWidgets('OutlineButton implements debugFillProperties', (WidgetTester tester) async {
|
||||||
expect(element, isNotNull);
|
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
||||||
RenderObject object = element.renderObject;
|
OutlineButton(
|
||||||
while (object != null && object is! RenderRepaintBoundary && object is! RenderView) {
|
onPressed: () { },
|
||||||
object = object.parent;
|
textColor: const Color(0xFF00FF00),
|
||||||
}
|
disabledTextColor: const Color(0xFFFF0000),
|
||||||
expect(object.debugLayer, isNotNull);
|
color: const Color(0xFF000000),
|
||||||
expect(object.debugLayer.firstChild, isInstanceOf<PhysicalModelLayer>());
|
highlightColor: const Color(0xFF1565C0),
|
||||||
final PhysicalModelLayer layer = object.debugLayer.firstChild;
|
splashColor: const Color(0xFF9E9E9E),
|
||||||
return layer.firstChild is PhysicalModelLayer ? layer.firstChild : layer;
|
child: const Text('Hello'),
|
||||||
}
|
).debugFillProperties(builder);
|
||||||
|
|
||||||
|
final List<String> description = builder.properties
|
||||||
|
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
||||||
|
.map((DiagnosticsNode node) => node.toString()).toList();
|
||||||
|
|
||||||
|
expect(description, <String>[
|
||||||
|
'textColor: Color(0xff00ff00)',
|
||||||
|
'disabledTextColor: Color(0xffff0000)',
|
||||||
|
'color: Color(0xff000000)',
|
||||||
|
'highlightColor: Color(0xff1565c0)',
|
||||||
|
'splashColor: Color(0xff9e9e9e)',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Default OutlineButton meets a11y contrast guidelines', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: OutlineButton(
|
||||||
|
child: const Text('OutlineButton'),
|
||||||
|
onPressed: () { },
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Default, not disabled.
|
||||||
|
await expectLater(tester, meetsGuideline(textContrastGuideline));
|
||||||
|
|
||||||
|
// Highlighted (pressed).
|
||||||
|
final Offset center = tester.getCenter(find.byType(OutlineButton));
|
||||||
|
await tester.startGesture(center);
|
||||||
|
await tester.pump(); // Start the splash and highlight animations.
|
||||||
|
await tester.pump(const Duration(milliseconds: 800)); // Wait for splash and highlight to be well under way.
|
||||||
|
await expectLater(tester, meetsGuideline(textContrastGuideline));
|
||||||
|
},
|
||||||
|
semanticsEnabled: true,
|
||||||
|
);
|
||||||
|
|
||||||
void checkPhysicalLayer(Element element, Color expectedColor, {Path clipPath, Rect clipRect}) {
|
|
||||||
final PhysicalModelLayer expectedLayer = findPhysicalLayer(element);
|
|
||||||
expect(expectedLayer.elevation, 0.0);
|
|
||||||
expect(expectedLayer.color, expectedColor);
|
|
||||||
if (clipPath != null) {
|
|
||||||
expect(clipRect, isNotNull);
|
|
||||||
expect(expectedLayer.clipPath, coversSameAreaAs(clipPath, areaToCompare: clipRect.inflate(10.0)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
testWidgets('Outline button responds to tap when enabled', (WidgetTester tester) async {
|
testWidgets('Outline button responds to tap when enabled', (WidgetTester tester) async {
|
||||||
int pressedCount = 0;
|
int pressedCount = 0;
|
||||||
|
|
||||||
@ -136,7 +167,7 @@ void main() {
|
|||||||
outlineButton,
|
outlineButton,
|
||||||
paints
|
paints
|
||||||
..path(color: disabledBorderColor, strokeWidth: borderWidth));
|
..path(color: disabledBorderColor, strokeWidth: borderWidth));
|
||||||
checkPhysicalLayer(
|
_checkPhysicalLayer(
|
||||||
tester.element(outlineButton),
|
tester.element(outlineButton),
|
||||||
const Color(0),
|
const Color(0),
|
||||||
clipPath: clipPath,
|
clipPath: clipPath,
|
||||||
@ -158,7 +189,7 @@ void main() {
|
|||||||
paints
|
paints
|
||||||
..path(color: borderColor, strokeWidth: borderWidth));
|
..path(color: borderColor, strokeWidth: borderWidth));
|
||||||
// initially, the interior of the button is transparent
|
// initially, the interior of the button is transparent
|
||||||
checkPhysicalLayer(
|
_checkPhysicalLayer(
|
||||||
tester.element(outlineButton),
|
tester.element(outlineButton),
|
||||||
fillColor.withAlpha(0x00),
|
fillColor.withAlpha(0x00),
|
||||||
clipPath: clipPath,
|
clipPath: clipPath,
|
||||||
@ -175,7 +206,7 @@ void main() {
|
|||||||
outlineButton,
|
outlineButton,
|
||||||
paints
|
paints
|
||||||
..path(color: highlightedBorderColor, strokeWidth: borderWidth));
|
..path(color: highlightedBorderColor, strokeWidth: borderWidth));
|
||||||
checkPhysicalLayer(
|
_checkPhysicalLayer(
|
||||||
tester.element(outlineButton),
|
tester.element(outlineButton),
|
||||||
fillColor.withAlpha(0xFF),
|
fillColor.withAlpha(0xFF),
|
||||||
clipPath: clipPath,
|
clipPath: clipPath,
|
||||||
@ -189,7 +220,7 @@ void main() {
|
|||||||
outlineButton,
|
outlineButton,
|
||||||
paints
|
paints
|
||||||
..path(color: borderColor, strokeWidth: borderWidth));
|
..path(color: borderColor, strokeWidth: borderWidth));
|
||||||
checkPhysicalLayer(
|
_checkPhysicalLayer(
|
||||||
tester.element(outlineButton),
|
tester.element(outlineButton),
|
||||||
fillColor.withAlpha(0x00),
|
fillColor.withAlpha(0x00),
|
||||||
clipPath: clipPath,
|
clipPath: clipPath,
|
||||||
@ -331,31 +362,6 @@ void main() {
|
|||||||
expect(tester.getSize(find.byType(Text)).height, equals(42.0));
|
expect(tester.getSize(find.byType(Text)).height, equals(42.0));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('OutlineButton implements debugFillProperties', (WidgetTester tester) async {
|
|
||||||
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
|
||||||
OutlineButton(
|
|
||||||
onPressed: () { },
|
|
||||||
textColor: const Color(0xFF00FF00),
|
|
||||||
disabledTextColor: const Color(0xFFFF0000),
|
|
||||||
color: const Color(0xFF000000),
|
|
||||||
highlightColor: const Color(0xFF1565C0),
|
|
||||||
splashColor: const Color(0xFF9E9E9E),
|
|
||||||
child: const Text('Hello'),
|
|
||||||
).debugFillProperties(builder);
|
|
||||||
|
|
||||||
final List<String> description = builder.properties
|
|
||||||
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
|
||||||
.map((DiagnosticsNode node) => node.toString()).toList();
|
|
||||||
|
|
||||||
expect(description, <String>[
|
|
||||||
'textColor: Color(0xff00ff00)',
|
|
||||||
'disabledTextColor: Color(0xffff0000)',
|
|
||||||
'color: Color(0xff000000)',
|
|
||||||
'highlightColor: Color(0xff1565c0)',
|
|
||||||
'splashColor: Color(0xff9e9e9e)',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('OutlineButton pressed fillColor default', (WidgetTester tester) async {
|
testWidgets('OutlineButton pressed fillColor default', (WidgetTester tester) async {
|
||||||
Widget buildFrame(ThemeData theme) {
|
Widget buildFrame(ThemeData theme) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
@ -385,18 +391,18 @@ void main() {
|
|||||||
Color fillColor = Colors.grey[850];
|
Color fillColor = Colors.grey[850];
|
||||||
|
|
||||||
// Initially the interior of the button is transparent.
|
// Initially the interior of the button is transparent.
|
||||||
checkPhysicalLayer(buttonElement, fillColor.withAlpha(0x00));
|
_checkPhysicalLayer(buttonElement, fillColor.withAlpha(0x00));
|
||||||
|
|
||||||
// Tap-press gesture on the button triggers the fill animation.
|
// Tap-press gesture on the button triggers the fill animation.
|
||||||
TestGesture gesture = await tester.startGesture(center);
|
TestGesture gesture = await tester.startGesture(center);
|
||||||
await tester.pump(); // Start the button fill animation.
|
await tester.pump(); // Start the button fill animation.
|
||||||
await tester.pump(const Duration(milliseconds: 200)); // Animation is complete.
|
await tester.pump(const Duration(milliseconds: 200)); // Animation is complete.
|
||||||
checkPhysicalLayer(buttonElement, fillColor.withAlpha(0xFF));
|
_checkPhysicalLayer(buttonElement, fillColor.withAlpha(0xFF));
|
||||||
|
|
||||||
// Tap gesture completes, button returns to its initial configuration.
|
// Tap gesture completes, button returns to its initial configuration.
|
||||||
await gesture.up();
|
await gesture.up();
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
checkPhysicalLayer(buttonElement, fillColor.withAlpha(0x00));
|
_checkPhysicalLayer(buttonElement, fillColor.withAlpha(0x00));
|
||||||
|
|
||||||
await tester.pumpWidget(buildFrame(ThemeData.light()));
|
await tester.pumpWidget(buildFrame(ThemeData.light()));
|
||||||
await tester.pumpAndSettle(); // Finish the theme change animation.
|
await tester.pumpAndSettle(); // Finish the theme change animation.
|
||||||
@ -412,11 +418,33 @@ void main() {
|
|||||||
gesture = await tester.startGesture(center);
|
gesture = await tester.startGesture(center);
|
||||||
await tester.pump(); // Start the button fill animation.
|
await tester.pump(); // Start the button fill animation.
|
||||||
await tester.pump(const Duration(milliseconds: 200)); // Animation is complete.
|
await tester.pump(const Duration(milliseconds: 200)); // Animation is complete.
|
||||||
checkPhysicalLayer(buttonElement, fillColor.withAlpha(0xFF));
|
_checkPhysicalLayer(buttonElement, fillColor.withAlpha(0xFF));
|
||||||
|
|
||||||
// Tap gesture completes, button returns to its initial configuration.
|
// Tap gesture completes, button returns to its initial configuration.
|
||||||
await gesture.up();
|
await gesture.up();
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
checkPhysicalLayer(buttonElement, fillColor.withAlpha(0x00));
|
_checkPhysicalLayer(buttonElement, fillColor.withAlpha(0x00));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PhysicalModelLayer _findPhysicalLayer(Element element) {
|
||||||
|
expect(element, isNotNull);
|
||||||
|
RenderObject object = element.renderObject;
|
||||||
|
while (object != null && object is! RenderRepaintBoundary && object is! RenderView) {
|
||||||
|
object = object.parent;
|
||||||
|
}
|
||||||
|
expect(object.debugLayer, isNotNull);
|
||||||
|
expect(object.debugLayer.firstChild, isInstanceOf<PhysicalModelLayer>());
|
||||||
|
final PhysicalModelLayer layer = object.debugLayer.firstChild;
|
||||||
|
return layer.firstChild is PhysicalModelLayer ? layer.firstChild : layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _checkPhysicalLayer(Element element, Color expectedColor, { Path clipPath, Rect clipRect }) {
|
||||||
|
final PhysicalModelLayer expectedLayer = _findPhysicalLayer(element);
|
||||||
|
expect(expectedLayer.elevation, 0.0);
|
||||||
|
expect(expectedLayer.color, expectedColor);
|
||||||
|
if (clipPath != null) {
|
||||||
|
expect(clipRect, isNotNull);
|
||||||
|
expect(expectedLayer.clipPath, coversSameAreaAs(clipPath, areaToCompare: clipRect.inflate(10.0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
63
packages/flutter/test/material/raised_button_test.dart
Normal file
63
packages/flutter/test/material/raised_button_test.dart
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Copyright 2019 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/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('RaisedButton implements debugFillProperties', (WidgetTester tester) async {
|
||||||
|
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
||||||
|
RaisedButton(
|
||||||
|
onPressed: () { },
|
||||||
|
textColor: const Color(0xFF00FF00),
|
||||||
|
disabledTextColor: const Color(0xFFFF0000),
|
||||||
|
color: const Color(0xFF000000),
|
||||||
|
highlightColor: const Color(0xFF1565C0),
|
||||||
|
splashColor: const Color(0xFF9E9E9E),
|
||||||
|
child: const Text('Hello'),
|
||||||
|
).debugFillProperties(builder);
|
||||||
|
|
||||||
|
final List<String> description = builder.properties
|
||||||
|
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
||||||
|
.map((DiagnosticsNode node) => node.toString()).toList();
|
||||||
|
|
||||||
|
expect(description, <String>[
|
||||||
|
'textColor: Color(0xff00ff00)',
|
||||||
|
'disabledTextColor: Color(0xffff0000)',
|
||||||
|
'color: Color(0xff000000)',
|
||||||
|
'highlightColor: Color(0xff1565c0)',
|
||||||
|
'splashColor: Color(0xff9e9e9e)',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Default RaisedButton meets a11y contrast guidelines', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: RaisedButton(
|
||||||
|
child: const Text('RaisedButton'),
|
||||||
|
onPressed: () { },
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Default, not disabled.
|
||||||
|
await expectLater(tester, meetsGuideline(textContrastGuideline));
|
||||||
|
|
||||||
|
// Highlighted (pressed).
|
||||||
|
final Offset center = tester.getCenter(find.byType(RaisedButton));
|
||||||
|
await tester.startGesture(center);
|
||||||
|
await tester.pump(); // Start the splash and highlight animations.
|
||||||
|
await tester.pump(const Duration(milliseconds: 800)); // Wait for splash and highlight to be well under way.
|
||||||
|
await expectLater(tester, meetsGuideline(textContrastGuideline));
|
||||||
|
},
|
||||||
|
semanticsEnabled: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user