1
0
mirror of https://github.com/flutter/samples.git synced 2026-04-05 03:01:19 +00:00

Compass app (#2446)

This commit is contained in:
Eric Windmill
2024-09-27 18:49:27 -04:00
committed by GitHub
parent fcf2552cda
commit 46b5a26b26
326 changed files with 53272 additions and 0 deletions

View File

@@ -0,0 +1,83 @@
// Copyright 2024 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:compass_app/domain/models/itinerary_config/itinerary_config.dart';
import 'package:compass_app/ui/activities/view_models/activities_viewmodel.dart';
import 'package:compass_app/ui/activities/widgets/activities_screen.dart';
import 'package:compass_app/ui/activities/widgets/activity_entry.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:mocktail_image_network/mocktail_image_network.dart';
import '../../../testing/app.dart';
import '../../../testing/fakes/repositories/fake_activities_repository.dart';
import '../../../testing/fakes/repositories/fake_itinerary_config_repository.dart';
import '../../../testing/mocks.dart';
void main() {
group('ResultsScreen widget tests', () {
late ActivitiesViewModel viewModel;
late MockGoRouter goRouter;
setUp(() {
viewModel = ActivitiesViewModel(
activityRepository: FakeActivityRepository(),
itineraryConfigRepository: FakeItineraryConfigRepository(
itineraryConfig: ItineraryConfig(
continent: 'Europe',
startDate: DateTime(2024, 01, 01),
endDate: DateTime(2024, 01, 31),
guests: 2,
destination: 'DESTINATION',
),
),
);
goRouter = MockGoRouter();
});
Future<void> loadScreen(WidgetTester tester) async {
await testApp(
tester,
ActivitiesScreen(viewModel: viewModel),
goRouter: goRouter,
);
}
testWidgets('should load screen', (WidgetTester tester) async {
await mockNetworkImages(() async {
await loadScreen(tester);
expect(find.byType(ActivitiesScreen), findsOneWidget);
});
});
testWidgets('should list activity', (WidgetTester tester) async {
await mockNetworkImages(() async {
await loadScreen(tester);
expect(find.byType(ActivityEntry), findsOneWidget);
expect(find.text('NAME'), findsOneWidget);
});
});
testWidgets('should select activity and confirm',
(WidgetTester tester) async {
await mockNetworkImages(() async {
await loadScreen(tester);
// Select one activity
await tester.tap(find.byKey(const ValueKey('REF-checkbox')));
expect(viewModel.selectedActivities, contains('REF'));
// Text 1 selected should appear
await tester.pumpAndSettle();
expect(find.text('1 selected'), findsOneWidget);
// Submit selection
await tester.tap(find.byKey(const ValueKey('confirm-button')));
// Should navigate to results screen
verify(() => goRouter.go('/booking')).called(1);
});
});
});
}

View File

@@ -0,0 +1,63 @@
// Copyright 2024 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:compass_app/ui/auth/login/view_models/login_viewmodel.dart';
import 'package:compass_app/ui/auth/login/widgets/login_screen.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:mocktail_image_network/mocktail_image_network.dart';
import '../../../testing/app.dart';
import '../../../testing/fakes/repositories/fake_auth_repository.dart';
import '../../../testing/mocks.dart';
void main() {
group('LoginScreen test', () {
late LoginViewModel viewModel;
late MockGoRouter goRouter;
late FakeAuthRepository fakeAuthRepository;
setUp(() {
fakeAuthRepository = FakeAuthRepository();
viewModel = LoginViewModel(
authRepository: fakeAuthRepository,
);
goRouter = MockGoRouter();
});
Future<void> loadScreen(WidgetTester tester) async {
await testApp(
tester,
LoginScreen(viewModel: viewModel),
goRouter: goRouter,
);
}
testWidgets('should load screen', (WidgetTester tester) async {
await mockNetworkImages(() async {
await loadScreen(tester);
expect(find.byType(LoginScreen), findsOneWidget);
});
});
testWidgets('should perform login', (WidgetTester tester) async {
await mockNetworkImages(() async {
await loadScreen(tester);
// Repo should have no key
expect(fakeAuthRepository.token, null);
// Perform login
await tester.tap(find.text('Login'));
await tester.pumpAndSettle();
// Repo should have key
expect(fakeAuthRepository.token, 'TOKEN');
// Should navigate to home screen
verify(() => goRouter.go('/')).called(1);
});
});
});
}

View File

@@ -0,0 +1,78 @@
// Copyright 2024 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:compass_app/domain/models/itinerary_config/itinerary_config.dart';
import 'package:compass_app/ui/auth/logout/view_models/logout_viewmodel.dart';
import 'package:compass_app/ui/auth/logout/widgets/logout_button.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail_image_network/mocktail_image_network.dart';
import '../../../testing/app.dart';
import '../../../testing/fakes/repositories/fake_auth_repository.dart';
import '../../../testing/fakes/repositories/fake_itinerary_config_repository.dart';
import '../../../testing/mocks.dart';
void main() {
group('LogoutButton test', () {
late MockGoRouter goRouter;
late FakeAuthRepository fakeAuthRepository;
late FakeItineraryConfigRepository fakeItineraryConfigRepository;
late LogoutViewModel viewModel;
setUp(() {
goRouter = MockGoRouter();
fakeAuthRepository = FakeAuthRepository();
// Setup a token, should be cleared after logout
fakeAuthRepository.token = 'TOKEN';
// Setup an ItineraryConfig with some data, should be cleared after logout
fakeItineraryConfigRepository = FakeItineraryConfigRepository(
itineraryConfig: const ItineraryConfig(continent: 'CONTINENT'));
viewModel = LogoutViewModel(
authRepository: fakeAuthRepository,
itineraryConfigRepository: fakeItineraryConfigRepository,
);
});
Future<void> loadScreen(WidgetTester tester) async {
await testApp(
tester,
LogoutButton(viewModel: viewModel),
goRouter: goRouter,
);
}
testWidgets('should load widget', (WidgetTester tester) async {
await mockNetworkImages(() async {
await loadScreen(tester);
expect(find.byType(LogoutButton), findsOneWidget);
});
});
testWidgets('should perform logout', (WidgetTester tester) async {
await mockNetworkImages(() async {
await loadScreen(tester);
// Repo should have a key
expect(fakeAuthRepository.token, 'TOKEN');
// Itinerary config should have data
expect(
fakeItineraryConfigRepository.itineraryConfig,
const ItineraryConfig(continent: 'CONTINENT'),
);
// // Perform logout
await tester.tap(find.byType(LogoutButton));
await tester.pumpAndSettle();
// Repo should have no key
expect(fakeAuthRepository.token, null);
// Itinerary config should be cleared
expect(
fakeItineraryConfigRepository.itineraryConfig,
const ItineraryConfig(),
);
});
});
});
}

View File

@@ -0,0 +1,113 @@
// Copyright 2024 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:compass_app/domain/use_cases/booking/booking_create_use_case.dart';
import 'package:compass_app/domain/use_cases/booking/booking_share_use_case.dart';
import 'package:compass_app/domain/models/itinerary_config/itinerary_config.dart';
import 'package:compass_app/ui/booking/view_models/booking_viewmodel.dart';
import 'package:compass_app/ui/booking/widgets/booking_screen.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import '../../../testing/app.dart';
import '../../../testing/fakes/repositories/fake_activities_repository.dart';
import '../../../testing/fakes/repositories/fake_booking_repository.dart';
import '../../../testing/fakes/repositories/fake_destination_repository.dart';
import '../../../testing/fakes/repositories/fake_itinerary_config_repository.dart';
import '../../../testing/mocks.dart';
import '../../../testing/models/activity.dart';
import '../../../testing/models/booking.dart';
import '../../../testing/models/destination.dart';
void main() {
group('BookingScreen widget tests', () {
late MockGoRouter goRouter;
late BookingViewModel viewModel;
late bool shared;
late FakeBookingRepository bookingRepository;
setUp(() {
shared = false;
bookingRepository = FakeBookingRepository();
viewModel = BookingViewModel(
itineraryConfigRepository: FakeItineraryConfigRepository(
itineraryConfig: ItineraryConfig(
continent: 'Europe',
startDate: DateTime(2024, 01, 01),
endDate: DateTime(2024, 01, 31),
guests: 2,
destination: kDestination1.ref,
activities: [kActivity.ref],
),
),
createBookingUseCase: BookingCreateUseCase(
activityRepository: FakeActivityRepository(),
destinationRepository: FakeDestinationRepository(),
bookingRepository: bookingRepository,
),
shareBookingUseCase: BookingShareUseCase.custom((text) async {
shared = true;
}),
bookingRepository: bookingRepository,
);
goRouter = MockGoRouter();
});
Future<void> loadScreen(WidgetTester tester) async {
await testApp(
tester,
BookingScreen(viewModel: viewModel),
goRouter: goRouter,
);
}
testWidgets('should load screen', (WidgetTester tester) async {
await loadScreen(tester);
expect(find.byType(BookingScreen), findsOneWidget);
});
testWidgets('should display booking from ID', (WidgetTester tester) async {
// Add a booking to repository
bookingRepository.createBooking(kBooking);
// Load screen
await loadScreen(tester);
// Load booking with ID 0
viewModel.loadBooking.execute(0);
// Wait for booking to load
await tester.pumpAndSettle();
expect(find.text(kBooking.destination.name), findsOneWidget);
expect(find.text(kBooking.destination.tags.first), findsOneWidget);
});
testWidgets('should create booking from itinerary config',
(WidgetTester tester) async {
await loadScreen(tester);
// Create a new booking from stored itinerary config
viewModel.createBooking.execute();
// Wait for booking to load
await tester.pumpAndSettle();
expect(find.text('name1'), findsOneWidget);
expect(find.text('tags1'), findsOneWidget);
// Booking is saved
expect(bookingRepository.bookings.length, 1);
});
testWidgets('should share booking', (WidgetTester tester) async {
bookingRepository.createBooking(kBooking);
await loadScreen(tester);
viewModel.loadBooking.execute(0);
await tester.pumpAndSettle();
await tester.tap(find.byKey(const Key('share-button')));
expect(shared, true);
});
});
}

View File

@@ -0,0 +1,131 @@
// Copyright 2024 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:compass_app/data/repositories/auth/auth_repository.dart';
import 'package:compass_app/data/repositories/itinerary_config/itinerary_config_repository.dart';
import 'package:compass_app/routing/routes.dart';
import 'package:compass_app/ui/home/view_models/home_viewmodel.dart';
import 'package:compass_app/ui/home/widgets/home_screen.dart';
import 'package:compass_app/utils/result.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:provider/provider.dart';
import '../../../../testing/app.dart';
import '../../../../testing/fakes/repositories/fake_auth_repository.dart';
import '../../../../testing/fakes/repositories/fake_booking_repository.dart';
import '../../../../testing/fakes/repositories/fake_itinerary_config_repository.dart';
import '../../../../testing/fakes/repositories/fake_user_repository.dart';
import '../../../../testing/mocks.dart';
import '../../../../testing/models/booking.dart';
void main() {
group('HomeScreen tests', () {
late HomeViewModel viewModel;
late MockGoRouter goRouter;
late FakeBookingRepository bookingRepository;
setUp(() {
bookingRepository = FakeBookingRepository()..createBooking(kBooking);
viewModel = HomeViewModel(
bookingRepository: bookingRepository,
userRepository: FakeUserRepository(),
);
goRouter = MockGoRouter();
when(() => goRouter.push(any())).thenAnswer((_) => Future.value(null));
});
loadWidget(WidgetTester tester) async {
await testApp(
tester,
ChangeNotifierProvider.value(
value: FakeAuthRepository() as AuthRepository,
child: Provider.value(
value: FakeItineraryConfigRepository() as ItineraryConfigRepository,
child: HomeScreen(viewModel: viewModel),
),
),
goRouter: goRouter,
);
}
testWidgets('should load screen', (tester) async {
await loadWidget(tester);
await tester.pumpAndSettle();
expect(find.byType(HomeScreen), findsOneWidget);
});
testWidgets('should show user name', (tester) async {
await loadWidget(tester);
await tester.pumpAndSettle();
expect(find.text('NAME\'s Trips'), findsOneWidget);
});
testWidgets('should navigate to search', (tester) async {
await loadWidget(tester);
await tester.pumpAndSettle();
// Tap on create a booking FAB
await tester.tap(find.byKey(const ValueKey('booking-button')));
await tester.pumpAndSettle();
// Should navigate to results screen
verify(() => goRouter.go(Routes.search)).called(1);
});
testWidgets('should open existing booking', (tester) async {
await loadWidget(tester);
await tester.pumpAndSettle();
// Tap on booking (created from kBooking)
await tester.tap(find.text('name1, Europe'));
await tester.pumpAndSettle();
// Should navigate to results screen
verify(() => goRouter.push(Routes.bookingWithId(0))).called(1);
});
testWidgets('should delete booking', (tester) async {
await loadWidget(tester);
await tester.pumpAndSettle();
// Swipe on booking (created from kBooking)
await tester.drag(find.text('name1, Europe'), const Offset(-1000, 0));
await tester.pumpAndSettle();
// Existing booking should be gone
expect(find.text('name1, Europe'), findsNothing);
// Booking should be deleted from repository
expect(bookingRepository.bookings, isEmpty);
});
testWidgets('fail to delete booking', (tester) async {
// Create a ViewModel with a repository that will fail to delete
viewModel = HomeViewModel(
bookingRepository: _BadFakeBookingRepository()..createBooking(kBooking),
userRepository: FakeUserRepository(),
);
await loadWidget(tester);
await tester.pumpAndSettle();
// Swipe on booking (created from kBooking)
await tester.drag(find.text('name1, Europe'), const Offset(-1000, 0));
await tester.pumpAndSettle();
// Existing booking should be there
expect(find.text('name1, Europe'), findsOneWidget);
});
});
}
class _BadFakeBookingRepository extends FakeBookingRepository {
@override
Future<Result<void>> delete(int id) async {
return Result.error(Exception('Failed to delete booking'));
}
}

View File

@@ -0,0 +1,80 @@
// Copyright 2024 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:compass_app/domain/models/itinerary_config/itinerary_config.dart';
import 'package:compass_app/ui/results/view_models/results_viewmodel.dart';
import 'package:compass_app/ui/results/widgets/results_screen.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:mocktail_image_network/mocktail_image_network.dart';
import '../../../testing/app.dart';
import '../../../testing/fakes/repositories/fake_destination_repository.dart';
import '../../../testing/fakes/repositories/fake_itinerary_config_repository.dart';
import '../../../testing/mocks.dart';
void main() {
group('ResultsScreen widget tests', () {
late MockGoRouter goRouter;
late ResultsViewModel viewModel;
setUp(() {
viewModel = ResultsViewModel(
destinationRepository: FakeDestinationRepository(),
itineraryConfigRepository: FakeItineraryConfigRepository(
itineraryConfig: ItineraryConfig(
continent: 'Europe',
startDate: DateTime(2024, 01, 01),
endDate: DateTime(2024, 01, 31),
guests: 2,
),
),
);
goRouter = MockGoRouter();
});
Future<void> loadScreen(WidgetTester tester) async {
await testApp(
tester,
ResultsScreen(viewModel: viewModel),
goRouter: goRouter,
);
}
testWidgets('should load screen', (WidgetTester tester) async {
await mockNetworkImages(() async {
await loadScreen(tester);
expect(find.byType(ResultsScreen), findsOneWidget);
});
});
testWidgets('should display destination', (WidgetTester tester) async {
await mockNetworkImages(() async {
await loadScreen(tester);
// Wait for list to load
await tester.pumpAndSettle();
// Note: Name is converted to uppercase
expect(find.text('NAME1'), findsOneWidget);
expect(find.text('tags1'), findsOneWidget);
});
});
testWidgets('should tap and navigate to activities',
(WidgetTester tester) async {
await mockNetworkImages(() async {
await loadScreen(tester);
// Wait for list to load
await tester.pumpAndSettle();
// warnIfMissed false because false negative
await tester.tap(find.text('NAME1'), warnIfMissed: false);
verify(() => goRouter.go('/activities')).called(1);
});
});
});
}

View File

@@ -0,0 +1,32 @@
// Copyright 2024 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:compass_app/domain/models/itinerary_config/itinerary_config.dart';
import 'package:compass_app/ui/results/view_models/results_viewmodel.dart';
import 'package:flutter_test/flutter_test.dart';
import '../../../testing/fakes/repositories/fake_destination_repository.dart';
import '../../../testing/fakes/repositories/fake_itinerary_config_repository.dart';
void main() {
group('ResultsViewModel tests', () {
final viewModel = ResultsViewModel(
destinationRepository: FakeDestinationRepository(),
itineraryConfigRepository: FakeItineraryConfigRepository(
itineraryConfig: ItineraryConfig(
continent: 'Europe',
startDate: DateTime(2024, 01, 01),
endDate: DateTime(2024, 01, 31),
guests: 2,
),
),
);
// perform a simple test
// verifies that the list of items is properly loaded
test('should load items', () async {
expect(viewModel.destinations.length, 2);
});
});
}

View File

@@ -0,0 +1,73 @@
// Copyright 2024 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:compass_app/ui/search_form/view_models/search_form_viewmodel.dart';
import '../../../../testing/fakes/repositories/fake_continent_repository.dart';
import '../../../../testing/fakes/repositories/fake_itinerary_config_repository.dart';
void main() {
group('SearchFormViewModel Tests', () {
late SearchFormViewModel viewModel;
setUp(() {
viewModel = SearchFormViewModel(
continentRepository: FakeContinentRepository(),
itineraryConfigRepository: FakeItineraryConfigRepository(),
);
});
test('Initial values are correct', () {
expect(viewModel.valid, false);
expect(viewModel.selectedContinent, null);
expect(viewModel.dateRange, null);
expect(viewModel.guests, 0);
});
test('Setting dateRange updates correctly', () {
final DateTimeRange newDateRange = DateTimeRange(
start: DateTime(2024, 1, 1),
end: DateTime(2024, 1, 31),
);
viewModel.dateRange = newDateRange;
expect(viewModel.dateRange, newDateRange);
});
test('Setting selectedContinent updates correctly', () {
viewModel.selectedContinent = 'CONTINENT';
expect(viewModel.selectedContinent, 'CONTINENT');
// Setting null should work
viewModel.selectedContinent = null;
expect(viewModel.selectedContinent, null);
});
test('Setting guests updates correctly', () {
viewModel.guests = 2;
expect(viewModel.guests, 2);
// Guests number should not be negative
viewModel.guests = -1;
expect(viewModel.guests, 0);
});
test('Set all values and save', () async {
expect(viewModel.valid, false);
viewModel.guests = 2;
viewModel.selectedContinent = 'CONTINENT';
final DateTimeRange newDateRange = DateTimeRange(
start: DateTime(2024, 1, 1),
end: DateTime(2024, 1, 31),
);
viewModel.dateRange = newDateRange;
expect(viewModel.valid, true);
await viewModel.updateItineraryConfig.execute();
expect(viewModel.updateItineraryConfig.completed, true);
});
});
}

View File

@@ -0,0 +1,39 @@
// Copyright 2024 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:compass_app/ui/search_form/view_models/search_form_viewmodel.dart';
import 'package:compass_app/ui/search_form/widgets/search_form_continent.dart';
import 'package:flutter_test/flutter_test.dart';
import '../../../../testing/app.dart';
import '../../../../testing/fakes/repositories/fake_continent_repository.dart';
import '../../../../testing/fakes/repositories/fake_itinerary_config_repository.dart';
void main() {
group('SearchFormContinent widget tests', () {
late SearchFormViewModel viewModel;
setUp(() {
viewModel = SearchFormViewModel(
continentRepository: FakeContinentRepository(),
itineraryConfigRepository: FakeItineraryConfigRepository(),
);
});
loadWidget(WidgetTester tester) async {
await testApp(tester, SearchFormContinent(viewModel: viewModel));
}
testWidgets('Should load and select continent',
(WidgetTester tester) async {
await loadWidget(tester);
expect(find.byType(SearchFormContinent), findsOneWidget);
// Select continent
await tester.tap(find.text('CONTINENT'), warnIfMissed: false);
expect(viewModel.selectedContinent, 'CONTINENT');
});
});
}

View File

@@ -0,0 +1,61 @@
// Copyright 2024 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:compass_app/ui/search_form/view_models/search_form_viewmodel.dart';
import 'package:compass_app/ui/search_form/widgets/search_form_date.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
import '../../../../testing/app.dart';
import '../../../../testing/fakes/repositories/fake_continent_repository.dart';
import '../../../../testing/fakes/repositories/fake_itinerary_config_repository.dart';
void main() {
group('SearchFormDate widget tests', () {
late SearchFormViewModel viewModel;
setUp(() {
viewModel = SearchFormViewModel(
continentRepository: FakeContinentRepository(),
itineraryConfigRepository: FakeItineraryConfigRepository(),
);
});
loadWidget(WidgetTester tester) async {
await testApp(tester, SearchFormDate(viewModel: viewModel));
}
testWidgets('should display date in different month',
(WidgetTester tester) async {
await loadWidget(tester);
expect(find.byType(SearchFormDate), findsOneWidget);
// Initial state
expect(find.text('Add Dates'), findsOneWidget);
// Simulate date picker input:
viewModel.dateRange = DateTimeRange(
start: DateTime(2024, 6, 12), end: DateTime(2024, 7, 23));
await tester.pumpAndSettle();
expect(find.text('12 Jun - 23 Jul'), findsOneWidget);
});
testWidgets('should display date in same month',
(WidgetTester tester) async {
await loadWidget(tester);
expect(find.byType(SearchFormDate), findsOneWidget);
// Initial state
expect(find.text('Add Dates'), findsOneWidget);
// Simulate date picker input:
viewModel.dateRange = DateTimeRange(
start: DateTime(2024, 6, 12), end: DateTime(2024, 6, 23));
await tester.pumpAndSettle();
expect(find.text('12 - 23 Jun'), findsOneWidget);
});
});
}

View File

@@ -0,0 +1,68 @@
// Copyright 2024 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:compass_app/ui/search_form/view_models/search_form_viewmodel.dart';
import 'package:compass_app/ui/search_form/widgets/search_form_guests.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
import '../../../../testing/app.dart';
import '../../../../testing/fakes/repositories/fake_continent_repository.dart';
import '../../../../testing/fakes/repositories/fake_itinerary_config_repository.dart';
void main() {
group('SearchFormGuests widget tests', () {
late SearchFormViewModel viewModel;
setUp(() {
viewModel = SearchFormViewModel(
continentRepository: FakeContinentRepository(),
itineraryConfigRepository: FakeItineraryConfigRepository(),
);
});
loadWidget(WidgetTester tester) async {
await testApp(tester, SearchFormGuests(viewModel: viewModel));
}
testWidgets('Increase number of guests', (WidgetTester tester) async {
await loadWidget(tester);
expect(find.byType(SearchFormGuests), findsOneWidget);
// Initial state
expect(find.text('0'), findsOneWidget);
await tester.tap(find.byKey(const ValueKey(addGuestsKey)));
await tester.pumpAndSettle();
expect(find.text('1'), findsOneWidget);
});
testWidgets('Decrease number of guests', (WidgetTester tester) async {
await loadWidget(tester);
expect(find.byType(SearchFormGuests), findsOneWidget);
// Initial state
expect(find.text('0'), findsOneWidget);
await tester.tap(find.byKey(const ValueKey(removeGuestsKey)));
await tester.pumpAndSettle();
// Should remain at 0
expect(find.text('0'), findsOneWidget);
await tester.tap(find.byKey(const ValueKey(addGuestsKey)));
await tester.pumpAndSettle();
// Increase to 1
expect(find.text('1'), findsOneWidget);
await tester.tap(find.byKey(const ValueKey(removeGuestsKey)));
await tester.pumpAndSettle();
// Back to 0
expect(find.text('0'), findsOneWidget);
});
});
}

View File

@@ -0,0 +1,74 @@
// Copyright 2024 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:compass_app/data/repositories/auth/auth_repository.dart';
import 'package:compass_app/data/repositories/itinerary_config/itinerary_config_repository.dart';
import 'package:compass_app/ui/search_form/view_models/search_form_viewmodel.dart';
import 'package:compass_app/ui/search_form/widgets/search_form_guests.dart';
import 'package:compass_app/ui/search_form/widgets/search_form_screen.dart';
import 'package:compass_app/ui/search_form/widgets/search_form_submit.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:provider/provider.dart';
import '../../../../testing/app.dart';
import '../../../../testing/fakes/repositories/fake_auth_repository.dart';
import '../../../../testing/fakes/repositories/fake_continent_repository.dart';
import '../../../../testing/fakes/repositories/fake_itinerary_config_repository.dart';
import '../../../../testing/mocks.dart';
void main() {
group('SearchFormScreen widget tests', () {
late SearchFormViewModel viewModel;
late MockGoRouter goRouter;
setUp(() {
viewModel = SearchFormViewModel(
continentRepository: FakeContinentRepository(),
itineraryConfigRepository: FakeItineraryConfigRepository(),
);
goRouter = MockGoRouter();
});
loadWidget(WidgetTester tester) async {
await testApp(
tester,
ChangeNotifierProvider.value(
value: FakeAuthRepository() as AuthRepository,
child: Provider.value(
value: FakeItineraryConfigRepository() as ItineraryConfigRepository,
child: SearchFormScreen(viewModel: viewModel),
),
),
goRouter: goRouter,
);
}
testWidgets('Should fill form and perform search',
(WidgetTester tester) async {
await loadWidget(tester);
expect(find.byType(SearchFormScreen), findsOneWidget);
// Select continent
await tester.tap(find.text('CONTINENT'), warnIfMissed: false);
// Select date
viewModel.dateRange = DateTimeRange(
start: DateTime(2024, 6, 12), end: DateTime(2024, 7, 23));
// Select guests
await tester.tap(find.byKey(const ValueKey(addGuestsKey)));
// Refresh screen state
await tester.pumpAndSettle();
// Perform search
await tester.tap(find.byKey(const ValueKey(searchFormSubmitButtonKey)));
// Should navigate to results screen
verify(() => goRouter.go('/results')).called(1);
});
});
}

View File

@@ -0,0 +1,62 @@
// Copyright 2024 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:compass_app/ui/search_form/view_models/search_form_viewmodel.dart';
import 'package:compass_app/ui/search_form/widgets/search_form_submit.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import '../../../../testing/app.dart';
import '../../../../testing/fakes/repositories/fake_continent_repository.dart';
import '../../../../testing/fakes/repositories/fake_itinerary_config_repository.dart';
import '../../../../testing/mocks.dart';
void main() {
group('SearchFormSubmit widget tests', () {
late SearchFormViewModel viewModel;
late MockGoRouter goRouter;
setUp(() {
viewModel = SearchFormViewModel(
continentRepository: FakeContinentRepository(),
itineraryConfigRepository: FakeItineraryConfigRepository(),
);
goRouter = MockGoRouter();
});
loadWidget(WidgetTester tester) async {
await testApp(
tester,
SearchFormSubmit(viewModel: viewModel),
goRouter: goRouter,
);
}
testWidgets('Should be enabled and allow tap', (WidgetTester tester) async {
await loadWidget(tester);
expect(find.byType(SearchFormSubmit), findsOneWidget);
// Tap should not navigate
await tester.tap(find.byKey(const ValueKey(searchFormSubmitButtonKey)));
verifyNever(() => goRouter.go(any()));
// Fill in data
viewModel.guests = 2;
viewModel.selectedContinent = 'CONTINENT';
final DateTimeRange newDateRange = DateTimeRange(
start: DateTime(2024, 1, 1),
end: DateTime(2024, 1, 31),
);
viewModel.dateRange = newDateRange;
await tester.pumpAndSettle();
// Perform search
await tester.tap(find.byKey(const ValueKey(searchFormSubmitButtonKey)));
// Should navigate to results screen
verify(() => goRouter.go('/results')).called(1);
});
});
}