No content changes: git diff --ignore-all-space over these files is empty. The churn came from editing on Windows against a repo checked out with LF.
172 lines
5.5 KiB
Dart
172 lines
5.5 KiB
Dart
import 'package:hive/hive.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:app_pfm_ocr_v2/core/storage/local_storage.dart';
|
|
import 'package:app_pfm_ocr_v2/features/camera/camera_drawer.dart';
|
|
import 'package:app_pfm_ocr_v2/main.dart';
|
|
import 'package:app_pfm_ocr_v2/models/document_model.dart';
|
|
|
|
class FakeBox extends Fake implements Box {
|
|
@override
|
|
Future<int> clear() async => 0;
|
|
}
|
|
|
|
class MockLocalStorage extends LocalStorage {
|
|
final List<Map<String, dynamic>> pending;
|
|
|
|
MockLocalStorage({this.pending = const []});
|
|
|
|
@override
|
|
Future<void> init() async {}
|
|
|
|
@override
|
|
Box get documentBox => FakeBox();
|
|
|
|
@override
|
|
Box get pendingDocumentsBox => FakeBox();
|
|
|
|
@override
|
|
List<DocumentModel> getAllDocuments() => [];
|
|
|
|
@override
|
|
List<Map<String, dynamic>> getAllPendingDocuments() => pending;
|
|
|
|
@override
|
|
Future<void> savePendingDocument(Map<String, dynamic> data) async {}
|
|
|
|
@override
|
|
Future<void> removePendingDocument(String id) async {}
|
|
}
|
|
|
|
Map<String, dynamic> _syncFailedPendingDoc(String id) {
|
|
return {
|
|
'id': id,
|
|
'image_path': 'fake_$id.jpg',
|
|
'status': 'syncFailed',
|
|
'document': null,
|
|
'latitude': null,
|
|
'longitude': null,
|
|
'error_message': 'Gagal sinkronisasi ke server: 500 Internal Server Error',
|
|
'created_at': DateTime.now().toIso8601String(),
|
|
'scan_mode': 'DO',
|
|
};
|
|
}
|
|
|
|
Widget _buildTestApp(LocalStorage storage) {
|
|
final router = GoRouter(
|
|
initialLocation: '/camera',
|
|
routes: [
|
|
GoRoute(
|
|
path: '/camera',
|
|
builder: (context, state) => Scaffold(
|
|
appBar: AppBar(),
|
|
drawer: const CameraDrawer(),
|
|
body: Builder(
|
|
builder: (context) => Center(
|
|
child: ElevatedButton(
|
|
onPressed: () => Scaffold.of(context).openDrawer(),
|
|
child: const Text('Open Drawer'),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
GoRoute(path: '/login', builder: (context, state) => const Scaffold(body: Text('LOGIN_SCREEN'))),
|
|
GoRoute(path: '/documents', builder: (context, state) => const Scaffold(body: Text('DOCS_SCREEN'))),
|
|
],
|
|
);
|
|
|
|
return ProviderScope(
|
|
overrides: [localStorageProvider.overrideWithValue(storage)],
|
|
child: MaterialApp.router(routerConfig: router),
|
|
);
|
|
}
|
|
|
|
/// CameraDrawer's mode-toggle row (DO Scan / Product Scan buttons) has a
|
|
/// pre-existing RenderFlex overflow at the fonts/metrics flutter_test falls
|
|
/// back to (unrelated to logout behavior - out of scope for this task).
|
|
/// `TestWidgetsFlutterBinding.runTest` installs its own `FlutterError.onError`
|
|
/// around the test body, so the override must happen inside the body itself
|
|
/// (a `setUp` override gets clobbered) to suppress just that known overflow
|
|
/// while still surfacing anything else.
|
|
Future<void> _ignoringKnownOverflow(Future<void> Function() body) async {
|
|
final original = FlutterError.onError;
|
|
FlutterError.onError = (FlutterErrorDetails details) {
|
|
if (details.exception.toString().contains('A RenderFlex overflowed')) return;
|
|
original?.call(details);
|
|
};
|
|
try {
|
|
await body();
|
|
} finally {
|
|
FlutterError.onError = original;
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
setUp(() {
|
|
SharedPreferences.setMockInitialValues({});
|
|
});
|
|
|
|
testWidgets('logs out immediately when there are no pending documents', (tester) async {
|
|
await _ignoringKnownOverflow(() async {
|
|
await tester.pumpWidget(_buildTestApp(MockLocalStorage()));
|
|
await tester.pumpAndSettle();
|
|
|
|
await tester.tap(find.text('Open Drawer'));
|
|
await tester.pumpAndSettle();
|
|
|
|
await tester.tap(find.text('Logout'));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('LOGIN_SCREEN'), findsOneWidget);
|
|
expect(find.text('Ada Dokumen Belum Tersinkron'), findsNothing);
|
|
});
|
|
});
|
|
|
|
testWidgets('warns and stays put when logout is cancelled with pending documents', (tester) async {
|
|
await _ignoringKnownOverflow(() async {
|
|
final storage = MockLocalStorage(pending: [_syncFailedPendingDoc('1')]);
|
|
await tester.pumpWidget(_buildTestApp(storage));
|
|
await tester.pumpAndSettle();
|
|
|
|
await tester.tap(find.text('Open Drawer'));
|
|
await tester.pumpAndSettle();
|
|
|
|
await tester.tap(find.text('Logout'));
|
|
await tester.pump();
|
|
|
|
expect(find.text('Ada Dokumen Belum Tersinkron'), findsOneWidget);
|
|
|
|
await tester.tap(find.text('Batal'));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('LOGIN_SCREEN'), findsNothing);
|
|
});
|
|
});
|
|
|
|
testWidgets('logs out after confirming the warning with pending documents', (tester) async {
|
|
await _ignoringKnownOverflow(() async {
|
|
final storage = MockLocalStorage(pending: [_syncFailedPendingDoc('1'), _syncFailedPendingDoc('2')]);
|
|
await tester.pumpWidget(_buildTestApp(storage));
|
|
await tester.pumpAndSettle();
|
|
|
|
await tester.tap(find.text('Open Drawer'));
|
|
await tester.pumpAndSettle();
|
|
|
|
await tester.tap(find.text('Logout'));
|
|
await tester.pump();
|
|
|
|
expect(find.text('Ada Dokumen Belum Tersinkron'), findsOneWidget);
|
|
expect(find.textContaining('2 dokumen'), findsOneWidget);
|
|
|
|
await tester.tap(find.text('Ya, Logout'));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('LOGIN_SCREEN'), findsOneWidget);
|
|
});
|
|
});
|
|
}
|