blob: f70785dd8c4f40e52185574cbf4c7942aad12e1a [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:io' as io;
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:flutter_test/flutter_test.dart';
import 'image_test_mocks.dart';
import 'utils.dart';
void main() => defineTests();
void defineTests() {
group('Image', () {
setUp(() {
// Only needs to be done once since the HttpClient generated
// by this override is cached as a static singleton.
io.HttpOverrides.global = TestHttpOverrides();
});
testWidgets(
'should not interrupt styling',
(WidgetTester tester) async {
const String data = '_textbefore ![alt](https://img) textafter_';
await tester.pumpWidget(
boilerplate(
const Markdown(data: data),
),
);
final Iterable<RichText> texts =
tester.widgetList(find.byType(RichText));
final RichText firstTextWidget = texts.first;
final TextSpan firstTextSpan = firstTextWidget.text as TextSpan;
final Image image = tester.widget(find.byType(Image));
final NetworkImage networkImage = image.image as NetworkImage;
final RichText secondTextWidget = texts.last;
final TextSpan secondTextSpan = secondTextWidget.text as TextSpan;
expect(firstTextSpan.text, 'textbefore ');
expect(firstTextSpan.style!.fontStyle, FontStyle.italic);
expect(networkImage.url, 'https://img');
expect(secondTextSpan.text, ' textafter');
expect(secondTextSpan.style!.fontStyle, FontStyle.italic);
},
);
testWidgets(
'should work with a link',
(WidgetTester tester) async {
const String data = '![alt](https://img#50x50)';
await tester.pumpWidget(
boilerplate(
const Markdown(data: data),
),
);
final Image image = tester.widget(find.byType(Image));
final NetworkImage networkImage = image.image as NetworkImage;
expect(networkImage.url, 'https://img');
expect(image.width, 50);
expect(image.height, 50);
},
);
testWidgets(
'should work with relative remote image',
(WidgetTester tester) async {
const String data = '![alt](/img.png)';
await tester.pumpWidget(
boilerplate(
const Markdown(
data: data,
imageDirectory: 'https://localhost',
),
),
);
final Iterable<Widget> widgets = tester.allWidgets;
final Image image =
widgets.firstWhere((Widget widget) => widget is Image) as Image;
expect(image.image is NetworkImage, isTrue);
expect((image.image as NetworkImage).url, 'https://localhost/img.png');
},
);
testWidgets(
'local files should be files',
(WidgetTester tester) async {
const String data = '![alt](http.png)';
await tester.pumpWidget(
boilerplate(
const Markdown(data: data),
),
);
final Iterable<Widget> widgets = tester.allWidgets;
final Image image =
widgets.firstWhere((Widget widget) => widget is Image) as Image;
expect(image.image is FileImage, isTrue);
},
);
testWidgets(
'should work with resources',
(WidgetTester tester) async {
TestWidgetsFlutterBinding.ensureInitialized();
const String data = '![alt](resource:assets/logo.png)';
await tester.pumpWidget(
boilerplate(
MaterialApp(
home: DefaultAssetBundle(
bundle: TestAssetBundle(),
child: Center(
child: Container(
color: Colors.white,
width: 500,
child: const Markdown(
data: data,
),
),
),
),
),
),
);
final Image image = tester.allWidgets
.firstWhere((Widget widget) => widget is Image) as Image;
expect(image.image is AssetImage, isTrue);
expect((image.image as AssetImage).assetName, 'assets/logo.png');
// Force the asset image to be rasterized so it can be compared.
await tester.runAsync(() async {
final Element element = tester.element(find.byType(Markdown));
await precacheImage(image.image, element);
});
await tester.pumpAndSettle();
await expectLater(
find.byType(Container),
matchesGoldenFile(
'assets/images/golden/image_test/resource_asset_logo.png'));
},
);
testWidgets(
'should work with local image files',
(WidgetTester tester) async {
const String data = '![alt](img.png#50x50)';
await tester.pumpWidget(
boilerplate(
const Markdown(data: data),
),
);
final Image image = tester.widget(find.byType(Image));
final FileImage fileImage = image.image as FileImage;
expect(fileImage.file.path, 'img.png');
expect(image.width, 50);
expect(image.height, 50);
},
);
testWidgets(
'should show properly next to text',
(WidgetTester tester) async {
const String data = 'Hello ![alt](img#50x50)';
await tester.pumpWidget(
boilerplate(
const Markdown(data: data),
),
);
final RichText richText = tester.widget(find.byType(RichText));
final TextSpan textSpan = richText.text as TextSpan;
expect(textSpan.text, 'Hello ');
expect(textSpan.style, isNotNull);
},
);
testWidgets(
'should work when nested in a link',
(WidgetTester tester) async {
final List<String> tapTexts = <String>[];
final List<String?> tapResults = <String?>[];
const String data = '[![alt](https://img#50x50)](href)';
await tester.pumpWidget(
boilerplate(
Markdown(
data: data,
onTapLink: (String text, String? value, String title) {
tapTexts.add(text);
tapResults.add(value);
},
),
),
);
final GestureDetector detector =
tester.widget(find.byType(GestureDetector));
detector.onTap!();
expect(tapTexts.length, 1);
expect(tapTexts, everyElement('alt'));
expect(tapResults.length, 1);
expect(tapResults, everyElement('href'));
},
);
testWidgets(
'should work when nested in a link with text',
(WidgetTester tester) async {
final List<String> tapTexts = <String>[];
final List<String?> tapResults = <String?>[];
const String data =
'[Text before ![alt](https://img#50x50) text after](href)';
await tester.pumpWidget(
boilerplate(
Markdown(
data: data,
onTapLink: (String text, String? value, String title) {
tapTexts.add(text);
tapResults.add(value);
},
),
),
);
final GestureDetector detector =
tester.widget(find.byType(GestureDetector));
detector.onTap!();
final Iterable<RichText> texts =
tester.widgetList(find.byType(RichText));
final RichText firstTextWidget = texts.first;
final TextSpan firstSpan = firstTextWidget.text as TextSpan;
(firstSpan.recognizer as TapGestureRecognizer?)!.onTap!();
final RichText lastTextWidget = texts.last;
final TextSpan lastSpan = lastTextWidget.text as TextSpan;
(lastSpan.recognizer as TapGestureRecognizer?)!.onTap!();
expect(firstSpan.children, null);
expect(firstSpan.text, 'Text before ');
expect(firstSpan.recognizer.runtimeType, equals(TapGestureRecognizer));
expect(lastSpan.children, null);
expect(lastSpan.text, ' text after');
expect(lastSpan.recognizer.runtimeType, equals(TapGestureRecognizer));
expect(tapTexts.length, 3);
expect(tapTexts, everyElement('Text before alt text after'));
expect(tapResults.length, 3);
expect(tapResults, everyElement('href'));
},
);
testWidgets(
'should work alongside different links',
(WidgetTester tester) async {
final List<String> tapTexts = <String>[];
final List<String?> tapResults = <String?>[];
const String data =
'[Link before](firstHref)[![alt](https://img#50x50)](imageHref)[link after](secondHref)';
await tester.pumpWidget(
boilerplate(
Markdown(
data: data,
onTapLink: (String text, String? value, String title) {
tapTexts.add(text);
tapResults.add(value);
},
),
),
);
final Iterable<RichText> texts =
tester.widgetList(find.byType(RichText));
final RichText firstTextWidget = texts.first;
final TextSpan firstSpan = firstTextWidget.text as TextSpan;
(firstSpan.recognizer as TapGestureRecognizer?)!.onTap!();
final GestureDetector detector =
tester.widget(find.byType(GestureDetector));
detector.onTap!();
final RichText lastTextWidget = texts.last;
final TextSpan lastSpan = lastTextWidget.text as TextSpan;
(lastSpan.recognizer as TapGestureRecognizer?)!.onTap!();
expect(firstSpan.children, null);
expect(firstSpan.text, 'Link before');
expect(firstSpan.recognizer.runtimeType, equals(TapGestureRecognizer));
expect(lastSpan.children, null);
expect(lastSpan.text, 'link after');
expect(lastSpan.recognizer.runtimeType, equals(TapGestureRecognizer));
expect(tapTexts.length, 3);
expect(tapTexts, <String>['Link before', 'alt', 'link after']);
expect(tapResults.length, 3);
expect(tapResults, <String>['firstHref', 'imageHref', 'secondHref']);
},
);
testWidgets(
'custom image builder',
(WidgetTester tester) async {
const String data = '![alt](https://img.png)';
Widget builder(Uri uri, String? title, String? alt) =>
Image.asset('assets/logo.png');
await tester.pumpWidget(
boilerplate(
MaterialApp(
home: DefaultAssetBundle(
bundle: TestAssetBundle(),
child: Center(
child: Container(
color: Colors.white,
width: 500,
child: Markdown(
data: data,
imageBuilder: builder,
),
),
),
),
),
),
);
final Iterable<Widget> widgets = tester.allWidgets;
final Image image =
widgets.firstWhere((Widget widget) => widget is Image) as Image;
expect(image.image.runtimeType, AssetImage);
expect((image.image as AssetImage).assetName, 'assets/logo.png');
// Force the asset image to be rasterized so it can be compared.
await tester.runAsync(() async {
final Element element = tester.element(find.byType(Markdown));
await precacheImage(image.image, element);
});
await tester.pumpAndSettle();
await expectLater(
find.byType(Container),
matchesGoldenFile(
'assets/images/golden/image_test/custom_builder_asset_logo.png'));
},
);
});
}