Pārlūkot izejas kodu

test: add more calendar integration tests (#2883)

* test: make sure that the row details page opens

* test: add more calendar tests
Richard Shiue 1 gadu atpakaļ
vecāks
revīzija
bef7fe87aa

+ 183 - 0
frontend/appflowy_flutter/integration_test/database_calendar_test.dart

@@ -74,5 +74,188 @@ void main() {
 
       await tester.pumpAndSettle();
     });
+
+    testWidgets('create new calendar event using new button', (tester) async {
+      await tester.initializeAppFlowy();
+      await tester.tapGoButton();
+
+      // Create the calendar view
+      await tester.tapAddButton();
+      await tester.tapCreateCalendarButton();
+
+      // Scroll until today's date cell is visible
+      await tester.scrollToToday();
+
+      // Hover over today's calendar cell
+      await tester.hoverOnTodayCalendarCell();
+
+      // Tap on create new event button
+      await tester.tapAddCalendarEventButton();
+
+      // Make sure that the row details page is opened
+      tester.assertRowDetailPageOpened();
+
+      // Dismiss the row details page
+      await tester.dismissRowDetailPage();
+
+      // Make sure that the event is inserted in the cell
+      tester.assertNumberOfEventsInCalendar(1);
+
+      // Create another event on the same day
+      await tester.hoverOnTodayCalendarCell();
+      await tester.tapAddCalendarEventButton();
+      await tester.dismissRowDetailPage();
+      tester.assertNumberOfEventsInCalendar(2);
+      tester.assertNumberofEventsOnSpecificDay(2, DateTime.now());
+    });
+
+    testWidgets('create new calendar event by double-clicking', (tester) async {
+      await tester.initializeAppFlowy();
+      await tester.tapGoButton();
+
+      // Create the calendar view
+      await tester.tapAddButton();
+      await tester.tapCreateCalendarButton();
+
+      // Scroll until today's date cell is visible
+      await tester.scrollToToday();
+
+      // Double click on today's calendar cell to create a new event
+      await tester.doubleClickCalendarCell(DateTime.now());
+
+      // Make sure that the row details page is opened
+      tester.assertRowDetailPageOpened();
+
+      // Dismiss the row details page
+      await tester.dismissRowDetailPage();
+
+      // Make sure that the event is inserted in the cell
+      tester.assertNumberOfEventsInCalendar(1);
+
+      // Create another event on the same day
+      await tester.doubleClickCalendarCell(DateTime.now());
+      await tester.dismissRowDetailPage();
+      tester.assertNumberOfEventsInCalendar(2);
+      tester.assertNumberofEventsOnSpecificDay(2, DateTime.now());
+    });
+
+    testWidgets('duplicate/delete a calendar event', (tester) async {
+      await tester.initializeAppFlowy();
+      await tester.tapGoButton();
+
+      // Create the calendar view
+      await tester.tapAddButton();
+      await tester.tapCreateCalendarButton();
+
+      // Create a new event in today's calendar cell
+      await tester.scrollToToday();
+      await tester.doubleClickCalendarCell(DateTime.now());
+
+      // Duplicate the event
+      await tester.tapRowDetailPageDuplicateRowButton();
+      await tester.dismissRowDetailPage();
+
+      // Check that there are 2 events
+      tester.assertNumberOfEventsInCalendar(2);
+
+      // Delete an event
+      await tester.openCalendarEvent(index: 0);
+      await tester.tapRowDetailPageDeleteRowButton();
+
+      // Check that there is 1 event
+      tester.assertNumberOfEventsInCalendar(1);
+    });
+
+    testWidgets('edit the title of a calendar date event', (tester) async {
+      await tester.initializeAppFlowy();
+      await tester.tapGoButton();
+
+      // Create the calendar view
+      await tester.tapAddButton();
+      await tester.tapCreateCalendarButton();
+
+      // Create a new event in today's calendar cell
+      await tester.scrollToToday();
+      await tester.doubleClickCalendarCell(DateTime.now());
+      await tester.dismissRowDetailPage();
+
+      // Click on the event
+      await tester.openCalendarEvent(index: 0);
+
+      // Make sure that the row details page is opened
+      tester.assertRowDetailPageOpened();
+
+      // Change the title of the event
+      await tester.editTitleInRowDetailPage('hello world');
+
+      // Dismiss the row details page
+      await tester.dismissRowDetailPage();
+
+      // Make sure that the event is edited
+      tester.assertNumberOfEventsInCalendar(1, title: 'hello world');
+    });
+
+    testWidgets(
+        'edit the date of the date field being used to layout the calendar',
+        (tester) async {
+      await tester.initializeAppFlowy();
+      await tester.tapGoButton();
+
+      // Create the calendar view
+      await tester.tapAddButton();
+      await tester.tapCreateCalendarButton();
+
+      // Create a new event in today's calendar cell
+      await tester.scrollToToday();
+      await tester.doubleClickCalendarCell(DateTime.now());
+      await tester.dismissRowDetailPage();
+
+      // Make sure that the event is today
+      tester.assertNumberofEventsOnSpecificDay(1, DateTime.now());
+
+      // Click on the event
+      await tester.openCalendarEvent(index: 0);
+
+      // Open the date editor of the event
+      await tester.tapDateCellInRowDetailPage();
+      await tester.findDateEditor(findsOneWidget);
+
+      // Edit the event's date
+      final tomorrow = DateTime.now().add(const Duration(days: 1));
+      await tester.selectDay(content: tomorrow.day);
+      await tester.dismissCellEditor();
+
+      // Dismiss the row details page
+      await tester.dismissRowDetailPage();
+
+      // Make sure that the event is edited
+      tester.assertNumberOfEventsInCalendar(1);
+      tester.assertNumberofEventsOnSpecificDay(1, tomorrow);
+    });
+
+    testWidgets('reschedule an event by drag-and-drop', (tester) async {
+      await tester.initializeAppFlowy();
+      await tester.tapGoButton();
+
+      // Create the calendar view
+      await tester.tapAddButton();
+      await tester.tapCreateCalendarButton();
+
+      // Create a new event on the first of this month
+      final today = DateTime.now();
+      final firstOfThisMonth = DateTime(today.year, today.month, 1);
+      await tester.doubleClickCalendarCell(firstOfThisMonth);
+      await tester.dismissRowDetailPage();
+
+      // Drag and drop the event onto the next week, same day
+      await tester.dragDropRescheduleCalendarEvent(firstOfThisMonth);
+
+      // Make sure that the event has been rescheduled to the new date
+      tester.assertNumberOfEventsInCalendar(1);
+      tester.assertNumberofEventsOnSpecificDay(
+        1,
+        firstOfThisMonth.add(const Duration(days: 7)),
+      );
+    });
   });
 }

+ 15 - 0
frontend/appflowy_flutter/integration_test/database_row_page_test.dart

@@ -28,6 +28,21 @@ void main() {
       await TestFolder.cleanTestLocation(null);
     });
 
+    testWidgets('row details page opens', (tester) async {
+      await tester.initializeAppFlowy();
+      await tester.tapGoButton();
+
+      // Create a new grid
+      await tester.tapAddButton();
+      await tester.tapCreateGridButton();
+
+      // Hover first row and then open the row page
+      await tester.openFirstRowDetailPage();
+
+      // Make sure that the row page is opened
+      tester.assertRowDetailPageOpened();
+    });
+
     testWidgets('insert emoji in the row detail page', (tester) async {
       await tester.initializeAppFlowy();
       await tester.tapGoButton();

+ 23 - 0
frontend/appflowy_flutter/integration_test/util/base.dart

@@ -127,6 +127,29 @@ extension AppFlowyTestBase on WidgetTester {
     return;
   }
 
+  Future<void> doubleTapButton(
+    Finder finder, {
+    int? pointer,
+    int buttons = kPrimaryButton,
+    bool warnIfMissed = true,
+    int milliseconds = 500,
+  }) async {
+    await tapButton(
+      finder,
+      pointer: pointer,
+      buttons: buttons,
+      warnIfMissed: warnIfMissed,
+      milliseconds: kDoubleTapMinTime.inMilliseconds,
+    );
+    await tapButton(
+      finder,
+      pointer: pointer,
+      buttons: buttons,
+      warnIfMissed: warnIfMissed,
+      milliseconds: milliseconds,
+    );
+  }
+
   Future<void> wait(int milliseconds) async {
     await pumpAndSettle(Duration(milliseconds: milliseconds));
     return;

+ 120 - 0
frontend/appflowy_flutter/integration_test/util/database_test_op.dart

@@ -2,6 +2,7 @@ import 'dart:io';
 
 import 'package:appflowy/generated/locale_keys.g.dart';
 import 'package:appflowy/plugins/database_view/board/presentation/board_page.dart';
+import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_day.dart';
 import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_page.dart';
 import 'package:appflowy/plugins/database_view/calendar/presentation/toolbar/calendar_layout_setting.dart';
 import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/checkbox.dart';
@@ -427,6 +428,22 @@ extension AppFlowyDatabaseTest on WidgetTester {
     await tapButton(expandButton);
   }
 
+  void assertRowDetailPageOpened() async {
+    final findRowDetailPage = find.byType(RowDetailPage);
+    expect(findRowDetailPage, findsOneWidget);
+  }
+
+  Future<void> dismissRowDetailPage() async {
+    await sendKeyEvent(LogicalKeyboardKey.escape);
+    await pumpAndSettle();
+  }
+
+  Future<void> editTitleInRowDetailPage(String title) async {
+    final titleField = find.byType(GridTextCell);
+    await enterText(titleField, title);
+    await pumpAndSettle();
+  }
+
   Future<void> hoverRowBanner() async {
     final banner = find.byType(RowBanner);
     expect(banner, findsOneWidget);
@@ -455,6 +472,21 @@ extension AppFlowyDatabaseTest on WidgetTester {
     await tapButton(emojiWidget);
   }
 
+  Future<void> tapDateCellInRowDetailPage() async {
+    final findDateCell = find.byType(GridDateCell);
+    await tapButton(findDateCell);
+  }
+
+  Future<void> duplicateRowInRowDetailPage() async {
+    final duplicateButton = find.byType(RowDetailPageDuplicateButton);
+    await tapButton(duplicateButton);
+  }
+
+  Future<void> deleteRowInRowDetailPage() async {
+    final deleteButton = find.byType(RowDetailPageDeleteButton);
+    await tapButton(deleteButton);
+  }
+
   Future<void> scrollGridByOffset(Offset offset) async {
     await drag(find.byType(GridPage), offset);
     await pumpAndSettle();
@@ -943,6 +975,94 @@ extension AppFlowyDatabaseTest on WidgetTester {
     expect(finder, findsOneWidget);
   }
 
+  Future<void> scrollToToday() async {
+    final todayCell = find.byWidgetPredicate(
+      (widget) => widget is CalendarDayCard && widget.isToday,
+      skipOffstage: false,
+    );
+    await ensureVisible(todayCell);
+    await pumpAndSettle(const Duration(milliseconds: 300));
+  }
+
+  Future<void> hoverOnTodayCalendarCell() async {
+    final todayCell = find.byWidgetPredicate(
+      (widget) => widget is CalendarDayCard && widget.isToday,
+    );
+
+    await hoverOnWidget(todayCell);
+  }
+
+  Future<void> tapAddCalendarEventButton() async {
+    final findFlowyButton = find.byType(FlowyIconButton);
+    final findNewEventButton = find.byType(NewEventButton);
+    final button = find.descendant(
+      of: findNewEventButton,
+      matching: findFlowyButton,
+    );
+    await tapButton(button);
+  }
+
+  /// Checks for a certain number of events. Parameters [date] and [title] can
+  /// also be provided to restrict the scope of the search
+  void assertNumberOfEventsInCalendar(int number, {String? title}) {
+    Finder findEvents = find.byType(EventCard);
+    if (title != null) {
+      findEvents = find.descendant(of: findEvents, matching: find.text(title));
+    }
+    expect(findEvents, findsNWidgets(number));
+  }
+
+  void assertNumberofEventsOnSpecificDay(
+    int number,
+    DateTime date, {
+    String? title,
+  }) {
+    final findDayCell = find.byWidgetPredicate(
+      (widget) =>
+          widget is CalendarDayCard &&
+          isSameDay(
+            widget.date,
+            date,
+          ),
+    );
+    Finder findEvents = find.descendant(
+      of: findDayCell,
+      matching: find.byType(EventCard),
+    );
+    if (title != null) {
+      findEvents = find.descendant(of: findEvents, matching: find.text(title));
+    }
+    expect(findEvents, findsNWidgets(number));
+  }
+
+  Future<void> doubleClickCalendarCell(DateTime date) async {
+    final todayCell = find.byWidgetPredicate(
+      (widget) => widget is CalendarDayCard && isSameDay(date, widget.date),
+    );
+
+    await doubleTapButton(todayCell);
+  }
+
+  Future<void> openCalendarEvent({required index, DateTime? date}) async {
+    final findDayCell = find.byWidgetPredicate(
+      (widget) =>
+          widget is CalendarDayCard &&
+          isSameDay(widget.date, date ?? DateTime.now()),
+    );
+    final cards = find.descendant(
+      of: findDayCell,
+      matching: find.byType(EventCard),
+    );
+
+    await tapButton(cards.at(index));
+  }
+
+  Future<void> dragDropRescheduleCalendarEvent(DateTime startDate) async {
+    final findEventCard = find.byType(EventCard);
+    await drag(findEventCard.first, const Offset(0, 300));
+    await pumpAndSettle();
+  }
+
   Future<void> tapCreateLinkedDatabaseViewButton(AddButtonAction action) async {
     final findAddButton = find.byType(AddDatabaseViewButton);
     await tapButton(findAddButton);

+ 8 - 6
frontend/appflowy_flutter/lib/plugins/database_view/calendar/presentation/calendar_day.dart

@@ -113,7 +113,7 @@ class CalendarDayCard extends StatelessWidget {
                         .add(CalendarEvent.moveEvent(event, date));
                   },
                 ),
-                _NewEventButton(onCreate: () => onCreateEvent(date)),
+                NewEventButton(onCreate: () => onCreateEvent(date)),
                 MouseRegion(
                   onEnter: (p) => notifyEnter(context, true),
                   onExit: (p) => notifyEnter(context, false),
@@ -160,9 +160,10 @@ class _Header extends StatelessWidget {
   }
 }
 
-class _NewEventButton extends StatelessWidget {
+@visibleForTesting
+class NewEventButton extends StatelessWidget {
   final VoidCallback onCreate;
-  const _NewEventButton({required this.onCreate, Key? key}) : super(key: key);
+  const NewEventButton({required this.onCreate, Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
@@ -254,7 +255,7 @@ class _EventList extends StatelessWidget {
   Widget build(BuildContext context) {
     return Flexible(
       child: ListView.separated(
-        itemBuilder: (BuildContext context, int index) => _EventCard(
+        itemBuilder: (BuildContext context, int index) => EventCard(
           event: events[index],
           viewId: viewId,
           rowCache: rowCache,
@@ -270,13 +271,14 @@ class _EventList extends StatelessWidget {
   }
 }
 
-class _EventCard extends StatelessWidget {
+@visibleForTesting
+class EventCard extends StatelessWidget {
   final CalendarDayEvent event;
   final String viewId;
   final RowCache rowCache;
   final BoxConstraints constraints;
 
-  const _EventCard({
+  const EventCard({
     required this.event,
     required this.viewId,
     required this.rowCache,

+ 1 - 0
frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/date_cell/date_editor.dart

@@ -337,6 +337,7 @@ class _TimeTextFieldState extends State<_TimeTextField> {
   }
 }
 
+@visibleForTesting
 class DateTypeOptionButton extends StatelessWidget {
   final PopoverMutex popoverMutex;
   const DateTypeOptionButton({