onedirve and google drive

This commit is contained in:
Courtney Arnold 2026-08-14 17:30:31 -05:00
parent a9bcec09b1
commit 901ec678a3
12 changed files with 373 additions and 29 deletions

View file

@ -0,0 +1,104 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart';
import 'package:fuel_tax_tracker/models/vehicle.dart';
import 'package:fuel_tax_tracker/screens/add_edit_vehicle_screen.dart';
import 'package:fuel_tax_tracker/screens/vehicle_detail_screen.dart';
import 'package:fuel_tax_tracker/services/app_state.dart';
/// Covers the "VIN already belongs to another vehicle" dialog on the Add
/// Vehicle form — [AddEditVehicleScreen] driven directly against an
/// [AppState] whose in-memory `vehicles` list is seeded by hand (no
/// database/cloud wiring needed, since the dialog only reads
/// [AppState.vehicleByVin]).
void main() {
final existingVehicle = Vehicle(
id: 'existing-id',
vin: '1FMPU18L1TLB51349',
nickname: 'Red Truck',
updatedAt: DateTime.utc(2026, 1, 1),
);
Future<AppState> pumpAddVehicleScreen(WidgetTester tester) async {
final appState = AppState()
..isLoading = false
..vehicles = [existingVehicle];
await tester.pumpWidget(
ChangeNotifierProvider.value(
value: appState,
child: const MaterialApp(home: AddEditVehicleScreen()),
),
);
return appState;
}
testWidgets('entering a VIN that already exists shows the dialog', (tester) async {
await pumpAddVehicleScreen(tester);
await tester.enterText(find.widgetWithText(TextFormField, 'VIN *'), existingVehicle.vin);
await tester.pumpAndSettle();
expect(find.text('Vehicle already exists'), findsOneWidget);
expect(
find.text(
'Vehicle with the VIN ${existingVehicle.vin} already exists, would you like to open that vehicle?',
),
findsOneWidget,
);
});
testWidgets('tapping No dismisses the dialog and stays on the create screen', (tester) async {
await pumpAddVehicleScreen(tester);
await tester.enterText(find.widgetWithText(TextFormField, 'VIN *'), existingVehicle.vin);
await tester.pumpAndSettle();
await tester.tap(find.widgetWithText(TextButton, 'No'));
await tester.pumpAndSettle();
expect(find.text('Vehicle already exists'), findsNothing);
expect(find.text('Add Vehicle'), findsWidgets);
expect(
find.text('A vehicle with VIN "${existingVehicle.vin}" already exists.'),
findsOneWidget,
reason: 'the field itself should be flagged invalid after declining to open the existing vehicle',
);
});
testWidgets('tapping Yes opens the existing vehicle', (tester) async {
await pumpAddVehicleScreen(tester);
await tester.enterText(find.widgetWithText(TextFormField, 'VIN *'), existingVehicle.vin);
await tester.pumpAndSettle();
await tester.tap(find.widgetWithText(TextButton, 'Yes'));
await tester.pumpAndSettle();
expect(find.byType(VehicleDetailScreen), findsOneWidget);
expect(find.text(existingVehicle.displayLabel), findsOneWidget);
expect(find.byType(AddEditVehicleScreen), findsNothing,
reason: 'the create screen should be popped, not left underneath');
});
testWidgets('editing a vehicle with its own VIN does not trigger the dialog', (tester) async {
final appState = AppState()
..isLoading = false
..vehicles = [existingVehicle];
await tester.pumpWidget(
ChangeNotifierProvider.value(
value: appState,
child: MaterialApp(home: AddEditVehicleScreen(vehicle: existingVehicle)),
),
);
// The field already starts pre-filled with its own VIN; nudge it to
// fire onChanged the same way a real edit would.
await tester.enterText(find.widgetWithText(TextFormField, 'VIN *'), existingVehicle.vin);
await tester.pumpAndSettle();
expect(find.text('Vehicle already exists'), findsNothing);
});
}

View file

@ -89,6 +89,31 @@ void main() {
'app-folder-id/$receiptsFolderName/1FMPU18L1TLB51349/2026.03');
});
test('selectAppFolder creates/finds the app folder under the picked parent', () async {
final session = _FakeSession();
final provider = _FakeProvider(session);
final syncService = CloudSyncService(provider: provider, databaseService: databaseService);
final folderId = await syncService.selectAppFolder('parent-id');
expect(folderId, 'parent-id/$appFolderName');
expect(session.findOrCreateFolderCalls, ['parent-id/$appFolderName']);
});
test('selectAppFolder uses the picked folder directly if it is already the app folder',
() async {
final session = _FakeSession();
final provider = _FakeProvider(session);
final syncService = CloudSyncService(provider: provider, databaseService: databaseService);
final folderId =
await syncService.selectAppFolder('parent-id/$appFolderName', currentFolderName: appFolderName);
expect(folderId, 'parent-id/$appFolderName');
expect(session.findOrCreateFolderCalls, isEmpty,
reason: 'should not nest another app folder inside a folder already named that');
});
test('syncNow is a no-op when not configured with a folder yet', () async {
final session = _FakeSession();
final provider = _FakeProvider(session);
@ -117,6 +142,7 @@ class _FakeSession implements CloudStorageSession {
final List<String> deletedFileIds = [];
final List<String> uploadedFileNames = [];
final Map<String, String> uploadedFileFolders = {};
final List<String> findOrCreateFolderCalls = [];
CloudFileInfo? existingDataFile;
@override
@ -126,8 +152,10 @@ class _FakeSession implements CloudStorageSession {
Future<List<CloudFolder>> listFolders({String? parentId, bool sharedWithMe = false}) async => [];
@override
Future<String> findOrCreateFolder({required String parentId, required String name}) async =>
'$parentId/$name';
Future<String> findOrCreateFolder({required String parentId, required String name}) async {
findOrCreateFolderCalls.add('$parentId/$name');
return '$parentId/$name';
}
@override
Future<CloudFileInfo?> findFile({required String folderId, required String name}) async {