test: add test for image_node_widget and image_node_builder

This commit is contained in:
Lucas.Xu 2022-08-24 12:02:50 +08:00
parent 012c3a851a
commit 3832af5fa8
4 changed files with 245 additions and 150 deletions

View File

@ -41,24 +41,20 @@ class ImageNodeBuilder extends NodeWidgetBuilder<Node> {
});
Alignment _textToAlignment(String text) {
if (text == 'center') {
return Alignment.center;
} else if (text == 'left') {
if (text == 'left') {
return Alignment.centerLeft;
} else if (text == 'right') {
return Alignment.centerRight;
}
throw UnimplementedError();
return Alignment.center;
}
String _alignmentToText(Alignment alignment) {
if (alignment == Alignment.center) {
return 'center';
} else if (alignment == Alignment.centerLeft) {
if (alignment == Alignment.centerLeft) {
return 'left';
} else if (alignment == Alignment.centerRight) {
return 'right';
}
throw UnimplementedError();
return 'center';
}
}

View File

@ -54,25 +54,7 @@ class _ImageNodeWidgetState extends State<ImageNodeWidget> {
widget.src,
width: imageWidth == null ? null : imageWidth! - _distance,
loadingBuilder: (context, child, loadingProgress) =>
loadingProgress == null
? child
: SizedBox(
width: imageWidth,
height: 300,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox.fromSize(
size: const Size(18, 18),
child: const CircularProgressIndicator(),
),
SizedBox.fromSize(
size: const Size(10, 10),
),
const Text('Loading'),
],
),
),
loadingProgress == null ? child : _buildLoading(context),
);
if (imageWidth == null) {
networkImage.image.resolve(const ImageConfiguration()).addListener(
@ -111,16 +93,39 @@ class _ImageNodeWidgetState extends State<ImageNodeWidget> {
},
),
if (_onFocus)
_buildImageToolbar(
context,
ImageToolbar(
top: 8,
right: 8,
height: 30,
),
alignment: widget.alignment,
onAlign: widget.onAlign,
onCopy: widget.onCopy,
onDelete: widget.onDelete,
)
],
);
}
Widget _buildLoading(BuildContext context) {
return SizedBox(
width: imageWidth,
height: 300,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox.fromSize(
size: const Size(18, 18),
child: const CircularProgressIndicator(),
),
SizedBox.fromSize(
size: const Size(10, 10),
),
const Text('Loading'),
],
),
);
}
Widget _buildEdgeGesture(
BuildContext context, {
double? top,
@ -169,20 +174,34 @@ class _ImageNodeWidgetState extends State<ImageNodeWidget> {
),
);
}
}
Widget _buildImageToolbar(
BuildContext context, {
double? top,
double? left,
double? right,
double? width,
double? height,
}) {
@visibleForTesting
class ImageToolbar extends StatelessWidget {
const ImageToolbar({
Key? key,
required this.top,
required this.right,
required this.height,
required this.alignment,
required this.onCopy,
required this.onDelete,
required this.onAlign,
}) : super(key: key);
final double top;
final double right;
final double height;
final Alignment alignment;
final VoidCallback onCopy;
final VoidCallback onDelete;
final void Function(Alignment alignment) onAlign;
@override
Widget build(BuildContext context) {
return Positioned(
top: top,
left: left,
right: right,
width: width,
height: height,
child: Container(
decoration: BoxDecoration(
@ -205,12 +224,12 @@ class _ImageNodeWidgetState extends State<ImageNodeWidget> {
padding: const EdgeInsets.fromLTRB(6.0, 4.0, 0.0, 4.0),
icon: FlowySvg(
name: 'image_toolbar/align_left',
color: widget.alignment == Alignment.centerLeft
color: alignment == Alignment.centerLeft
? const Color(0xFF00BCF0)
: null,
),
onPressed: () {
widget.onAlign(Alignment.centerLeft);
onAlign(Alignment.centerLeft);
},
),
IconButton(
@ -219,12 +238,12 @@ class _ImageNodeWidgetState extends State<ImageNodeWidget> {
padding: const EdgeInsets.fromLTRB(0.0, 4.0, 0.0, 4.0),
icon: FlowySvg(
name: 'image_toolbar/align_center',
color: widget.alignment == Alignment.center
color: alignment == Alignment.center
? const Color(0xFF00BCF0)
: null,
),
onPressed: () {
widget.onAlign(Alignment.center);
onAlign(Alignment.center);
},
),
IconButton(
@ -233,12 +252,12 @@ class _ImageNodeWidgetState extends State<ImageNodeWidget> {
padding: const EdgeInsets.fromLTRB(0.0, 4.0, 4.0, 4.0),
icon: FlowySvg(
name: 'image_toolbar/align_right',
color: widget.alignment == Alignment.centerRight
color: alignment == Alignment.centerRight
? const Color(0xFF00BCF0)
: null,
),
onPressed: () {
widget.onAlign(Alignment.centerRight);
onAlign(Alignment.centerRight);
},
),
const Center(
@ -254,7 +273,7 @@ class _ImageNodeWidgetState extends State<ImageNodeWidget> {
name: 'image_toolbar/copy',
),
onPressed: () {
widget.onCopy();
onCopy();
},
),
IconButton(
@ -265,7 +284,7 @@ class _ImageNodeWidgetState extends State<ImageNodeWidget> {
name: 'image_toolbar/delete',
),
onPressed: () {
widget.onDelete();
onDelete();
},
),
],

View File

@ -0,0 +1,131 @@
import 'package:appflowy_editor/src/render/image/image_node_widget.dart';
import 'package:appflowy_editor/src/service/editor_service.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:network_image_mock/network_image_mock.dart';
import '../../infra/test_editor.dart';
void main() async {
setUpAll(() {
TestWidgetsFlutterBinding.ensureInitialized();
});
group('image_node_builder.dart', () {
testWidgets('render image node', (tester) async {
mockNetworkImagesFor(() async {
const text = 'Welcome to Appflowy 😁';
const src =
'https://images.unsplash.com/photo-1471897488648-5eae4ac6686b?ixlib=rb-1.2.1&dl=sarah-dorweiler-QeVmJxZOv3k-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb';
final editor = tester.editor
..insertTextNode(text)
..insertImageNode(src)
..insertTextNode(text);
await editor.startTesting();
expect(editor.documentLength, 3);
expect(find.byType(Image), findsOneWidget);
});
});
testWidgets('render image align', (tester) async {
mockNetworkImagesFor(() async {
const text = 'Welcome to Appflowy 😁';
const src =
'https://images.unsplash.com/photo-1471897488648-5eae4ac6686b?ixlib=rb-1.2.1&dl=sarah-dorweiler-QeVmJxZOv3k-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb';
final editor = tester.editor
..insertTextNode(text)
..insertImageNode(src, align: 'left')
..insertImageNode(src, align: 'center')
..insertImageNode(src, align: 'right')
..insertTextNode(text);
await editor.startTesting();
expect(editor.documentLength, 5);
final imageFinder = find.byType(Image);
expect(imageFinder, findsNWidgets(3));
final editorFinder = find.byType(AppFlowyEditor);
final editorRect = tester.getRect(editorFinder);
final leftImageRect = tester.getRect(imageFinder.at(0));
expect(leftImageRect.left, editorRect.left);
final rightImageRect = tester.getRect(imageFinder.at(2));
expect(rightImageRect.right, editorRect.right);
final centerImageRect = tester.getRect(imageFinder.at(1));
expect(centerImageRect.left,
(leftImageRect.left + rightImageRect.left) / 2.0);
expect(leftImageRect.size, centerImageRect.size);
expect(rightImageRect.size, centerImageRect.size);
final imageNodeWidgetFinder = find.byType(ImageNodeWidget);
final leftImage =
tester.firstWidget(imageNodeWidgetFinder) as ImageNodeWidget;
leftImage.onAlign(Alignment.center);
await tester.pump(const Duration(milliseconds: 100));
expect(
tester.getRect(imageFinder.at(0)).left,
centerImageRect.left,
);
leftImage.onAlign(Alignment.centerRight);
await tester.pump(const Duration(milliseconds: 100));
expect(
tester.getRect(imageFinder.at(0)).left,
rightImageRect.left,
);
});
});
testWidgets('render image copy', (tester) async {
mockNetworkImagesFor(() async {
const text = 'Welcome to Appflowy 😁';
const src =
'https://images.unsplash.com/photo-1471897488648-5eae4ac6686b?ixlib=rb-1.2.1&dl=sarah-dorweiler-QeVmJxZOv3k-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb';
final editor = tester.editor
..insertTextNode(text)
..insertImageNode(src)
..insertTextNode(text);
await editor.startTesting();
expect(editor.documentLength, 3);
final imageFinder = find.byType(Image);
expect(imageFinder, findsOneWidget);
final imageNodeWidgetFinder = find.byType(ImageNodeWidget);
final image =
tester.firstWidget(imageNodeWidgetFinder) as ImageNodeWidget;
image.onCopy();
});
});
testWidgets('render image delete', (tester) async {
mockNetworkImagesFor(() async {
const text = 'Welcome to Appflowy 😁';
const src =
'https://images.unsplash.com/photo-1471897488648-5eae4ac6686b?ixlib=rb-1.2.1&dl=sarah-dorweiler-QeVmJxZOv3k-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb';
final editor = tester.editor
..insertTextNode(text)
..insertImageNode(src)
..insertImageNode(src)
..insertTextNode(text);
await editor.startTesting();
expect(editor.documentLength, 4);
final imageFinder = find.byType(Image);
expect(imageFinder, findsNWidgets(2));
final imageNodeWidgetFinder = find.byType(ImageNodeWidget);
final image =
tester.firstWidget(imageNodeWidgetFinder) as ImageNodeWidget;
image.onDelete();
await tester.pump(const Duration(milliseconds: 100));
expect(editor.documentLength, 3);
expect(find.byType(Image), findsNWidgets(1));
});
});
});
}

View File

@ -1,130 +1,79 @@
import 'package:appflowy_editor/src/render/image/image_node_widget.dart';
import 'package:appflowy_editor/src/service/editor_service.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:network_image_mock/network_image_mock.dart';
import '../../infra/test_editor.dart';
void main() async {
setUpAll(() {
TestWidgetsFlutterBinding.ensureInitialized();
});
group('image_node_widget.dart', () {
testWidgets('render image node', (tester) async {
testWidgets('build the image node widget', (tester) async {
mockNetworkImagesFor(() async {
const text = 'Welcome to Appflowy 😁';
var onCopyHit = false;
var onDeleteHit = false;
var onAlignHit = false;
const src =
'https://images.unsplash.com/photo-1471897488648-5eae4ac6686b?ixlib=rb-1.2.1&dl=sarah-dorweiler-QeVmJxZOv3k-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb';
final editor = tester.editor
..insertTextNode(text)
..insertImageNode(src)
..insertTextNode(text);
await editor.startTesting();
expect(editor.documentLength, 3);
expect(find.byType(Image), findsOneWidget);
});
});
testWidgets('render image align', (tester) async {
mockNetworkImagesFor(() async {
const text = 'Welcome to Appflowy 😁';
const src =
'https://images.unsplash.com/photo-1471897488648-5eae4ac6686b?ixlib=rb-1.2.1&dl=sarah-dorweiler-QeVmJxZOv3k-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb';
final editor = tester.editor
..insertTextNode(text)
..insertImageNode(src, align: 'left')
..insertImageNode(src, align: 'center')
..insertImageNode(src, align: 'right')
..insertTextNode(text);
await editor.startTesting();
expect(editor.documentLength, 5);
final imageFinder = find.byType(Image);
expect(imageFinder, findsNWidgets(3));
final editorFinder = find.byType(AppFlowyEditor);
final editorRect = tester.getRect(editorFinder);
final leftImageRect = tester.getRect(imageFinder.at(0));
expect(leftImageRect.left, editorRect.left);
final rightImageRect = tester.getRect(imageFinder.at(2));
expect(rightImageRect.right, editorRect.right);
final centerImageRect = tester.getRect(imageFinder.at(1));
expect(centerImageRect.left,
(leftImageRect.left + rightImageRect.left) / 2.0);
expect(leftImageRect.size, centerImageRect.size);
expect(rightImageRect.size, centerImageRect.size);
final imageNodeWidgetFinder = find.byType(ImageNodeWidget);
final leftImage =
tester.firstWidget(imageNodeWidgetFinder) as ImageNodeWidget;
leftImage.onAlign(Alignment.center);
await tester.pump(const Duration(milliseconds: 100));
expect(
tester.getRect(imageFinder.at(0)).left,
centerImageRect.left,
final widget = ImageNodeWidget(
src: src,
alignment: Alignment.center,
onCopy: () {
onCopyHit = true;
},
onDelete: () {
onDeleteHit = true;
},
onAlign: (alignment) {
onAlignHit = true;
},
);
leftImage.onAlign(Alignment.centerRight);
await tester.pump(const Duration(milliseconds: 100));
expect(
tester.getRect(imageFinder.at(0)).left,
rightImageRect.left,
await tester.pumpWidget(
MaterialApp(
home: Material(
child: widget,
),
),
);
});
});
expect(find.byType(ImageNodeWidget), findsOneWidget);
testWidgets('render image copy', (tester) async {
mockNetworkImagesFor(() async {
const text = 'Welcome to Appflowy 😁';
const src =
'https://images.unsplash.com/photo-1471897488648-5eae4ac6686b?ixlib=rb-1.2.1&dl=sarah-dorweiler-QeVmJxZOv3k-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb';
final editor = tester.editor
..insertTextNode(text)
..insertImageNode(src)
..insertTextNode(text);
await editor.startTesting();
final gesture =
await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer(location: Offset.zero);
expect(editor.documentLength, 3);
final imageFinder = find.byType(Image);
expect(imageFinder, findsOneWidget);
expect(find.byType(ImageToolbar), findsNothing);
final imageNodeWidgetFinder = find.byType(ImageNodeWidget);
final image =
tester.firstWidget(imageNodeWidgetFinder) as ImageNodeWidget;
image.onCopy();
});
});
addTearDown(gesture.removePointer);
await tester.pump();
await gesture.moveTo(tester.getCenter(find.byType(ImageNodeWidget)));
await tester.pump();
testWidgets('render image delete', (tester) async {
mockNetworkImagesFor(() async {
const text = 'Welcome to Appflowy 😁';
const src =
'https://images.unsplash.com/photo-1471897488648-5eae4ac6686b?ixlib=rb-1.2.1&dl=sarah-dorweiler-QeVmJxZOv3k-unsplash.jpg&w=640&q=80&fm=jpg&crop=entropy&cs=tinysrgb';
final editor = tester.editor
..insertTextNode(text)
..insertImageNode(src)
..insertImageNode(src)
..insertTextNode(text);
await editor.startTesting();
expect(find.byType(ImageToolbar), findsOneWidget);
expect(editor.documentLength, 4);
final imageFinder = find.byType(Image);
expect(imageFinder, findsNWidgets(2));
final iconFinder = find.byType(IconButton);
expect(iconFinder, findsNWidgets(5));
final imageNodeWidgetFinder = find.byType(ImageNodeWidget);
final image =
tester.firstWidget(imageNodeWidgetFinder) as ImageNodeWidget;
image.onDelete();
await tester.tap(iconFinder.at(0));
expect(onAlignHit, true);
onAlignHit = false;
await tester.pump(const Duration(milliseconds: 100));
expect(editor.documentLength, 3);
expect(find.byType(Image), findsNWidgets(1));
await tester.tap(iconFinder.at(1));
expect(onAlignHit, true);
onAlignHit = false;
await tester.tap(iconFinder.at(2));
expect(onAlignHit, true);
onAlignHit = false;
await tester.tap(iconFinder.at(3));
expect(onCopyHit, true);
await tester.tap(iconFinder.at(4));
expect(onDeleteHit, true);
});
});
});