mirror of
https://github.com/flutter/samples.git
synced 2025-11-08 13:58:47 +00:00
Compass app (#2446)
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
// 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/activity/activity_repository_local.dart';
|
||||
import 'package:compass_app/data/services/local/local_data_service.dart';
|
||||
import 'package:compass_app/utils/result.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
group('ActivityRepositoryLocal tests', () {
|
||||
// To load assets
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
final repository = ActivityRepositoryLocal(
|
||||
localDataService: LocalDataService(),
|
||||
);
|
||||
|
||||
test('should get by destination ref', () async {
|
||||
final result = await repository.getByDestination('alaska');
|
||||
expect(result, isA<Ok>());
|
||||
|
||||
final list = result.asOk.value;
|
||||
expect(list.length, 20);
|
||||
|
||||
final activity = list.first;
|
||||
expect(activity.name, 'Glacier Trekking and Ice Climbing');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// 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/activity/activity_repository.dart';
|
||||
import 'package:compass_app/data/repositories/activity/activity_repository_remote.dart';
|
||||
import 'package:compass_app/utils/result.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../../../../testing/fakes/services/fake_api_client.dart';
|
||||
|
||||
void main() {
|
||||
group('ActivityRepositoryRemote tests', () {
|
||||
late FakeApiClient apiClient;
|
||||
late ActivityRepository repository;
|
||||
|
||||
setUp(() {
|
||||
apiClient = FakeApiClient();
|
||||
repository = ActivityRepositoryRemote(apiClient: apiClient);
|
||||
});
|
||||
|
||||
test('should get activities for destination', () async {
|
||||
final result = await repository.getByDestination('alaska');
|
||||
expect(result, isA<Ok>());
|
||||
|
||||
final list = result.asOk.value;
|
||||
expect(list.length, 1);
|
||||
|
||||
final destination = list.first;
|
||||
expect(destination.name, 'Glacier Trekking and Ice Climbing');
|
||||
|
||||
// Only one request happened
|
||||
expect(apiClient.requestCount, 1);
|
||||
});
|
||||
|
||||
test('should get destinations from cache', () async {
|
||||
// Request destination once
|
||||
var result = await repository.getByDestination('alaska');
|
||||
expect(result, isA<Ok>());
|
||||
|
||||
// Request destination another time
|
||||
result = await repository.getByDestination('alaska');
|
||||
expect(result, isA<Ok>());
|
||||
|
||||
// Only one request happened
|
||||
expect(apiClient.requestCount, 1);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// 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_remote.dart';
|
||||
import 'package:compass_app/utils/result.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../../../../testing/fakes/services/fake_api_client.dart';
|
||||
import '../../../../testing/fakes/services/fake_auth_api_client.dart';
|
||||
import '../../../../testing/fakes/services/fake_shared_preferences_service.dart';
|
||||
|
||||
void main() {
|
||||
group('AuthRepositoryRemote tests', () {
|
||||
late FakeApiClient apiClient;
|
||||
late FakeAuthApiClient authApiClient;
|
||||
late FakeSharedPreferencesService sharedPreferencesService;
|
||||
late AuthRepositoryRemote repository;
|
||||
|
||||
setUp(() {
|
||||
apiClient = FakeApiClient();
|
||||
authApiClient = FakeAuthApiClient();
|
||||
sharedPreferencesService = FakeSharedPreferencesService();
|
||||
repository = AuthRepositoryRemote(
|
||||
apiClient: apiClient,
|
||||
authApiClient: authApiClient,
|
||||
sharedPreferencesService: sharedPreferencesService,
|
||||
);
|
||||
});
|
||||
|
||||
test('fetch on start, has token', () async {
|
||||
// Stored token in shared preferences
|
||||
sharedPreferencesService.token = 'TOKEN';
|
||||
|
||||
// Create an AuthRepository, should perform initial fetch
|
||||
final repository = AuthRepositoryRemote(
|
||||
apiClient: apiClient,
|
||||
authApiClient: authApiClient,
|
||||
sharedPreferencesService: sharedPreferencesService,
|
||||
);
|
||||
|
||||
final isAuthenticated = await repository.isAuthenticated;
|
||||
|
||||
// True because Token is SharedPreferences
|
||||
expect(isAuthenticated, isTrue);
|
||||
|
||||
// Check auth token
|
||||
await expectAuthHeader(apiClient, 'Bearer TOKEN');
|
||||
});
|
||||
|
||||
test('fetch on start, no token', () async {
|
||||
// Stored token in shared preferences
|
||||
sharedPreferencesService.token = null;
|
||||
|
||||
// Create an AuthRepository, should perform initial fetch
|
||||
final repository = AuthRepositoryRemote(
|
||||
apiClient: apiClient,
|
||||
authApiClient: authApiClient,
|
||||
sharedPreferencesService: sharedPreferencesService,
|
||||
);
|
||||
|
||||
final isAuthenticated = await repository.isAuthenticated;
|
||||
|
||||
// True because Token is SharedPreferences
|
||||
expect(isAuthenticated, isFalse);
|
||||
|
||||
// Check auth token
|
||||
await expectAuthHeader(apiClient, null);
|
||||
});
|
||||
|
||||
test('perform login', () async {
|
||||
final result = await repository.login(
|
||||
email: 'EMAIL',
|
||||
password: 'PASSWORD',
|
||||
);
|
||||
expect(result, isA<Ok>());
|
||||
expect(await repository.isAuthenticated, isTrue);
|
||||
expect(sharedPreferencesService.token, 'TOKEN');
|
||||
|
||||
// Check auth token
|
||||
await expectAuthHeader(apiClient, 'Bearer TOKEN');
|
||||
});
|
||||
|
||||
test('perform logout', () async {
|
||||
// logged in status
|
||||
sharedPreferencesService.token = 'TOKEN';
|
||||
|
||||
final result = await repository.logout();
|
||||
expect(result, isA<Ok>());
|
||||
expect(await repository.isAuthenticated, isFalse);
|
||||
expect(sharedPreferencesService.token, null);
|
||||
|
||||
// Check auth token
|
||||
await expectAuthHeader(apiClient, null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> expectAuthHeader(FakeApiClient apiClient, String? header) async {
|
||||
final header = apiClient.authHeaderProvider?.call();
|
||||
expect(header, header);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// 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/booking/booking_repository.dart';
|
||||
import 'package:compass_app/data/repositories/booking/booking_repository_remote.dart';
|
||||
import 'package:compass_app/utils/result.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../../../../testing/fakes/services/fake_api_client.dart';
|
||||
import '../../../../testing/models/booking.dart';
|
||||
|
||||
void main() {
|
||||
group('BookingRepositoryRemote tests', () {
|
||||
late BookingRepository bookingRepository;
|
||||
late FakeApiClient fakeApiClient;
|
||||
|
||||
setUp(() {
|
||||
fakeApiClient = FakeApiClient();
|
||||
bookingRepository = BookingRepositoryRemote(
|
||||
apiClient: fakeApiClient,
|
||||
);
|
||||
});
|
||||
|
||||
test('should get booking', () async {
|
||||
final result = await bookingRepository.getBooking(0);
|
||||
final booking = result.asOk.value;
|
||||
expect(booking, kBooking.copyWith(id: 0));
|
||||
});
|
||||
|
||||
test('should create booking', () async {
|
||||
expect(fakeApiClient.bookings, isEmpty);
|
||||
final result = await bookingRepository.createBooking(kBooking);
|
||||
expect(result, isA<Ok<void>>());
|
||||
expect(fakeApiClient.bookings.first, kBookingApiModel);
|
||||
});
|
||||
|
||||
test('should get list of booking', () async {
|
||||
final result = await bookingRepository.getBookingsList();
|
||||
final list = result.asOk.value;
|
||||
expect(list, [kBookingSummary]);
|
||||
});
|
||||
|
||||
test('should delete booking', () async {
|
||||
// Ensure no bookings exist
|
||||
expect(fakeApiClient.bookings, isEmpty);
|
||||
|
||||
// Add a booking
|
||||
var result = await bookingRepository.createBooking(kBooking);
|
||||
expect(result, isA<Ok<void>>());
|
||||
|
||||
// Delete the booking
|
||||
result = await bookingRepository.delete(0);
|
||||
expect(result, isA<Ok<void>>());
|
||||
|
||||
// Check if the booking was deleted from the server
|
||||
expect(fakeApiClient.bookings, isEmpty);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// 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/continent/continent_repository.dart';
|
||||
import 'package:compass_app/data/repositories/continent/continent_repository_remote.dart';
|
||||
import 'package:compass_app/utils/result.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../../../../testing/fakes/services/fake_api_client.dart';
|
||||
|
||||
void main() {
|
||||
group('ContinentRepositoryRemote tests', () {
|
||||
late FakeApiClient apiClient;
|
||||
late ContinentRepository repository;
|
||||
|
||||
setUp(() {
|
||||
apiClient = FakeApiClient();
|
||||
repository = ContinentRepositoryRemote(apiClient: apiClient);
|
||||
});
|
||||
|
||||
test('should get continents', () async {
|
||||
final result = await repository.getContinents();
|
||||
expect(result, isA<Ok>());
|
||||
|
||||
final list = result.asOk.value;
|
||||
expect(list.length, 3);
|
||||
|
||||
final destination = list.first;
|
||||
expect(destination.name, 'CONTINENT');
|
||||
|
||||
// Only one request happened
|
||||
expect(apiClient.requestCount, 1);
|
||||
});
|
||||
|
||||
test('should get continents from cache', () async {
|
||||
// Request continents once
|
||||
var result = await repository.getContinents();
|
||||
expect(result, isA<Ok>());
|
||||
|
||||
// Request continents another time
|
||||
result = await repository.getContinents();
|
||||
expect(result, isA<Ok>());
|
||||
|
||||
// Only one request happened
|
||||
expect(apiClient.requestCount, 1);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// 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/services/local/local_data_service.dart';
|
||||
import 'package:compass_app/utils/result.dart';
|
||||
import 'package:compass_app/data/repositories/destination/destination_repository_local.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
group('DestinationRepositoryLocal tests', () {
|
||||
// To load assets
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
final repository = DestinationRepositoryLocal(
|
||||
localDataService: LocalDataService(),
|
||||
);
|
||||
|
||||
test('should load and parse', () async {
|
||||
// Should load the json and parse it
|
||||
final result = await repository.getDestinations();
|
||||
expect(result, isA<Ok>());
|
||||
|
||||
// Check that the list is complete
|
||||
final list = result.asOk.value;
|
||||
expect(list.length, 137);
|
||||
|
||||
// Check first item
|
||||
final destination = list.first;
|
||||
expect(destination.name, 'Alaska');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// 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/destination/destination_repository.dart';
|
||||
import 'package:compass_app/data/repositories/destination/destination_repository_remote.dart';
|
||||
import 'package:compass_app/utils/result.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../../../../testing/fakes/services/fake_api_client.dart';
|
||||
|
||||
void main() {
|
||||
group('DestinationRepositoryRemote tests', () {
|
||||
late FakeApiClient apiClient;
|
||||
late DestinationRepository repository;
|
||||
|
||||
setUp(() {
|
||||
apiClient = FakeApiClient();
|
||||
repository = DestinationRepositoryRemote(apiClient: apiClient);
|
||||
});
|
||||
|
||||
test('should get destinations', () async {
|
||||
final result = await repository.getDestinations();
|
||||
expect(result, isA<Ok>());
|
||||
|
||||
final list = result.asOk.value;
|
||||
expect(list.length, 2);
|
||||
|
||||
final destination = list.first;
|
||||
expect(destination.name, 'name1');
|
||||
|
||||
// Only one request happened
|
||||
expect(apiClient.requestCount, 1);
|
||||
});
|
||||
|
||||
test('should get destinations from cache', () async {
|
||||
// Request destination once
|
||||
var result = await repository.getDestinations();
|
||||
expect(result, isA<Ok>());
|
||||
|
||||
// Request destination another time
|
||||
result = await repository.getDestinations();
|
||||
expect(result, isA<Ok>());
|
||||
|
||||
// Only one request happened
|
||||
expect(apiClient.requestCount, 1);
|
||||
});
|
||||
});
|
||||
}
|
||||
83
compass_app/app/test/data/services/api/api_client_test.dart
Normal file
83
compass_app/app/test/data/services/api/api_client_test.dart
Normal 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/data/services/api/api_client.dart';
|
||||
import 'package:compass_app/domain/models/continent/continent.dart';
|
||||
import 'package:compass_app/utils/result.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../../../../testing/mocks.dart';
|
||||
import '../../../../testing/models/activity.dart';
|
||||
import '../../../../testing/models/booking.dart';
|
||||
import '../../../../testing/models/destination.dart';
|
||||
import '../../../../testing/models/user.dart';
|
||||
|
||||
void main() {
|
||||
group('ApiClient', () {
|
||||
late MockHttpClient mockHttpClient;
|
||||
late ApiClient apiClient;
|
||||
|
||||
setUp(() {
|
||||
mockHttpClient = MockHttpClient();
|
||||
apiClient = ApiClient(clientFactory: () => mockHttpClient);
|
||||
});
|
||||
|
||||
test('should get continents', () async {
|
||||
final continents = [const Continent(name: 'NAME', imageUrl: 'URL')];
|
||||
mockHttpClient.mockGet('/continent', continents);
|
||||
final result = await apiClient.getContinents();
|
||||
expect(result.asOk.value, continents);
|
||||
});
|
||||
|
||||
test('should get activities by destination', () async {
|
||||
final activites = [kActivity];
|
||||
mockHttpClient.mockGet(
|
||||
'/destination/${kDestination1.ref}/activity',
|
||||
activites,
|
||||
);
|
||||
final result =
|
||||
await apiClient.getActivityByDestination(kDestination1.ref);
|
||||
expect(result.asOk.value, activites);
|
||||
});
|
||||
|
||||
test('should get booking', () async {
|
||||
mockHttpClient.mockGet(
|
||||
'/booking/${kBookingApiModel.id}',
|
||||
kBookingApiModel,
|
||||
);
|
||||
final result = await apiClient.getBooking(kBookingApiModel.id!);
|
||||
expect(result.asOk.value, kBookingApiModel);
|
||||
});
|
||||
|
||||
test('should get bookings', () async {
|
||||
mockHttpClient.mockGet('/booking', [kBookingApiModel]);
|
||||
final result = await apiClient.getBookings();
|
||||
expect(result.asOk.value, [kBookingApiModel]);
|
||||
});
|
||||
|
||||
test('should get destinations', () async {
|
||||
mockHttpClient.mockGet('/destination', [kDestination1]);
|
||||
final result = await apiClient.getDestinations();
|
||||
expect(result.asOk.value, [kDestination1]);
|
||||
});
|
||||
|
||||
test('should get user', () async {
|
||||
mockHttpClient.mockGet('/user', userApiModel);
|
||||
final result = await apiClient.getUser();
|
||||
expect(result.asOk.value, userApiModel);
|
||||
});
|
||||
|
||||
test('should post booking', () async {
|
||||
mockHttpClient.mockPost('/booking', kBookingApiModel);
|
||||
final result = await apiClient.postBooking(kBookingApiModel);
|
||||
expect(result.asOk.value, kBookingApiModel);
|
||||
});
|
||||
|
||||
test('should delete booking', () async {
|
||||
mockHttpClient.mockDelete('/booking/0');
|
||||
final result = await apiClient.deleteBooking(0);
|
||||
expect(result, isA<Ok<void>>());
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// 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/services/api/auth_api_client.dart';
|
||||
import 'package:compass_app/data/services/api/model/login_request/login_request.dart';
|
||||
import 'package:compass_app/data/services/api/model/login_response/login_response.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../../../../testing/mocks.dart';
|
||||
|
||||
void main() {
|
||||
group('AuthApiClient', () {
|
||||
late MockHttpClient mockHttpClient;
|
||||
late AuthApiClient apiClient;
|
||||
|
||||
setUp(() {
|
||||
mockHttpClient = MockHttpClient();
|
||||
apiClient = AuthApiClient(clientFactory: () => mockHttpClient);
|
||||
});
|
||||
|
||||
test('should post login', () async {
|
||||
const loginResponse = LoginResponse(
|
||||
token: 'TOKEN',
|
||||
userId: '123',
|
||||
);
|
||||
mockHttpClient.mockPost(
|
||||
'/login',
|
||||
loginResponse,
|
||||
200,
|
||||
);
|
||||
final result = await apiClient.login(
|
||||
const LoginRequest(
|
||||
email: 'EMAIL',
|
||||
password: 'PASSWORD',
|
||||
),
|
||||
);
|
||||
expect(result.asOk.value, loginResponse);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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/models/itinerary_config/itinerary_config.dart';
|
||||
import 'package:flutter_test/flutter_test.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/models/activity.dart';
|
||||
import '../../../../testing/models/booking.dart';
|
||||
import '../../../../testing/models/destination.dart';
|
||||
|
||||
void main() {
|
||||
group('BookingCreateUseCase tests', () {
|
||||
test('Create booking', () async {
|
||||
final useCase = BookingCreateUseCase(
|
||||
activityRepository: FakeActivityRepository(),
|
||||
destinationRepository: FakeDestinationRepository(),
|
||||
bookingRepository: FakeBookingRepository(),
|
||||
);
|
||||
|
||||
final booking = await useCase.createFrom(
|
||||
ItineraryConfig(
|
||||
startDate: DateTime(2024, 01, 01),
|
||||
endDate: DateTime(2024, 02, 12),
|
||||
destination: kDestination1.ref,
|
||||
activities: [kActivity.ref],
|
||||
),
|
||||
);
|
||||
|
||||
expect(booking.asOk.value, kBooking);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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_share_use_case.dart';
|
||||
import 'package:compass_app/domain/models/booking/booking.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../../../../testing/models/activity.dart';
|
||||
import '../../../../testing/models/destination.dart';
|
||||
|
||||
void main() {
|
||||
group('BookingShareUseCase tests', () {
|
||||
test('Share booking', () async {
|
||||
String? sharedText;
|
||||
final useCase = BookingShareUseCase.custom((text) async {
|
||||
sharedText = text;
|
||||
});
|
||||
final booking = Booking(
|
||||
startDate: DateTime(2024, 01, 01),
|
||||
endDate: DateTime(2024, 02, 12),
|
||||
destination: kDestination1,
|
||||
activity: [kActivity],
|
||||
);
|
||||
await useCase.shareBooking(booking);
|
||||
expect(
|
||||
sharedText,
|
||||
'Trip to name1\n'
|
||||
'on 1 Jan - 12 Feb\n'
|
||||
'Activities:\n'
|
||||
' - NAME.',
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
63
compass_app/app/test/ui/auth/login_screen_test.dart
Normal file
63
compass_app/app/test/ui/auth/login_screen_test.dart
Normal 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
78
compass_app/app/test/ui/auth/logout_button_test.dart
Normal file
78
compass_app/app/test/ui/auth/logout_button_test.dart
Normal 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(),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
113
compass_app/app/test/ui/booking/booking_screen_test.dart
Normal file
113
compass_app/app/test/ui/booking/booking_screen_test.dart
Normal 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);
|
||||
});
|
||||
});
|
||||
}
|
||||
131
compass_app/app/test/ui/home/widgets/home_screen_test.dart
Normal file
131
compass_app/app/test/ui/home/widgets/home_screen_test.dart
Normal 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'));
|
||||
}
|
||||
}
|
||||
80
compass_app/app/test/ui/results/results_screen_test.dart
Normal file
80
compass_app/app/test/ui/results/results_screen_test.dart
Normal 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
32
compass_app/app/test/ui/results/results_viewmodel_test.dart
Normal file
32
compass_app/app/test/ui/results/results_viewmodel_test.dart
Normal 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);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
102
compass_app/app/test/utils/command_test.dart
Normal file
102
compass_app/app/test/utils/command_test.dart
Normal file
@@ -0,0 +1,102 @@
|
||||
// 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/utils/command.dart';
|
||||
import 'package:compass_app/utils/result.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
group('Command0 tests', () {
|
||||
test('should complete void command', () async {
|
||||
// Void action
|
||||
final command = Command0<void>(() => Future.value(Result.ok(null)));
|
||||
|
||||
// Run void action
|
||||
await command.execute();
|
||||
|
||||
// Action completed
|
||||
expect(command.completed, true);
|
||||
});
|
||||
|
||||
test('should complete bool command', () async {
|
||||
// Action that returns bool
|
||||
final command = Command0<bool>(() => Future.value(Result.ok(true)));
|
||||
|
||||
// Run action with result
|
||||
await command.execute();
|
||||
|
||||
// Action completed
|
||||
expect(command.completed, true);
|
||||
expect(command.result!.asOk.value, true);
|
||||
});
|
||||
|
||||
test('running should be true', () async {
|
||||
final command = Command0<void>(() => Future.value(Result.ok(null)));
|
||||
final future = command.execute();
|
||||
|
||||
// Action is running
|
||||
expect(command.running, true);
|
||||
|
||||
// Await execution
|
||||
await future;
|
||||
|
||||
// Action finished running
|
||||
expect(command.running, false);
|
||||
});
|
||||
|
||||
test('should only run once', () async {
|
||||
int count = 0;
|
||||
final command = Command0<int>(() => Future.value(Result.ok(count++)));
|
||||
final future = command.execute();
|
||||
|
||||
// Run multiple times
|
||||
command.execute();
|
||||
command.execute();
|
||||
command.execute();
|
||||
command.execute();
|
||||
|
||||
// Await execution
|
||||
await future;
|
||||
|
||||
// Action is called once
|
||||
expect(count, 1);
|
||||
});
|
||||
|
||||
test('should handle errors', () async {
|
||||
final command =
|
||||
Command0<int>(() => Future.value(Result.error(Exception('ERROR!'))));
|
||||
await command.execute();
|
||||
expect(command.error, true);
|
||||
expect(command.result, isA<Error>());
|
||||
});
|
||||
});
|
||||
|
||||
group('Command1 tests', () {
|
||||
test('should complete void command, bool argument', () async {
|
||||
// Void action with bool argument
|
||||
final command = Command1<void, bool>((a) {
|
||||
expect(a, true);
|
||||
return Future.value(Result.ok(null));
|
||||
});
|
||||
|
||||
// Run void action, ignore void return
|
||||
await command.execute(true);
|
||||
|
||||
expect(command.completed, true);
|
||||
});
|
||||
|
||||
test('should complete bool command, bool argument', () async {
|
||||
// Action that returns bool argument
|
||||
final command =
|
||||
Command1<bool, bool>((a) => Future.value(Result.ok(true)));
|
||||
|
||||
// Run action with result and argument
|
||||
await command.execute(true);
|
||||
|
||||
// Argument was passed to onComplete
|
||||
expect(command.completed, true);
|
||||
expect(command.result!.asOk.value, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user