// Guards the two eraser modes (the "优化橡皮擦工具" work): the default point // eraser cuts a stroke into surviving segments, while the new stroke-eraser // mode removes the entire stroke on contact. Both are driven by the same // stylus pass; only the eraserWholeStroke flag differs. import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:badnote/editor/canvas/pen_canvas.dart'; import 'package:badnote/editor/canvas/pen_stroke.dart'; void main() { const pageSize = Size(400, 600); // A long horizontal stroke spanning the page through the vertical center. PenStroke longStroke() => PenStroke( points: const [ PenPoint(0.1, 0.5, 0.5), PenPoint(0.3, 0.5, 0.5), PenPoint(0.5, 0.5, 0.5), PenPoint(0.7, 0.5, 0.5), PenPoint(0.9, 0.5, 0.5), ], color: 0xFF000000, width: 0.004, kind: PenStrokeKind.pen, ); Widget host({ required void Function(int, List) onErase, required bool wholeStroke, }) { final controller = TransformationController(); addTearDown(controller.dispose); return MaterialApp( home: Scaffold( body: Center( child: SizedBox( width: pageSize.width, height: pageSize.height, child: PenCanvas( pageWidget: Container(color: const Color(0xFFEEEEEE)), pageSize: pageSize, strokes: [longStroke()], transformationController: controller, tool: CanvasTool.eraser, color: const Color(0xFF000000), strokeWidth: 0.004, eraserWholeStroke: wholeStroke, onStrokeComplete: (_) {}, onEraseStroke: onErase, ), ), ), ), ); } // Drag the eraser vertically through the page center (normalized 0.5, 0.5), // grazing the MIDDLE of the long horizontal stroke. Future erasePass(WidgetTester tester) async { final center = tester.getCenter(find.byType(PenCanvas)); final g = await tester.startGesture(center + const Offset(0, -15), kind: PointerDeviceKind.stylus); await g.moveBy(const Offset(0, 15)); await g.moveBy(const Offset(0, 15)); await g.up(); await tester.pump(); } testWidgets('point eraser (default) splits the stroke into surviving pieces', (tester) async { List? replacement; await tester.pumpWidget(host( onErase: (_, pieces) => replacement = pieces, wholeStroke: false, )); await erasePass(tester); expect(replacement, isNotNull, reason: 'an erase should have fired'); // Grazing the middle leaves the two ends as surviving sub-strokes. expect(replacement!.length, greaterThanOrEqualTo(1)); expect(replacement, isNotEmpty, reason: 'point eraser keeps the untouched ends'); }); testWidgets('stroke eraser removes the entire stroke on contact', (tester) async { List? replacement; await tester.pumpWidget(host( onErase: (_, pieces) => replacement = pieces, wholeStroke: true, )); await erasePass(tester); expect(replacement, isNotNull, reason: 'an erase should have fired'); expect(replacement, isEmpty, reason: 'stroke eraser deletes the whole stroke, leaving no pieces'); }); }