todo-app/test/settings_screen_harness.dart
Krzysztof kuhy Rudnicki 10a546f6d6 Split nine oversized test files under the cap
Every split is by test-group/concern, with the shared setup moved to a
sibling helper the splits import. All 313 tests are preserved -- the count
is unchanged and `test('…')` names are byte-identical to the originals
(verified by diffing the sorted name lists).

  note_repository_test      677 -> 4 files + note_repository_helpers
  capture_screen_test       660 -> 4 files + capture_screen_harness
  note_editor_test          502 -> 3 files + note_editor_harness
  port_guard_test           405 -> 3 files + port_guard_fakes
  wrapper_server_test       402 -> 2 files + wrapper_server_harness
  settings_screen_test      391 -> 2 files + settings_screen_harness
  github_mirror_screen_test 365 -> 2 files + github_mirror_harness
  notes_list_screen_test    347 -> 2 files + notes_list_harness
  analytics_service_test    300 -> 2 files + analytics_helpers

Helpers are deliberately NOT named `*_test.dart`; the runner would collect
them and fail on the missing `main()`.

Three things did not survive a mechanical split and were handled explicitly:

  * wrapper_server's `enabledOrigin` closes over the `late root` that setUp
    assigns, so a top-level function cannot see it. It is now a
    WrapperHarness class with an install() that registers the setUp/tearDown
    pair -- the only structural change in this commit.
  * note_repository's splits each repeat `setUpAll(sqfliteFfiInit)`, and
    analytics' each repeat `@TestOn('vm')`. Omitting either fails quietly
    (uninitialised sqflite / wrong platform) rather than loudly.
  * capture_screen's `recordingMock` was declared mid-file between two
    tests, so it moved to the harness rather than being dropped.

Verified: 313 tests pass (same as before the refactor), 100% coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-14 16:42:13 +02:00

117 lines
4.0 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/// The pumpSettings harness and its file-selector fakes.
///
/// Shared by the files `settings_screen_test.dart` was split into for the
/// 250-line cap. Deliberately NOT named `*_test.dart`: the runner would
/// collect it and fail on the missing `main()`.
library;
import 'dart:convert';
import 'dart:io';
import 'package:crdt_sync/crdt_sync.dart';
import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sync_settings_ui/sync_settings_ui.dart';
import 'package:todo/analytics/analytics_service.dart';
import 'package:todo/data/app_settings.dart';
import 'package:todo/data/note.dart';
import 'package:todo/data/note_repository.dart';
import 'package:todo/sync/backlog_export_io.dart';
import 'package:todo/sync/notes_markdown.dart';
import 'package:todo/sync/sync_settings.dart';
import 'package:todo/ui/github_mirror_screen.dart';
import 'package:todo/ui/settings_screen.dart';
import 'fake_note_repository.dart';
import 'fake_secure_storage.dart';
/// Stub file picker that returns a fixed in-memory file (no disk I/O, so the
/// `_import` flow stays timer-free and deterministic under the widget tester).
class _FakeFileSelector extends FileSelectorPlatform
with MockPlatformInterfaceMixin {
_FakeFileSelector(this.file);
final XFile? file;
@override
Future<XFile?> openFile({
List<XTypeGroup>? acceptedTypeGroups,
String? initialDirectory,
String? confirmButtonText,
}) async => file;
}
/// Repository whose reads fail, to exercise the export error path.
class _ExplodingRepo extends FakeNoteRepository {
@override
Future<List<Note>> listNotes({
NoteSort sort = NoteSort.modifiedDesc,
NoteFilter filter = const NoteFilter(),
}) async => throw Exception('db down');
}
/// File picker stub that throws, to exercise the import error path.
class _ThrowingFileSelector extends FileSelectorPlatform
with MockPlatformInterfaceMixin {
@override
Future<XFile?> openFile({
List<XTypeGroup>? acceptedTypeGroups,
String? initialDirectory,
String? confirmButtonText,
}) async => throw Exception('picker blew up');
}
Future<FakeNoteRepository> pumpSettings(
WidgetTester tester, {
SyncSettings initial = const SyncSettings(
owner: 'kuhyx',
repo: 'syncs',
token: 't',
),
http.Client? httpClient,
List<Note> seed = const [],
FakeNoteRepository? repository,
Future<FirebaseRestClient?> Function()? firebaseFactory,
ValueNotifier<AppSettings>? appSettings,
AnalyticsService? analytics,
}) async {
SharedPreferences.setMockInitialValues({});
installFakeSecureStorage();
// Tall surface so the whole settings ListView builds and the pushed
// Sync settings screen's Backup section (below the default 800×600
// fold) isn't lazy-skipped.
tester.view.physicalSize = const Size(1200, 2800);
tester.view.devicePixelRatio = 1.0;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
final repo = repository ?? FakeNoteRepository(seed);
addTearDown(repo.close);
await tester.pumpWidget(
MaterialApp(
home: SettingsScreen(
initial: initial,
repository: repo,
appSettings:
appSettings ??
ValueNotifier(const AppSettings(advancedMode: false)),
analytics: analytics,
httpClient: httpClient,
// Injected so the widget never reaches for the platform: the real
// factory wants the OS keystore, which doesn't exist under
// `flutter test`.
firebaseFactory: firebaseFactory ?? () async => null,
accountLoader: () async => null,
sessionProbe: () async => false,
stateStore: InMemorySyncStateStore(),
),
),
);
await tester.pump();
return repo;
}