From 3832af5fa868e07007bed37db6830a3bc0362cb5 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Wed, 24 Aug 2022 12:02:50 +0800 Subject: [PATCH] test: add test for image_node_widget and image_node_builder --- .../src/render/image/image_node_builder.dart | 12 +- .../src/render/image/image_node_widget.dart | 99 +++++++----- .../render/image/image_node_builder_test.dart | 131 +++++++++++++++ .../render/image/image_node_widget_test.dart | 153 ++++++------------ 4 files changed, 245 insertions(+), 150 deletions(-) create mode 100644 frontend/app_flowy/packages/appflowy_editor/test/render/image/image_node_builder_test.dart diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/image/image_node_builder.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/image/image_node_builder.dart index 5824849540..cab86a5dc4 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/image/image_node_builder.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/image/image_node_builder.dart @@ -41,24 +41,20 @@ class ImageNodeBuilder extends NodeWidgetBuilder { }); 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'; } } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/image/image_node_widget.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/image/image_node_widget.dart index 12b22787ff..f5ffdeb4ce 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/image/image_node_widget.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/image/image_node_widget.dart @@ -54,25 +54,7 @@ class _ImageNodeWidgetState extends State { 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 { }, ), 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 { ), ); } +} - 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 { 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 { 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 { 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 { name: 'image_toolbar/copy', ), onPressed: () { - widget.onCopy(); + onCopy(); }, ), IconButton( @@ -265,7 +284,7 @@ class _ImageNodeWidgetState extends State { name: 'image_toolbar/delete', ), onPressed: () { - widget.onDelete(); + onDelete(); }, ), ], diff --git a/frontend/app_flowy/packages/appflowy_editor/test/render/image/image_node_builder_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/render/image/image_node_builder_test.dart new file mode 100644 index 0000000000..9121fa1868 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/test/render/image/image_node_builder_test.dart @@ -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)); + }); + }); + }); +} diff --git a/frontend/app_flowy/packages/appflowy_editor/test/render/image/image_node_widget_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/render/image/image_node_widget_test.dart index 7829c456eb..4cb68a488f 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/render/image/image_node_widget_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/render/image/image_node_widget_test.dart @@ -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); }); }); });