import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'dart:ui' show window; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:mockito/mockito.dart'; Future _checkWidgetAndGolden(Key key, String filename) async { final Finder widgetFinder = find.byKey(key); expect(widgetFinder, findsOneWidget); await expectLater(widgetFinder, matchesGoldenFile('golden_widget/$filename')); } void main() { const String svgStr = ''' '''; const String stickFigureSvgStr = ''' svg/stick_figure Created with Sketch. '''; final Uint8List svgBytes = utf8.encode(svgStr) as Uint8List; testWidgets('SvgPicture can work with a FittedBox', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); await tester.pumpWidget( MediaQuery( data: const MediaQueryData(size: Size(100, 100)), child: Row( key: key, textDirection: TextDirection.ltr, children: [ Flexible( child: FittedBox( fit: BoxFit.fitWidth, child: SvgPicture.string( svgStr, width: 20.0, height: 14.0, ), ), ), ], ), ), ); await tester.pumpAndSettle(); final Finder widgetFinder = find.byKey(key); expect(widgetFinder, findsOneWidget); }); testWidgets('SvgPicture.string', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); await tester.pumpWidget( MediaQuery( data: MediaQueryData.fromWindow(window), child: RepaintBoundary( key: key, child: SvgPicture.string( svgStr, width: 100.0, height: 100.0, ), ), ), ); await tester.pumpAndSettle(); await _checkWidgetAndGolden(key, 'flutter_logo.string.png'); }); testWidgets('SvgPicture natural size', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); await tester.pumpWidget( MediaQuery( data: MediaQueryData.fromWindow(window), child: Center( key: key, child: SvgPicture.string( svgStr, ), ), ), ); await tester.pumpAndSettle(); await _checkWidgetAndGolden(key, 'flutter_logo.natural.png'); }); testWidgets('SvgPicture clipped', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); await tester.pumpWidget( MediaQuery( data: MediaQueryData.fromWindow(window), child: Center( key: key, child: SvgPicture.string( stickFigureSvgStr, ), ), ), ); await tester.pumpAndSettle(); await _checkWidgetAndGolden(key, 'stick_figure.withclipping.png'); }); testWidgets('SvgPicture.string rtl', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); await tester.pumpWidget( MediaQuery( data: MediaQueryData.fromWindow(window), child: RepaintBoundary( key: key, child: Directionality( textDirection: TextDirection.rtl, child: SvgPicture.string( svgStr, matchTextDirection: true, width: 100.0, height: 100.0, ), ), ), ), ); await tester.pumpAndSettle(); await _checkWidgetAndGolden(key, 'flutter_logo.string.rtl.png'); }); testWidgets('SvgPicture.memory', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); await tester.pumpWidget( MediaQuery( data: MediaQueryData.fromWindow(window), child: RepaintBoundary( key: key, child: SvgPicture.memory( svgBytes, ), ), ), ); await tester.pumpAndSettle(); await _checkWidgetAndGolden(key, 'flutter_logo.memory.png'); }); testWidgets('SvgPicture.asset', (WidgetTester tester) async { final MockAssetBundle mockAsset = MockAssetBundle(); when(mockAsset.loadString('test.svg')) .thenAnswer((_) => Future.value(svgStr)); final GlobalKey key = GlobalKey(); await tester.pumpWidget( MediaQuery( data: MediaQueryData.fromWindow(window), child: RepaintBoundary( key: key, child: SvgPicture.asset( 'test.svg', bundle: mockAsset, ), ), ), ); await tester.pumpAndSettle(); await _checkWidgetAndGolden(key, 'flutter_logo.asset.png'); }); testWidgets('SvgPicture.asset DefaultAssetBundle', (WidgetTester tester) async { final MockAssetBundle mockAsset = MockAssetBundle(); when(mockAsset.loadString('test.svg')) .thenAnswer((_) => Future.value(svgStr)); final GlobalKey key = GlobalKey(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: MediaQuery( data: MediaQueryData.fromWindow(window), child: DefaultAssetBundle( bundle: mockAsset, child: RepaintBoundary( key: key, child: SvgPicture.asset( 'test.svg', semanticsLabel: 'Test SVG', ), ), ), ), ), ); await tester.pumpAndSettle(); await _checkWidgetAndGolden(key, 'flutter_logo.asset.png'); }); final MockHttpClient mockHttpClient = MockHttpClient(); final MockHttpClientRequest mockRequest = MockHttpClientRequest(); final MockHttpClientResponse mockResponse = MockHttpClientResponse(); when(mockHttpClient.getUrl(any)) .thenAnswer((_) => Future.value(mockRequest)); when(mockRequest.close()) .thenAnswer((_) => Future.value(mockResponse)); when(mockResponse.transform(any)) .thenAnswer((_) => Stream.fromIterable([svgBytes])); when(mockResponse.listen(any, onDone: anyNamed('onDone'), onError: anyNamed('onError'), cancelOnError: anyNamed('cancelOnError'))) .thenAnswer((Invocation invocation) { final void Function(Uint8List) onData = invocation.positionalArguments[0] as void Function(Uint8List); final void Function(Object) onError = invocation.namedArguments[#onError] as void Function(Object); final VoidCallback onDone = invocation.namedArguments[#onDone] as VoidCallback; final bool cancelOnError = invocation.namedArguments[#cancelOnError] as bool; return Stream.fromIterable([svgBytes]).listen( onData, onDone: onDone, onError: onError, cancelOnError: cancelOnError, ); }); testWidgets('SvgPicture.network', (WidgetTester tester) async { await HttpOverrides.runZoned(() async { when(mockResponse.statusCode).thenReturn(200); final GlobalKey key = GlobalKey(); await tester.pumpWidget( MediaQuery( data: MediaQueryData.fromWindow(window), child: RepaintBoundary( key: key, child: SvgPicture.network( 'test.svg', ), ), ), ); await tester.pumpAndSettle(); await _checkWidgetAndGolden(key, 'flutter_logo.network.png'); }, createHttpClient: (SecurityContext c) => mockHttpClient); }); testWidgets('SvgPicture can be created without a MediaQuery', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); await tester.pumpWidget( RepaintBoundary( key: key, child: SvgPicture.string( svgStr, width: 100.0, height: 100.0, ), ), ); await tester.pumpAndSettle(); await _checkWidgetAndGolden(key, 'flutter_logo.string.png'); }); testWidgets('SvgPicture.network HTTP exception', (WidgetTester tester) async { await HttpOverrides.runZoned(() async { expect(() async { when(mockResponse.statusCode).thenReturn(400); await tester.pumpWidget( MediaQuery( data: MediaQueryData.fromWindow(window), child: SvgPicture.network( 'notFound.svg', ), ), ); }, isNotNull); }, createHttpClient: (SecurityContext c) => mockHttpClient); }); testWidgets('SvgPicture semantics', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: RepaintBoundary( child: SvgPicture.string( svgStr, semanticsLabel: 'Flutter Logo', width: 100.0, height: 100.0, ), ), ), ); await tester.pumpAndSettle(); expect(find.byType(Semantics), findsOneWidget); expect(find.bySemanticsLabel('Flutter Logo'), findsOneWidget); }, semanticsEnabled: true); testWidgets('SvgPicture semantics - no label', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: RepaintBoundary( child: SvgPicture.string( svgStr, width: 100.0, height: 100.0, ), ), ), ); await tester.pumpAndSettle(); expect(find.byType(Semantics), findsOneWidget); }, semanticsEnabled: true); testWidgets('SvgPicture semantics - exclude', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: RepaintBoundary( child: SvgPicture.string( svgStr, excludeFromSemantics: true, width: 100.0, height: 100.0, ), ), ), ); await tester.pumpAndSettle(); expect(find.byType(Semantics), findsNothing); }, semanticsEnabled: true); testWidgets('SvgPicture colorFilter - flutter logo', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); await tester.pumpWidget( RepaintBoundary( key: key, child: SvgPicture.string( svgStr, width: 100.0, height: 100.0, color: const Color(0xFF990000), ), ), ); await tester.pumpAndSettle(); await _checkWidgetAndGolden(key, 'flutter_logo.string.color_filter.png'); }); testWidgets('SvgPicture colorFilter with text', (WidgetTester tester) async { const String svgData = ''' 2 1 '''; final GlobalKey key = GlobalKey(); await tester.pumpWidget( RepaintBoundary( key: key, child: SvgPicture.string( svgData, width: 100.0, height: 100.0, color: const Color(0xFF990000), ), ), ); await tester.pumpAndSettle(); await _checkWidgetAndGolden(key, 'text_color_filter.png'); }, skip: !isLinux); testWidgets('Nested SVG elements report a FlutterError', (WidgetTester tester) async { await svg.fromSvgString( '', 'test'); final UnsupportedError error = tester.takeException() as UnsupportedError; expect(error.message, 'Unsupported nested element.'); }); testWidgets('Can take AlignmentDirectional', (WidgetTester tester) async { await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, child: SvgPicture.string( svgStr, alignment: AlignmentDirectional.bottomEnd, ), )); expect(find.byType(SvgPicture), findsOneWidget); }); testWidgets('SvgPicture.string respects clipBehavior', (WidgetTester tester) async { await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, child: SvgPicture.string(svgStr), )); await tester.pumpAndSettle(); // Check that the render object has received the default clip behavior. final RenderFittedBox renderObject = tester.allRenderObjects.whereType().first; expect(renderObject.clipBehavior, equals(Clip.hardEdge)); // Pump a new widget to check that the render object can update its clip // behavior. await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: SvgPicture.string(svgStr, clipBehavior: Clip.antiAlias), ), ); await tester.pumpAndSettle(); expect(renderObject.clipBehavior, equals(Clip.antiAlias)); }); testWidgets('SvgPicture.asset respects clipBehavior', (WidgetTester tester) async { final MockAssetBundle mockAsset = MockAssetBundle(); when(mockAsset.loadString('test.svg')) .thenAnswer((_) => Future.value(svgStr)); await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, child: SvgPicture.asset( 'test.svg', bundle: mockAsset, ), )); await tester.pumpAndSettle(); // Check that the render object has received the default clip behavior. final RenderFittedBox renderObject = tester.allRenderObjects.whereType().first; expect(renderObject.clipBehavior, equals(Clip.hardEdge)); // Pump a new widget to check that the render object can update its clip // behavior. await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: SvgPicture.asset( 'test.svg', bundle: mockAsset, clipBehavior: Clip.antiAlias, ), ), ); await tester.pumpAndSettle(); expect(renderObject.clipBehavior, equals(Clip.antiAlias)); }); testWidgets('SvgPicture.memory respects clipBehavior', (WidgetTester tester) async { await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, child: SvgPicture.memory(svgBytes), )); await tester.pumpAndSettle(); // Check that the render object has received the default clip behavior. final RenderFittedBox renderObject = tester.allRenderObjects.whereType().first; expect(renderObject.clipBehavior, equals(Clip.hardEdge)); // Pump a new widget to check that the render object can update its clip // behavior. await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: SvgPicture.memory(svgBytes, clipBehavior: Clip.antiAlias), ), ); await tester.pumpAndSettle(); expect(renderObject.clipBehavior, equals(Clip.antiAlias)); }); testWidgets('SvgPicture.network respects clipBehavior', (WidgetTester tester) async { await HttpOverrides.runZoned(() async { when(mockResponse.statusCode).thenReturn(200); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: SvgPicture.network('test.svg'), ), ); await tester.pumpAndSettle(); // Check that the render object has received the default clip behavior. final RenderFittedBox renderObject = tester.allRenderObjects.whereType().first; expect(renderObject.clipBehavior, equals(Clip.hardEdge)); // Pump a new widget to check that the render object can update its clip // behavior. await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: SvgPicture.network('test.svg', clipBehavior: Clip.antiAlias), ), ); await tester.pumpAndSettle(); expect(renderObject.clipBehavior, equals(Clip.antiAlias)); }, createHttpClient: (SecurityContext c) => mockHttpClient); }); testWidgets('SvgPicture respects clipBehavior', (WidgetTester tester) async { await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, child: SvgPicture.string(svgStr), )); await tester.pumpAndSettle(); // Check that the render object has received the default clip behavior. final RenderFittedBox renderObject = tester.allRenderObjects.whereType().first; expect(renderObject.clipBehavior, equals(Clip.hardEdge)); // Pump a new widget to check that the render object can update its clip // behavior. await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: SvgPicture.string(svgStr, clipBehavior: Clip.antiAlias), ), ); await tester.pumpAndSettle(); expect(renderObject.clipBehavior, equals(Clip.antiAlias)); }); } class MockAssetBundle extends Mock implements AssetBundle {} class MockHttpClient extends Mock implements HttpClient {} class MockHttpClientRequest extends Mock implements HttpClientRequest {} class MockHttpClientResponse extends Mock implements HttpClientResponse {}