todo-app/test/capture_screen_editing_test.dart
Krzysztof kuhy Rudnicki 091e68a2d9 Text-free UI: glyph chips, icon-only actions
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 01:02:25 +01:00

162 lines
5.5 KiB
Dart

import 'dart:convert';
import 'package:crdt_sync/crdt_sync.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:shared_preferences/shared_preferences.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_template.dart';
import 'package:todo/sync/local_backup.dart';
import 'package:todo/sync/notes_markdown.dart';
import 'package:todo/ui/capture_screen.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';
import 'capture_screen_harness.dart';
void main() {
testWidgets('defaults to Raw, with Guided available via the entry wizard', (
tester,
) async {
await pumpCapture(tester);
// Defaults to Raw: a single text field, no note persisted yet.
expect(find.byType(TextField), findsOneWidget);
expect(find.bySemanticsLabel('Guided'), findsOneWidget);
expect(find.text('0'), findsOneWidget);
// Tapping Guided on the empty draft opens the priority+template wizard
// rather than jumping straight into the stepper.
await tester.tap(find.bySemanticsLabel('Guided'));
await tester.pump();
expect(find.bySemanticsLabel('Step 1 of 2'), findsOneWidget);
await tester.tap(find.bySemanticsLabel('Next'));
await tester.pump();
await tester.tap(find.bySemanticsLabel('Start'));
await tester.pump();
// Now on step 1 of the full-screen step page.
expect(find.byType(LinearProgressIndicator), findsOneWidget);
expect(
find.text('1 / ${NoteTemplate.llmDesignSpec.sections.length}'),
findsOneWidget,
);
expect(find.textContaining('imperative'), findsOneWidget); // title helper
});
testWidgets('saving the untouched template creates no note', (tester) async {
final repo = await pumpCapture(tester);
await tester.tap(find.bySemanticsLabel('New note'));
await tester.pump();
expect(await repo.listNotes(), isEmpty);
});
testWidgets('typing into the template persists a note with defaults', (
tester,
) async {
final repo = await pumpCapture(tester);
// The first field in the guided stepper is the title section.
await tester.enterText(find.byType(TextField).first, 'My idea');
await tester.pump();
final notes = await repo.listNotes();
expect(notes, hasLength(1));
expect(notes.single.text, contains('My idea'));
expect(notes.single.priority, Priority.medium);
expect(notes.single.status, Status.todo);
expect(find.text('1'), findsOneWidget);
});
testWidgets('the new-note action resets the template', (tester) async {
final repo = await pumpCapture(tester);
await tester.enterText(find.byType(TextField).first, 'A real idea');
await tester.pump();
await tester.tap(find.bySemanticsLabel('New note'));
await tester.pump();
expect(await repo.listNotes(), hasLength(1));
// The editor reset to a fresh, empty Raw draft.
expect(find.byType(TextField), findsOneWidget);
final raw = tester.widget<TextField>(find.byType(TextField));
expect(raw.controller!.text, isEmpty);
});
testWidgets('tapping Sync while unconfigured prompts to connect a backend', (
tester,
) async {
await pumpCapture(tester); // empty prefs → no token → not configured
await tester.tap(find.bySemanticsLabel('Sync'));
await tester.pump(); // settings load + snackbar
await tester.pump();
// The behaviour that matters is the routing, not the wording: an
// unconfigured device is sent to settings to connect a backend. Asserting
// on the snackbar text tied this to one phrasing, and the snackbar is
// gone by the time settings has pushed anyway.
expect(find.textContaining('Connect Firebase'), findsWidgets);
});
testWidgets('the notes-list button navigates to the list screen', (
tester,
) async {
await pumpCapture(tester);
await tester.tap(find.bySemanticsLabel('Notes'));
await tester.pump();
await tester.pump(const Duration(milliseconds: 300)); // route transition
expect(find.byIcon(Icons.note), findsOneWidget); // list screen title
});
testWidgets('changing the priority dropdown updates the saved note', (
tester,
) async {
final repo = await pumpCapture(tester);
await tester.enterText(find.byType(TextField).first, 'Prioritised idea');
await tester.pump();
await tester.tap(
find.byWidgetPredicate((w) => w is DropdownButton<Priority>),
);
await tester.pump();
await tester.pump(const Duration(milliseconds: 400)); // menu open
await tester.tap(find.bySemanticsLabel('High').last);
await tester.pump();
expect((await repo.listNotes()).single.priority, Priority.high);
});
testWidgets('changing the status dropdown updates the saved note', (
tester,
) async {
final repo = await pumpCapture(tester);
await tester.enterText(find.byType(TextField).first, 'Status idea');
await tester.pump();
await tester.tap(
find.byWidgetPredicate((w) => w is DropdownButton<Status>),
);
await tester.pump();
await tester.pump(const Duration(milliseconds: 400)); // menu open
await tester.tap(find.bySemanticsLabel('In progress').last);
await tester.pump();
expect((await repo.listNotes()).single.status, Status.inProgress);
});
}