|
| 1 | +import 'dart:async'; |
| 2 | +import 'dart:io'; |
| 3 | + |
1 | 4 | import 'package:checks/checks.dart';
|
2 | 5 | import 'package:flutter/material.dart';
|
3 | 6 | import 'package:flutter_test/flutter_test.dart';
|
| 7 | +import 'package:zulip/api/core.dart'; |
4 | 8 | import 'package:zulip/widgets/content.dart';
|
| 9 | +import 'package:zulip/widgets/store.dart'; |
| 10 | + |
| 11 | +import '../example_data.dart' as eg; |
| 12 | +import '../model/binding.dart'; |
5 | 13 |
|
6 | 14 | void main() {
|
7 |
| - testWidgets('Throws if no `PerAccountStoreWidget` ancestor', (WidgetTester tester) async { |
8 |
| - await tester.pumpWidget( |
9 |
| - const RealmContentNetworkImage('https://zulip.invalid/path/to/image.png', filterQuality: FilterQuality.medium)); |
10 |
| - check(tester.takeException()).isA<AssertionError>(); |
| 15 | + TestDataBinding.ensureInitialized(); |
| 16 | + |
| 17 | + group('RealmContentNetworkImage', () { |
| 18 | + final authHeaders = authHeader(email: eg.selfAccount.email, apiKey: eg.selfAccount.apiKey); |
| 19 | + |
| 20 | + Future<String?> actualAuthHeader(WidgetTester tester, String src) async { |
| 21 | + final globalStore = TestDataBinding.instance.globalStore; |
| 22 | + addTearDown(TestDataBinding.instance.reset); |
| 23 | + await globalStore.add(eg.selfAccount, eg.initialSnapshot); |
| 24 | + |
| 25 | + final httpClient = _FakeHttpClient(); |
| 26 | + debugNetworkImageHttpClientProvider = () => httpClient; |
| 27 | + httpClient.request.response |
| 28 | + ..statusCode = HttpStatus.ok |
| 29 | + ..content = kSolidBlueAvatar; |
| 30 | + |
| 31 | + await tester.pumpWidget(GlobalStoreWidget( |
| 32 | + child: PerAccountStoreWidget(accountId: eg.selfAccount.id, |
| 33 | + child: RealmContentNetworkImage(src)))); |
| 34 | + await tester.pump(); |
| 35 | + await tester.pump(); |
| 36 | + |
| 37 | + final headers = httpClient.request.headers.values; |
| 38 | + check(authHeaders.keys).deepEquals(['Authorization']); |
| 39 | + return headers['Authorization']?.single; |
| 40 | + } |
| 41 | + |
| 42 | + testWidgets('includes auth header if `src` on-realm', (tester) async { |
| 43 | + check(await actualAuthHeader(tester, 'https://chat.example/image.png')) |
| 44 | + .isNotNull().equals(authHeaders['Authorization']!); |
| 45 | + debugNetworkImageHttpClientProvider = null; |
| 46 | + }); |
| 47 | + |
| 48 | + testWidgets('excludes auth header if `src` off-realm', (tester) async { |
| 49 | + check(await actualAuthHeader(tester, 'https://other.example/image.png')) |
| 50 | + .isNull(); |
| 51 | + debugNetworkImageHttpClientProvider = null; |
| 52 | + }); |
| 53 | + |
| 54 | + testWidgets('throws if no `PerAccountStoreWidget` ancestor', (WidgetTester tester) async { |
| 55 | + await tester.pumpWidget( |
| 56 | + const RealmContentNetworkImage('https://zulip.invalid/path/to/image.png', filterQuality: FilterQuality.medium)); |
| 57 | + check(tester.takeException()).isA<AssertionError>(); |
| 58 | + }); |
11 | 59 | });
|
| 60 | +} |
| 61 | + |
| 62 | +class _FakeHttpClient extends Fake implements HttpClient { |
| 63 | + final _FakeHttpClientRequest request = _FakeHttpClientRequest(); |
| 64 | + |
| 65 | + @override |
| 66 | + Future<HttpClientRequest> getUrl(Uri url) async => request; |
| 67 | +} |
| 68 | + |
| 69 | +class _FakeHttpClientRequest extends Fake implements HttpClientRequest { |
| 70 | + final _FakeHttpClientResponse response = _FakeHttpClientResponse(); |
12 | 71 |
|
13 |
| - // TODO(#30): Simulate a `PerAccountStoreWidget` ancestor, to use in more tests: |
14 |
| - // TODO: 'Includes auth header if `src` is on-realm' |
15 |
| - // TODO: 'Excludes auth header if `src` is off-realm' |
| 72 | + @override |
| 73 | + final _FakeHttpHeaders headers = _FakeHttpHeaders(); |
| 74 | + |
| 75 | + @override |
| 76 | + Future<HttpClientResponse> close() async => response; |
16 | 77 | }
|
| 78 | + |
| 79 | +class _FakeHttpHeaders extends Fake implements HttpHeaders { |
| 80 | + final Map<String, List<String>> values = {}; |
| 81 | + |
| 82 | + @override |
| 83 | + void add(String name, Object value, {bool preserveHeaderCase = false}) { |
| 84 | + (values[name] ??= []).add(value.toString()); |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +class _FakeHttpClientResponse extends Fake implements HttpClientResponse { |
| 89 | + @override |
| 90 | + int statusCode = HttpStatus.ok; |
| 91 | + |
| 92 | + late List<int> content; |
| 93 | + |
| 94 | + @override |
| 95 | + int get contentLength => content.length; |
| 96 | + |
| 97 | + @override |
| 98 | + HttpClientResponseCompressionState get compressionState => HttpClientResponseCompressionState.notCompressed; |
| 99 | + |
| 100 | + @override |
| 101 | + StreamSubscription<List<int>> listen(void Function(List<int> event)? onData, {Function? onError, void Function()? onDone, bool? cancelOnError}) { |
| 102 | + return Stream.value(content).listen( |
| 103 | + onData, onDone: onDone, onError: onError, cancelOnError: cancelOnError); |
| 104 | + } |
| 105 | +} |
| 106 | + |
| 107 | +/// A 100x100 PNG image of solid Zulip blue, [kZulipBrandColor]. |
| 108 | +// Made from the following SVG: |
| 109 | +// <svg xmlns="http://www.w3.org/2000/svg" width="1" height="1" viewBox="0 0 1 1"> |
| 110 | +// <rect style="fill:#6492fe;fill-opacity:1" width="1" height="1" x="0" y="0" /> |
| 111 | +// </svg> |
| 112 | +// with `inkscape tmp.svg -w 100 --export-png=tmp1.png`, |
| 113 | +// `zopflipng tmp1.png tmp.png`, |
| 114 | +// and `xxd -i tmp.png`. |
| 115 | +const List<int> kSolidBlueAvatar = [ |
| 116 | + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, |
| 117 | + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x64, |
| 118 | + 0x01, 0x03, 0x00, 0x00, 0x00, 0x4a, 0x2c, 0x07, 0x17, 0x00, 0x00, 0x00, |
| 119 | + 0x03, 0x50, 0x4c, 0x54, 0x45, 0x64, 0x92, 0xfe, 0xf1, 0xd6, 0x69, 0xa5, |
| 120 | + 0x00, 0x00, 0x00, 0x13, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0x63, 0xa0, |
| 121 | + 0x2b, 0x18, 0x05, 0xa3, 0x60, 0x14, 0x8c, 0x82, 0x51, 0x00, 0x00, 0x05, |
| 122 | + 0x78, 0x00, 0x01, 0x1e, 0xcd, 0x28, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x49, |
| 123 | + 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, |
| 124 | +]; |
0 commit comments