1
+ import 'dart:async' ;
1
2
import 'dart:convert' ;
2
3
import 'dart:io' ;
3
4
import 'dart:typed_data' ;
4
5
5
6
import 'package:mocktail/mocktail.dart' ;
6
7
8
+ /// Signature for a function that returns a `List<int>` for a given [Uri] .
9
+ typedef ImageResolver = List <int > Function (Uri uri);
10
+
7
11
/// {@template mocktail_image_network}
8
12
/// Utility method that allows you to execute a widget test when you pump a
9
13
/// widget that contains an `Image.network` node in its tree.
@@ -40,11 +44,19 @@ import 'package:mocktail/mocktail.dart';
40
44
/// }
41
45
/// ```
42
46
/// {@endtemplate}
43
- T mockNetworkImages <T >(T Function () body, {Uint8List ? imageBytes}) {
47
+ T mockNetworkImages <T >(
48
+ T Function () body, {
49
+ Uint8List ? imageBytes,
50
+ ImageResolver ? imageResolver,
51
+ }) {
52
+ assert (
53
+ imageBytes == null || imageResolver == null ,
54
+ 'One of imageBytes or imageResolver can be provided, but not both.' ,
55
+ );
44
56
return HttpOverrides .runZoned (
45
57
body,
46
58
createHttpClient: (_) => _createHttpClient (
47
- data : imageBytes ?? _transparentPixelPng ,
59
+ imageResolver ?? = _defaultImageResolver (imageBytes) ,
48
60
),
49
61
);
50
62
}
@@ -53,6 +65,7 @@ class _MockHttpClient extends Mock implements HttpClient {
53
65
_MockHttpClient () {
54
66
registerFallbackValue ((List <int > _) {});
55
67
registerFallbackValue (Uri ());
68
+ registerFallbackValue (const Stream <List <int >>.empty ());
56
69
}
57
70
}
58
71
@@ -62,15 +75,64 @@ class _MockHttpClientResponse extends Mock implements HttpClientResponse {}
62
75
63
76
class _MockHttpHeaders extends Mock implements HttpHeaders {}
64
77
65
- HttpClient _createHttpClient ({ required List < int > data} ) {
78
+ HttpClient _createHttpClient (ImageResolver imageResolver ) {
66
79
final client = _MockHttpClient ();
80
+
81
+ when (() => client.getUrl (any ())).thenAnswer (
82
+ (invokation) async => _createRequest (
83
+ invokation.positionalArguments.first as Uri ,
84
+ imageResolver,
85
+ ),
86
+ );
87
+ when (() => client.openUrl (any (), any ())).thenAnswer (
88
+ (invokation) async => _createRequest (
89
+ invokation.positionalArguments.last as Uri ,
90
+ imageResolver,
91
+ ),
92
+ );
93
+
94
+ return client;
95
+ }
96
+
97
+ HttpClientRequest _createRequest (Uri uri, ImageResolver imageResolver) {
67
98
final request = _MockHttpClientRequest ();
99
+ final headers = _MockHttpHeaders ();
100
+
101
+ when (() => request.headers).thenReturn (headers);
102
+ when (
103
+ () => request.addStream (any ()),
104
+ ).thenAnswer ((invocation) {
105
+ final stream = invocation.positionalArguments.first as Stream <List <int >>;
106
+ return stream.fold <List <int >>(
107
+ < int > [],
108
+ (previous, element) => previous..addAll (element),
109
+ );
110
+ });
111
+ when (
112
+ request.close,
113
+ ).thenAnswer ((_) async => _createResponse (uri, imageResolver));
114
+
115
+ return request;
116
+ }
117
+
118
+ HttpClientResponse _createResponse (Uri uri, ImageResolver imageResolver) {
68
119
final response = _MockHttpClientResponse ();
69
120
final headers = _MockHttpHeaders ();
70
- when (() => response.compressionState)
71
- .thenReturn (HttpClientResponseCompressionState .notCompressed);
72
- when (() => response.contentLength).thenReturn (_transparentPixelPng.length);
121
+ final data = imageResolver (uri);
122
+
123
+ when (() => response.headers).thenReturn (headers);
124
+ when (() => response.contentLength).thenReturn (data.length);
73
125
when (() => response.statusCode).thenReturn (HttpStatus .ok);
126
+ when (() => response.isRedirect).thenReturn (false );
127
+ when (() => response.redirects).thenReturn ([]);
128
+ when (() => response.persistentConnection).thenReturn (false );
129
+ when (() => response.reasonPhrase).thenReturn ('OK' );
130
+ when (
131
+ () => response.compressionState,
132
+ ).thenReturn (HttpClientResponseCompressionState .notCompressed);
133
+ when (
134
+ () => response.handleError (any (), test: any (named: 'test' )),
135
+ ).thenAnswer ((_) => Stream <List <int >>.value (data));
74
136
when (
75
137
() => response.listen (
76
138
any (),
@@ -80,17 +142,30 @@ HttpClient _createHttpClient({required List<int> data}) {
80
142
),
81
143
).thenAnswer ((invocation) {
82
144
final onData =
83
- invocation.positionalArguments[ 0 ] as void Function (List <int >);
145
+ invocation.positionalArguments.first as void Function (List <int >);
84
146
final onDone = invocation.namedArguments[#onDone] as void Function ()? ;
85
- return Stream <List <int >>.fromIterable (< List <int >> [data])
86
- .listen (onData, onDone: onDone);
147
+ return Stream <List <int >>.fromIterable (
148
+ < List <int >> [data],
149
+ ).listen (onData, onDone: onDone);
87
150
});
88
- when (() => request.headers).thenReturn (headers);
89
- when (request.close).thenAnswer ((_) async => response);
90
- when (() => client.getUrl (any ())).thenAnswer ((_) async => request);
91
- return client;
151
+ return response;
92
152
}
93
153
154
+ ImageResolver _defaultImageResolver (Uint8List ? imageBytes) {
155
+ if (imageBytes != null ) return (_) => imageBytes;
156
+
157
+ return (uri) {
158
+ final extension = uri.path.split ('.' ).last;
159
+ return _mockedResponses[extension ] ?? _transparentPixelPng;
160
+ };
161
+ }
162
+
163
+ final _mockedResponses = < String , List <int >> {
164
+ 'png' : _transparentPixelPng,
165
+ 'svg' : _emptySvg,
166
+ };
167
+
168
+ final _emptySvg = '<svg viewBox="0 0 10 10" />' .codeUnits;
94
169
final _transparentPixelPng = base64Decode (
95
170
'''iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==''' ,
96
171
);
0 commit comments