Add unit tests for the Material widget (for clipping and elevation) (#14334)
This commit is contained in:
@@ -165,4 +165,167 @@ void main() {
|
||||
final RenderPhysicalModel modelE = getShadow(tester);
|
||||
expect(modelE.shadowColor, equals(const Color(0xFFFF0000)));
|
||||
});
|
||||
|
||||
group('Transparency clipping', () {
|
||||
testWidgets('clips to bounding rect by default', (WidgetTester tester) async {
|
||||
final GlobalKey materialKey = new GlobalKey();
|
||||
await tester.pumpWidget(
|
||||
new Material(
|
||||
key: materialKey,
|
||||
type: MaterialType.transparency,
|
||||
child: const SizedBox(width: 100.0, height: 100.0)
|
||||
)
|
||||
);
|
||||
|
||||
expect(find.byKey(materialKey), clipsWithBoundingRect);
|
||||
});
|
||||
|
||||
testWidgets('clips to rounded rect when borderRadius provided', (WidgetTester tester) async {
|
||||
final GlobalKey materialKey = new GlobalKey();
|
||||
await tester.pumpWidget(
|
||||
new Material(
|
||||
key: materialKey,
|
||||
type: MaterialType.transparency,
|
||||
borderRadius: const BorderRadius.all(const Radius.circular(10.0)),
|
||||
child: const SizedBox(width: 100.0, height: 100.0)
|
||||
)
|
||||
);
|
||||
|
||||
expect(
|
||||
find.byKey(materialKey),
|
||||
clipsWithBoundingRRect(
|
||||
borderRadius: const BorderRadius.all(const Radius.circular(10.0))
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('PhysicalModels', () {
|
||||
testWidgets('canvas', (WidgetTester tester) async {
|
||||
final GlobalKey materialKey = new GlobalKey();
|
||||
await tester.pumpWidget(
|
||||
new Material(
|
||||
key: materialKey,
|
||||
type: MaterialType.canvas,
|
||||
child: const SizedBox(width: 100.0, height: 100.0)
|
||||
)
|
||||
);
|
||||
|
||||
expect(find.byKey(materialKey), rendersOnPhysicalModel(
|
||||
shape: BoxShape.rectangle,
|
||||
borderRadius: BorderRadius.zero,
|
||||
elevation: 0.0,
|
||||
));
|
||||
});
|
||||
|
||||
testWidgets('canvas with borderRadius and elevation', (WidgetTester tester) async {
|
||||
final GlobalKey materialKey = new GlobalKey();
|
||||
await tester.pumpWidget(
|
||||
new Material(
|
||||
key: materialKey,
|
||||
type: MaterialType.canvas,
|
||||
borderRadius: const BorderRadius.all(const Radius.circular(5.0)),
|
||||
child: const SizedBox(width: 100.0, height: 100.0),
|
||||
elevation: 1.0,
|
||||
)
|
||||
);
|
||||
|
||||
expect(find.byKey(materialKey), rendersOnPhysicalModel(
|
||||
shape: BoxShape.rectangle,
|
||||
borderRadius: const BorderRadius.all(const Radius.circular(5.0)),
|
||||
elevation: 1.0,
|
||||
));
|
||||
});
|
||||
|
||||
testWidgets('card', (WidgetTester tester) async {
|
||||
final GlobalKey materialKey = new GlobalKey();
|
||||
await tester.pumpWidget(
|
||||
new Material(
|
||||
key: materialKey,
|
||||
type: MaterialType.card,
|
||||
child: const SizedBox(width: 100.0, height: 100.0),
|
||||
)
|
||||
);
|
||||
|
||||
expect(find.byKey(materialKey), rendersOnPhysicalModel(
|
||||
shape: BoxShape.rectangle,
|
||||
borderRadius: const BorderRadius.all(const Radius.circular(2.0)),
|
||||
elevation: 0.0,
|
||||
));
|
||||
});
|
||||
|
||||
testWidgets('card with borderRadius and elevation', (WidgetTester tester) async {
|
||||
final GlobalKey materialKey = new GlobalKey();
|
||||
await tester.pumpWidget(
|
||||
new Material(
|
||||
key: materialKey,
|
||||
type: MaterialType.card,
|
||||
borderRadius: const BorderRadius.all(const Radius.circular(5.0)),
|
||||
elevation: 5.0,
|
||||
child: const SizedBox(width: 100.0, height: 100.0),
|
||||
)
|
||||
);
|
||||
|
||||
expect(find.byKey(materialKey), rendersOnPhysicalModel(
|
||||
shape: BoxShape.rectangle,
|
||||
borderRadius: const BorderRadius.all(const Radius.circular(5.0)),
|
||||
elevation: 5.0,
|
||||
));
|
||||
});
|
||||
|
||||
testWidgets('circle', (WidgetTester tester) async {
|
||||
final GlobalKey materialKey = new GlobalKey();
|
||||
await tester.pumpWidget(
|
||||
new Material(
|
||||
key: materialKey,
|
||||
type: MaterialType.circle,
|
||||
child: const SizedBox(width: 100.0, height: 100.0),
|
||||
color: const Color(0xFF0000FF),
|
||||
)
|
||||
);
|
||||
|
||||
expect(find.byKey(materialKey), rendersOnPhysicalModel(
|
||||
shape: BoxShape.circle,
|
||||
elevation: 0.0,
|
||||
));
|
||||
});
|
||||
|
||||
testWidgets('button', (WidgetTester tester) async {
|
||||
final GlobalKey materialKey = new GlobalKey();
|
||||
await tester.pumpWidget(
|
||||
new Material(
|
||||
key: materialKey,
|
||||
type: MaterialType.button,
|
||||
child: const SizedBox(width: 100.0, height: 100.0),
|
||||
color: const Color(0xFF0000FF),
|
||||
)
|
||||
);
|
||||
|
||||
expect(find.byKey(materialKey), rendersOnPhysicalModel(
|
||||
shape: BoxShape.rectangle,
|
||||
borderRadius: const BorderRadius.all(const Radius.circular(2.0)),
|
||||
elevation: 0.0,
|
||||
));
|
||||
});
|
||||
|
||||
testWidgets('button with elevation and borderRadius', (WidgetTester tester) async {
|
||||
final GlobalKey materialKey = new GlobalKey();
|
||||
await tester.pumpWidget(
|
||||
new Material(
|
||||
key: materialKey,
|
||||
type: MaterialType.button,
|
||||
child: const SizedBox(width: 100.0, height: 100.0),
|
||||
color: const Color(0xFF0000FF),
|
||||
borderRadius: const BorderRadius.all(const Radius.circular(6.0)),
|
||||
elevation: 4.0,
|
||||
)
|
||||
);
|
||||
|
||||
expect(find.byKey(materialKey), rendersOnPhysicalModel(
|
||||
shape: BoxShape.rectangle,
|
||||
borderRadius: const BorderRadius.all(const Radius.circular(6.0)),
|
||||
elevation: 4.0,
|
||||
));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:test/test.dart';
|
||||
@@ -780,3 +781,142 @@ class _IsMethodCall extends Matcher {
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts that a [Finder] locates a single object whose root RenderObject
|
||||
/// is a [RenderClipRect] with no clipper set.
|
||||
const Matcher clipsWithBoundingRect = const _ClipsWithBoundingRect();
|
||||
|
||||
/// Asserts that a [Finder] locates a single object whose root RenderObject
|
||||
/// is a [RenderClipRRect] with no clipper set, and border radius equals to
|
||||
/// [borderRadius].
|
||||
Matcher clipsWithBoundingRRect({@required BorderRadius borderRadius}) {
|
||||
return new _ClipsWithBoundingRRect(borderRadius: borderRadius);
|
||||
}
|
||||
|
||||
/// Asserts that a [Finder] locates a single object whose root RenderObject
|
||||
/// is a [RenderPhysicalModel].
|
||||
///
|
||||
/// - If [shape] is non null asserts that [RenderPhysicalModel.shape] is equal to
|
||||
/// [shape].
|
||||
/// - If [borderRadius] is non null asserts that [RenderPhysicalModel.borderRadius] is equal to
|
||||
/// [borderRadius].
|
||||
/// - If [elevation] is non null asserts that [RenderPhysicalModel.elevation] is equal to
|
||||
/// [elevation].
|
||||
Matcher rendersOnPhysicalModel({
|
||||
BoxShape shape,
|
||||
BorderRadius borderRadius,
|
||||
double elevation,
|
||||
}) {
|
||||
return new _RendersOnPhysicalModel(
|
||||
shape: shape,
|
||||
borderRadius: borderRadius,
|
||||
elevation: elevation,
|
||||
);
|
||||
}
|
||||
|
||||
abstract class _MatchRenderObject<T extends RenderObject> extends Matcher {
|
||||
const _MatchRenderObject();
|
||||
|
||||
bool renderObjectMatches(Map<dynamic, dynamic> matchState, T renderObject);
|
||||
|
||||
@override
|
||||
bool matches(covariant Finder finder, Map<dynamic, dynamic> matchState) {
|
||||
final Iterable<Element> nodes = finder.evaluate();
|
||||
if (nodes.length != 1)
|
||||
return failWithDescription(matchState, 'did not have a exactly one child element');
|
||||
final RenderObject renderObject = nodes.single.renderObject;
|
||||
if (renderObject.runtimeType != T)
|
||||
return failWithDescription(matchState, 'had a root render object of type: ${renderObject.runtimeType}');
|
||||
|
||||
return renderObjectMatches(matchState, renderObject);
|
||||
}
|
||||
|
||||
bool failWithDescription(Map<dynamic, dynamic> matchState, String description) {
|
||||
matchState['failure'] = description;
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
Description describeMismatch(
|
||||
dynamic item,
|
||||
Description mismatchDescription,
|
||||
Map<dynamic, dynamic> matchState,
|
||||
bool verbose
|
||||
) {
|
||||
return mismatchDescription.add(matchState['failure']);
|
||||
}
|
||||
}
|
||||
|
||||
class _RendersOnPhysicalModel extends _MatchRenderObject<RenderPhysicalModel> {
|
||||
const _RendersOnPhysicalModel({
|
||||
this.shape,
|
||||
this.borderRadius,
|
||||
this.elevation,
|
||||
});
|
||||
|
||||
final BoxShape shape;
|
||||
final BorderRadius borderRadius;
|
||||
final double elevation;
|
||||
|
||||
@override
|
||||
bool renderObjectMatches(Map<dynamic, dynamic> matchState, RenderPhysicalModel renderObject) {
|
||||
if (shape != null && renderObject.shape != shape)
|
||||
return failWithDescription(matchState, 'had shape: ${renderObject.shape}');
|
||||
|
||||
if (borderRadius != null && renderObject.borderRadius != borderRadius)
|
||||
return failWithDescription(matchState, 'had borderRadius: ${renderObject.borderRadius}');
|
||||
|
||||
if (elevation != null && renderObject.elevation != elevation)
|
||||
return failWithDescription(matchState, 'had elevation: ${renderObject.elevation}');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Description describe(Description description) {
|
||||
description.add('renders on a physical model');
|
||||
if (shape != null)
|
||||
description.add(' with shape $shape');
|
||||
if (borderRadius != null)
|
||||
description.add(' with borderRadius $borderRadius');
|
||||
if (elevation != null)
|
||||
description.add(' with elevation $elevation');
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
class _ClipsWithBoundingRect extends _MatchRenderObject<RenderClipRect> {
|
||||
const _ClipsWithBoundingRect();
|
||||
|
||||
@override
|
||||
bool renderObjectMatches(Map<dynamic, dynamic> matchState, RenderClipRect renderObject) {
|
||||
if (renderObject.clipper != null)
|
||||
return failWithDescription(matchState, 'had a non null clipper ${renderObject.clipper}');
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Description describe(Description description) =>
|
||||
description.add('clips with bounding rectangle');
|
||||
}
|
||||
|
||||
class _ClipsWithBoundingRRect extends _MatchRenderObject<RenderClipRRect> {
|
||||
const _ClipsWithBoundingRRect({@required this.borderRadius});
|
||||
|
||||
final BorderRadius borderRadius;
|
||||
|
||||
|
||||
@override
|
||||
bool renderObjectMatches(Map<dynamic, dynamic> matchState, RenderClipRRect renderObject) {
|
||||
if (renderObject.clipper != null)
|
||||
return failWithDescription(matchState, 'had a non null clipper ${renderObject.clipper}');
|
||||
|
||||
if (renderObject.borderRadius != borderRadius)
|
||||
return failWithDescription(matchState, 'had borderRadius: ${renderObject.borderRadius}');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Description describe(Description description) =>
|
||||
description.add('clips with bounding rounded rectangle with borderRadius: $borderRadius');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user