// test/pen_select_move_test.dart // // Pins the SELECT tool's select + move + delete on the PenCanvas editors (note): // * tapping a committed stroke selects it (onSelectStroke fires with its index); // * dragging the selection translates the stroke's normalized points (the // committed stroke list is replaced with shifted points); // * the move is one undoable step (undo restores the original points). // // Drives the real PenNoteScreen with a pre-loaded stroke and a stylus gesture. import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:badnote/editor/canvas/pen_canvas.dart'; import 'package:badnote/editor/canvas/pen_note_screen.dart'; import 'package:badnote/editor/canvas/pen_stroke.dart'; import 'package:badnote/editor/engine/shape_geometry.dart'; import 'package:badnote/models/ink_point.dart'; import 'package:badnote/models/ink_stroke.dart'; import 'package:badnote/models/note.dart'; import 'package:badnote/models/pen_tool.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); // A note with a single stroke crossing the page center so a center tap hits it. Note noteWithStroke() => Note( id: 'n1', title: 'Test', strokes: [ InkStroke( id: 's1', // Through the page center (kNoteLogicalPage = 1000 x 1414) so a // center tap on the canvas hits the stroke. points: const [ InkPoint(x: 300, y: 707, timestamp: 0), InkPoint(x: 500, y: 707, timestamp: 0), InkPoint(x: 700, y: 707, timestamp: 0), ], tool: PenTool.pen, createdAt: DateTime.fromMillisecondsSinceEpoch(0), ), ], createdAt: DateTime.fromMillisecondsSinceEpoch(0), updatedAt: DateTime.fromMillisecondsSinceEpoch(0), ); PenCanvas canvas(WidgetTester tester) => tester.widget(find.byType(PenCanvas)); List strokes(WidgetTester tester) => canvas(tester).strokes; /// Activate the SELECT tool via its toolbar button (the "Select" tooltip). Future activateSelect(WidgetTester tester) async { await tester.tap(find.byTooltip('Select')); await tester.pumpAndSettle(); } testWidgets('tapping a stroke selects it, dragging moves it, undo restores', (tester) async { SharedPreferences.setMockInitialValues({}); await tester.pumpWidget(ProviderScope( child: MaterialApp(home: PenNoteScreen(note: noteWithStroke())), )); await tester.pump(); // PenConfig load expect(strokes(tester), hasLength(1)); final before = strokes(tester).single; final beforeBounds = penStrokeBounds(before)!; await activateSelect(tester); expect(canvas(tester).tool, CanvasTool.select); // Tap the stroke (page center) to select, then drag it down-right. final center = tester.getCenter(find.byType(PenCanvas)); final g = await tester.startGesture(center, kind: PointerDeviceKind.stylus); await tester.pump(); // selection should now be set. expect(canvas(tester).selectedStrokeIndex, 0); await g.moveBy(const Offset(40, 30)); await g.moveBy(const Offset(20, 10)); await g.up(); await tester.pump(); final after = strokes(tester).single; final afterBounds = penStrokeBounds(after)!; // The stroke translated: its bounds shifted right and down. expect(afterBounds.left, greaterThan(beforeBounds.left), reason: 'stroke moved right'); expect(afterBounds.top, greaterThan(beforeBounds.top), reason: 'stroke moved down'); // Same number of points (a translate, not a redraw). expect(after.points.length, before.points.length); // Undo restores the original position (one undoable step). await tester.tap(find.byTooltip('Undo')); await tester.pump(); final undoneBounds = penStrokeBounds(strokes(tester).single)!; expect(undoneBounds.left, closeTo(beforeBounds.left, 1e-6)); expect(undoneBounds.top, closeTo(beforeBounds.top, 1e-6)); }); testWidgets('delete-selection removes the selected stroke (undoable)', (tester) async { SharedPreferences.setMockInitialValues({}); await tester.pumpWidget(ProviderScope( child: MaterialApp(home: PenNoteScreen(note: noteWithStroke())), )); await tester.pump(); await activateSelect(tester); final center = tester.getCenter(find.byType(PenCanvas)); final g = await tester.startGesture(center, kind: PointerDeviceKind.stylus); await g.up(); await tester.pump(); expect(canvas(tester).selectedStrokeIndex, 0); // The delete button appears only with a live selection. await tester.tap(find.byTooltip('Delete selection')); await tester.pump(); expect(strokes(tester), isEmpty); await tester.tap(find.byTooltip('Undo')); await tester.pump(); expect(strokes(tester), hasLength(1)); }); }