Replace the Inbox badge button in MonthHeader with a bottom-nav tab (hidden when notification parsing is disabled). Redirect /inbox → /home when parsing is off, register the Inbox shell branch unconditionally, and add a settings action to the Inbox screen. Move the parsing tile to the top of the profile screen so settings stay reachable when the tab is hidden. Add tests for the nav tab visibility and inbox routing, plus a release-install PowerShell helper that preserves app data. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
110 lines
4.4 KiB
Dart
110 lines
4.4 KiB
Dart
import 'package:drift/native.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'package:intl/date_symbol_data_local.dart';
|
|
import 'package:new_budget/src/app/app.dart';
|
|
import 'package:new_budget/src/app/router/app_router.dart';
|
|
import 'package:new_budget/src/app/router/app_routes.dart';
|
|
import 'package:new_budget/src/core/database/app_database.dart';
|
|
import 'package:new_budget/src/core/providers/database_provider.dart';
|
|
import 'package:new_budget/src/features/notification_parsing/application/notification_ingest_worker.dart';
|
|
import 'package:new_budget/src/features/notification_parsing/application/parsing_settings_controller.dart';
|
|
import 'package:new_budget/src/features/notification_parsing/application/parsing_worker.dart';
|
|
import 'package:new_budget/src/features/notification_parsing/presentation/screens/inbox_screen.dart';
|
|
|
|
// Воркеры парсинга — no-op: реальные подписываются на connectivity_plus и
|
|
// нативный notification-listener канал, которых под `flutter test` нет.
|
|
class _FakeParsingWorker extends ParsingWorker {
|
|
@override
|
|
void build(String userId) {}
|
|
}
|
|
|
|
class _FakeIngestWorker extends NotificationIngestWorker {
|
|
@override
|
|
void build(String userId) {}
|
|
}
|
|
|
|
void main() {
|
|
setUpAll(() {
|
|
GoogleFonts.config.allowRuntimeFetching = false;
|
|
});
|
|
|
|
testWidgets(
|
|
'вкладка Inbox видна только при включённом парсинге, /inbox редиректит',
|
|
(tester) async {
|
|
await initializeDateFormatting('ru');
|
|
final db = AppDatabase.forTesting(NativeDatabase.memory());
|
|
addTearDown(db.close);
|
|
|
|
// Реальная in-memory БД: активный пользователь + выключенный парсинг.
|
|
await db.usersDao
|
|
.insertUser(UsersTableCompanion.insert(id: 'u1', name: 'Тест'));
|
|
await db.settingsDao.setPreference('active_user_id', 'u1');
|
|
await db.settingsDao.setPreference('parsing_enabled', 'false');
|
|
|
|
final container = ProviderContainer(
|
|
overrides: [
|
|
appDatabaseProvider.overrideWithValue(db),
|
|
parsingWorkerProvider.overrideWith2((_) => _FakeParsingWorker()),
|
|
notificationIngestWorkerProvider
|
|
.overrideWith2((_) => _FakeIngestWorker()),
|
|
],
|
|
);
|
|
addTearDown(container.dispose);
|
|
|
|
await tester.pumpWidget(
|
|
UncontrolledProviderScope(
|
|
container: container,
|
|
child: const NewBudgetApp(),
|
|
),
|
|
);
|
|
// Пара кадров: активный пользователь и настройки парсинга грузятся из БД.
|
|
await tester.pump();
|
|
await tester.pump();
|
|
await tester.pump();
|
|
|
|
// ignore: avoid_print
|
|
for (final t in tester.allWidgets.whereType<Text>()) {
|
|
// ignore: avoid_print
|
|
print('TEXT: ${t.data}');
|
|
}
|
|
|
|
// Парсинг выключен → вкладки Inbox нет, остальные на месте.
|
|
expect(find.text('Inbox'), findsNothing);
|
|
expect(find.text('Home'), findsOneWidget);
|
|
|
|
// Deep-link на /inbox при выключенном парсинге уводит на /home.
|
|
final router = container.read(appRouterProvider);
|
|
router.go(AppRoutes.inbox);
|
|
await tester.pump();
|
|
await tester.pump();
|
|
expect(router.state.matchedLocation, AppRoutes.home);
|
|
expect(find.byType(InboxScreen), findsNothing);
|
|
|
|
// Включаем парсинг — вкладка появляется без пересоздания роутера.
|
|
await container
|
|
.read(parsingSettingsControllerProvider.notifier)
|
|
.setEnabled(true);
|
|
await tester.pump();
|
|
expect(find.text('Inbox'), findsOneWidget);
|
|
|
|
// Теперь /inbox доступен.
|
|
await tester.tap(find.text('Inbox'));
|
|
await tester.pump();
|
|
await tester.pump();
|
|
expect(router.state.matchedLocation, AppRoutes.inbox);
|
|
expect(find.byType(InboxScreen), findsOneWidget);
|
|
|
|
// Выключаем парсинг, находясь НА вкладке Inbox: redirect возвращает
|
|
// на /home (refreshListenable дёргается слушателем настроек).
|
|
await container
|
|
.read(parsingSettingsControllerProvider.notifier)
|
|
.setEnabled(false);
|
|
await tester.pump();
|
|
await tester.pump();
|
|
expect(router.state.matchedLocation, AppRoutes.home);
|
|
expect(find.text('Inbox'), findsNothing);
|
|
});
|
|
}
|