Skip to content

Commit 95a9b97

Browse files
Add test for dynamic_content_color.0.dart (flutter#158309)
Fixes flutter#130459 It adds a test for - `examples/api/lib/material/color_scheme/dynamic_content_color.0.dart`
1 parent 3770a10 commit 95a9b97

File tree

3 files changed

+182
-51
lines changed

3 files changed

+182
-51
lines changed

dev/bots/check_code_samples.dart

Lines changed: 5 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -142,29 +142,18 @@ class SampleChecker {
142142
// Get a list of the filenames that were not found in the source files.
143143
final List<String> missingFilenames = checkForMissingLinks(exampleFilenames, exampleLinks);
144144

145-
// Get a list of any tests that are missing, as well as any that used to be
146-
// missing, but have been implemented.
147-
final (List<File> missingTests, List<File> noLongerMissing) = checkForMissingTests(exampleFilenames);
145+
// Get a list of any tests that are missing.
146+
final List<File> missingTests = checkForMissingTests(exampleFilenames);
148147

149148
// Remove any that we know are exceptions (examples that aren't expected to be
150149
// linked into any source files). These are typically template files used to
151150
// generate new examples.
152151
missingFilenames.removeWhere((String file) => _knownUnlinkedExamples.contains(file));
153152

154-
if (missingFilenames.isEmpty && missingTests.isEmpty && noLongerMissing.isEmpty && malformedLinks.isEmpty) {
153+
if (missingFilenames.isEmpty && missingTests.isEmpty && malformedLinks.isEmpty) {
155154
return true;
156155
}
157156

158-
if (noLongerMissing.isNotEmpty) {
159-
final StringBuffer buffer = StringBuffer('The following tests have been implemented! Huzzah!:\n');
160-
for (final File name in noLongerMissing) {
161-
buffer.writeln(' ${getRelativePath(name)}');
162-
}
163-
buffer.writeln('However, they now need to be removed from the _knownMissingTests');
164-
buffer.write('list in the script $_scriptLocation.');
165-
foundError(buffer.toString().split('\n'));
166-
}
167-
168157
if (missingTests.isNotEmpty) {
169158
final StringBuffer buffer = StringBuffer('The following example test files are missing:\n');
170159
for (final File name in missingTests) {
@@ -279,35 +268,14 @@ class SampleChecker {
279268
return '${path.join(testPath, path.basenameWithoutExtension(example.path))}_test.dart';
280269
}
281270

282-
(List<File>, List<File>) checkForMissingTests(List<File> exampleFilenames) {
271+
List<File> checkForMissingTests(List<File> exampleFilenames) {
283272
final List<File> missingTests = <File>[];
284-
final List<File> noLongerMissingTests = <File>[];
285273
for (final File example in exampleFilenames) {
286274
final File testFile = filesystem.file(getTestNameForExample(example, examples));
287-
final String name = path.relative(testFile.absolute.path, from: flutterRoot.absolute.path);
288275
if (!testFile.existsSync()) {
289276
missingTests.add(testFile);
290-
} else if (_knownMissingTests.contains(name.replaceAll(r'\', '/'))) {
291-
noLongerMissingTests.add(testFile);
292277
}
293278
}
294-
// Skip any that we know are missing.
295-
missingTests.removeWhere(
296-
(File test) {
297-
final String name = path.relative(test.absolute.path, from: flutterRoot.absolute.path).replaceAll(r'\', '/');
298-
return _knownMissingTests.contains(name);
299-
},
300-
);
301-
return (missingTests, noLongerMissingTests);
279+
return missingTests;
302280
}
303281
}
304-
305-
// These tests are known to be missing. They should all eventually be
306-
// implemented, but until they are we allow them, so that we can catch any new
307-
// examples that are added without tests.
308-
//
309-
// TODO(gspencergoog): implement the missing tests.
310-
// See https://github.com/flutter/flutter/issues/130459
311-
final Set<String> _knownMissingTests = <String>{
312-
'examples/api/test/material/color_scheme/dynamic_content_color.0_test.dart',
313-
};

examples/api/lib/material/color_scheme/dynamic_content_color.0.dart

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,23 @@ import 'package:flutter/material.dart';
99
const Widget divider = SizedBox(height: 10);
1010
const double narrowScreenWidthThreshold = 400;
1111

12-
void main() => runApp(DynamicColorExample());
12+
void main() => runApp(const DynamicColorExample());
1313

1414
class DynamicColorExample extends StatefulWidget {
15-
DynamicColorExample({super.key});
15+
const DynamicColorExample({super.key});
1616

17-
final List<ImageProvider> images = <NetworkImage>[
18-
const NetworkImage(
17+
static const List<ImageProvider> images = <NetworkImage>[
18+
NetworkImage(
1919
'https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_1.png'),
20-
const NetworkImage(
20+
NetworkImage(
2121
'https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_2.png'),
22-
const NetworkImage(
22+
NetworkImage(
2323
'https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_3.png'),
24-
const NetworkImage(
24+
NetworkImage(
2525
'https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_4.png'),
26-
const NetworkImage(
26+
NetworkImage(
2727
'https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_5.png'),
28-
const NetworkImage(
28+
NetworkImage(
2929
'https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_6.png'),
3030
];
3131

@@ -48,7 +48,7 @@ class _DynamicColorExampleState extends State<DynamicColorExample> {
4848
isLoading = true;
4949
currentColorScheme = const ColorScheme.light();
5050
WidgetsBinding.instance.addPostFrameCallback((_) {
51-
_updateImage(widget.images[selectedImage]);
51+
_updateImage(DynamicColorExample.images[selectedImage]);
5252
isLoading = false;
5353
});
5454
}
@@ -105,7 +105,7 @@ class _DynamicColorExampleState extends State<DynamicColorExample> {
105105
onChanged: (bool value) {
106106
setState(() {
107107
isLight = value;
108-
_updateImage(widget.images[selectedImage]);
108+
_updateImage(DynamicColorExample.images[selectedImage]);
109109
});
110110
})
111111
],
@@ -120,7 +120,7 @@ class _DynamicColorExampleState extends State<DynamicColorExample> {
120120
divider,
121121
_imagesRow(
122122
context,
123-
widget.images,
123+
DynamicColorExample.images,
124124
colorScheme,
125125
),
126126
divider,
@@ -192,7 +192,7 @@ class _DynamicColorExampleState extends State<DynamicColorExample> {
192192
return;
193193
}
194194
setState(() {
195-
selectedImage = widget.images.indexOf(provider);
195+
selectedImage = DynamicColorExample.images.indexOf(provider);
196196
currentColorScheme = newColorScheme;
197197
});
198198
}
@@ -227,7 +227,7 @@ class _DynamicColorExampleState extends State<DynamicColorExample> {
227227
child: GestureDetector(
228228
onTap: () => _updateImage(image),
229229
child: Card(
230-
color: widget.images.indexOf(image) == selectedImage
230+
color: DynamicColorExample.images.indexOf(image) == selectedImage
231231
? colorScheme.primaryContainer
232232
: colorScheme.surface,
233233
child: Padding(
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:async';
6+
import 'dart:io';
7+
8+
import 'package:flutter/material.dart';
9+
import 'package:flutter/services.dart';
10+
import 'package:flutter_api_samples/material/color_scheme/dynamic_content_color.0.dart' as example;
11+
import 'package:flutter_test/flutter_test.dart';
12+
13+
void main() {
14+
15+
testWidgets('The theme colors are created dynamically from the first image', (WidgetTester tester) async {
16+
await HttpOverrides.runZoned(
17+
() async {
18+
await tester.pumpWidget(
19+
const example.DynamicColorExample(),
20+
);
21+
22+
expect(
23+
find.widgetWithText(AppBar, 'Content Based Dynamic Color'),
24+
findsOne,
25+
);
26+
expect(find.byType(Switch), findsOne);
27+
expect(find.byIcon(Icons.light_mode), findsOne);
28+
29+
// Loads the images.
30+
// Using runAsync forces the streams to complete. This is needed because
31+
// loading the fake image is a real async task.
32+
await tester.pump();
33+
await tester.runAsync(() => Future<void>.delayed(Duration.zero));
34+
await tester.runAsync(() => Future<void>.delayed(Duration.zero));
35+
await tester.runAsync(() => Future<void>.delayed(Duration.zero));
36+
await tester.runAsync(() => Future<void>.delayed(Duration.zero));
37+
await tester.runAsync(() => Future<void>.delayed(Duration.zero));
38+
await tester.pump();
39+
await tester.pump(kThemeChangeDuration);
40+
41+
expect(find.byType(Image), findsExactly(6));
42+
43+
expect(find.text('Light ColorScheme'), findsOne);
44+
expect(find.text('Dark ColorScheme'), findsOne);
45+
expect(find.text('primary'), findsExactly(2));
46+
expect(find.text('onPrimary'), findsExactly(2));
47+
expect(find.text('primaryContainer'), findsExactly(2));
48+
expect(find.text('onPrimaryContainer'), findsExactly(2));
49+
expect(find.text('secondary'), findsExactly(2));
50+
expect(find.text('onSecondary'), findsExactly(2));
51+
expect(find.text('secondaryContainer'), findsExactly(2));
52+
expect(find.text('onSecondaryContainer'), findsExactly(2));
53+
expect(find.text('tertiary'), findsExactly(2));
54+
expect(find.text('onTertiary'), findsExactly(2));
55+
expect(find.text('tertiaryContainer'), findsExactly(2));
56+
expect(find.text('onTertiaryContainer'), findsExactly(2));
57+
expect(find.text('error'), findsExactly(2));
58+
expect(find.text('onError'), findsExactly(2));
59+
expect(find.text('errorContainer'), findsExactly(2));
60+
expect(find.text('onErrorContainer'), findsExactly(2));
61+
expect(find.text('surface'), findsExactly(2));
62+
expect(find.text('onSurface'), findsExactly(2));
63+
expect(find.text('onSurfaceVariant'), findsExactly(2));
64+
expect(find.text('outline'), findsExactly(2));
65+
expect(find.text('shadow'), findsExactly(2));
66+
expect(find.text('inverseSurface'), findsExactly(2));
67+
expect(find.text('onInverseSurface'), findsExactly(2));
68+
expect(find.text('inversePrimary'), findsExactly(2));
69+
70+
ThemeData themeData = Theme.of(
71+
tester.element(find.byType(Scaffold)),
72+
);
73+
74+
expect(themeData.colorScheme.primary, const Color(0xff575992));
75+
expect(themeData.colorScheme.secondary, const Color(0xff5d5c72));
76+
77+
await tester.tap(find.byType(Switch));
78+
await tester.pump();
79+
80+
// Loads the images.
81+
// Using runAsync forces the streams to complete. This is needed because
82+
// loading the fake image is a real async task.
83+
await tester.runAsync(() => Future<void>.delayed(Duration.zero));
84+
await tester.runAsync(() => Future<void>.delayed(Duration.zero));
85+
await tester.pump();
86+
await tester.pump(kThemeChangeDuration);
87+
88+
themeData = Theme.of(tester.element(find.byType(Scaffold)));
89+
90+
expect(themeData.primaryColor, const Color(0xff131318));
91+
expect(themeData.colorScheme.secondary, const Color(0xffc6c4dd));
92+
},
93+
createHttpClient: (SecurityContext? securityContext) => _FakeClient(),
94+
);
95+
});
96+
}
97+
98+
class _FakeClient extends Fake implements HttpClient {
99+
@override
100+
Future<HttpClientRequest> getUrl(Uri url) async {
101+
return _FakeHttpClientRequest();
102+
}
103+
104+
@override
105+
bool autoUncompress = true;
106+
}
107+
108+
class _FakeHttpClientRequest extends Fake implements HttpClientRequest {
109+
@override
110+
HttpHeaders get headers => _FakeHttpHeaders();
111+
112+
@override
113+
Future<HttpClientResponse> close() async {
114+
return _FakeHttpClientResponse();
115+
}
116+
}
117+
118+
class _FakeHttpHeaders extends Fake implements HttpHeaders {}
119+
120+
class _FakeHttpClientResponse extends Fake implements HttpClientResponse {
121+
@override
122+
HttpClientResponseCompressionState get compressionState => HttpClientResponseCompressionState.notCompressed;
123+
124+
@override
125+
int get contentLength => _blueSquarePng.length;
126+
127+
@override
128+
int get statusCode => 200;
129+
130+
@override
131+
Future<E> drain<E>([E? futureValue]) {
132+
return Future<E>.value(futureValue as E);
133+
}
134+
135+
@override
136+
StreamSubscription<List<int>> listen(
137+
void Function(List<int> event)? onData, {
138+
Function? onError,
139+
void Function()? onDone,
140+
bool? cancelOnError,
141+
}) {
142+
return Stream<List<int>>.value(_blueSquarePng).listen(
143+
onData,
144+
onDone: onDone,
145+
onError: onError,
146+
cancelOnError: cancelOnError,
147+
);
148+
}
149+
}
150+
151+
/// A 50x50 blue square png.
152+
final Uint8List _blueSquarePng = Uint8List.fromList(const <int>[
153+
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49,
154+
0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x08, 0x06,
155+
0x00, 0x00, 0x00, 0x1e, 0x3f, 0x88, 0xb1, 0x00, 0x00, 0x00, 0x48, 0x49, 0x44,
156+
0x41, 0x54, 0x78, 0xda, 0xed, 0xcf, 0x31, 0x0d, 0x00, 0x30, 0x08, 0x00, 0xb0,
157+
0x61, 0x63, 0x2f, 0xfe, 0x2d, 0x61, 0x05, 0x34, 0xf0, 0x92, 0xd6, 0x41, 0x23,
158+
0x7f, 0xf5, 0x3b, 0x20, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
159+
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
160+
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
161+
0x44, 0x44, 0x44, 0x36, 0x06, 0x03, 0x6e, 0x69, 0x47, 0x12, 0x8e, 0xea, 0xaa,
162+
0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
163+
]);

0 commit comments

Comments
 (0)