Kaynağa Gözat

test: add test for image_node_widget and image_node_builder

Lucas.Xu 2 yıl önce
ebeveyn
işleme
3832af5fa8

+ 4 - 8
frontend/app_flowy/packages/appflowy_editor/lib/src/render/image/image_node_builder.dart

@@ -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';
   }
 }

+ 59 - 40
frontend/app_flowy/packages/appflowy_editor/lib/src/render/image/image_node_widget.dart

@@ -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();
               },
             ),
           ],

+ 131 - 0
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));
+      });
+    });
+  });
+}

+ 50 - 101
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 {
-      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 {
+    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, 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 widget = ImageNodeWidget(
+          src: src,
+          alignment: Alignment.center,
+          onCopy: () {
+            onCopyHit = true;
+          },
+          onDelete: () {
+            onDeleteHit = true;
+          },
+          onAlign: (alignment) {
+            onAlignHit = true;
+          },
+        );
 
-        final imageNodeWidgetFinder = find.byType(ImageNodeWidget);
+        await tester.pumpWidget(
+          MaterialApp(
+            home: Material(
+              child: widget,
+            ),
+          ),
+        );
+        expect(find.byType(ImageNodeWidget), findsOneWidget);
 
-        final leftImage =
-            tester.firstWidget(imageNodeWidgetFinder) as ImageNodeWidget;
+        final gesture =
+            await tester.createGesture(kind: PointerDeviceKind.mouse);
+        await gesture.addPointer(location: Offset.zero);
 
-        leftImage.onAlign(Alignment.center);
-        await tester.pump(const Duration(milliseconds: 100));
-        expect(
-          tester.getRect(imageFinder.at(0)).left,
-          centerImageRect.left,
-        );
+        expect(find.byType(ImageToolbar), findsNothing);
 
-        leftImage.onAlign(Alignment.centerRight);
-        await tester.pump(const Duration(milliseconds: 100));
-        expect(
-          tester.getRect(imageFinder.at(0)).left,
-          rightImageRect.left,
-        );
-      });
-    });
+        addTearDown(gesture.removePointer);
+        await tester.pump();
+        await gesture.moveTo(tester.getCenter(find.byType(ImageNodeWidget)));
+        await tester.pump();
 
-    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(find.byType(ImageToolbar), findsOneWidget);
 
-        expect(editor.documentLength, 3);
-        final imageFinder = find.byType(Image);
-        expect(imageFinder, findsOneWidget);
+        final iconFinder = find.byType(IconButton);
+        expect(iconFinder, findsNWidgets(5));
 
-        final imageNodeWidgetFinder = find.byType(ImageNodeWidget);
-        final image =
-            tester.firstWidget(imageNodeWidgetFinder) as ImageNodeWidget;
-        image.onCopy();
-      });
-    });
+        await tester.tap(iconFinder.at(0));
+        expect(onAlignHit, true);
+        onAlignHit = false;
 
-    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();
+        await tester.tap(iconFinder.at(1));
+        expect(onAlignHit, true);
+        onAlignHit = false;
 
-        expect(editor.documentLength, 4);
-        final imageFinder = find.byType(Image);
-        expect(imageFinder, findsNWidgets(2));
+        await tester.tap(iconFinder.at(2));
+        expect(onAlignHit, true);
+        onAlignHit = false;
 
-        final imageNodeWidgetFinder = find.byType(ImageNodeWidget);
-        final image =
-            tester.firstWidget(imageNodeWidgetFinder) as ImageNodeWidget;
-        image.onDelete();
+        await tester.tap(iconFinder.at(3));
+        expect(onCopyHit, true);
 
-        await tester.pump(const Duration(milliseconds: 100));
-        expect(editor.documentLength, 3);
-        expect(find.byType(Image), findsNWidgets(1));
+        await tester.tap(iconFinder.at(4));
+        expect(onDeleteHit, true);
       });
     });
   });