test: add more calendar integration tests (#2883)

* test: make sure that the row details page opens

* test: add more calendar tests
This commit is contained in:
Richard Shiue 2023-06-22 23:40:49 +08:00 committed by GitHub
parent feceb430cf
commit bef7fe87aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 350 additions and 6 deletions

View File

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

View File

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

View File

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

View File

@ -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);

View File

@ -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,

View File

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