mirror of
https://github.com/kuhyx/todo-app.git
synced 2026-07-04 15:03:01 +02:00
Notes list & filtering: - Text-search filter plus independent date-range filters for both created and last-updated (AND-combined), a priority filter, and a new status filter. Default view hides Done/Abandoned and renders as "unfiltered" (no badge for the default state); fixed badge clipping. - NoteSort options wired into the list UI; watchCount() for the "N saved". Status & priority: - New Status enum (toDo/inProgress/Done/Abandoned) as a settable + filterable attribute on every note, with capture-screen dropdown. - Removed "None" priority: every note is Low/Medium/High, default Medium. Schema migration v2->v3 rewrites legacy priority 0 -> Medium. Export / import: - NotesMarkdown round-trippable single-file format with HTML-comment markers. - Settings "Export notes" (mobile share sheet / desktop writes ~/todo/BACKLOG.md) and "Import notes" (file picker + safe newer-wins merge by id). Structured template: - Every new note pre-fills the richer what/where/must/nice/out/done/depends/ estimate/refs scaffold. Tests: - New fast (~5s), deterministic suite via FakeNoteRepository (no DB timers) and injected http/file-selector/url-launcher fakes. 86 tests, 96.2% line coverage (note.dart & sync_service.dart at 100%, settings 98.7%). Mobile-only share branch excluded via coverage:ignore (unreachable on the Linux test host). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
110 lines
3.4 KiB
Dart
110 lines
3.4 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:todo/data/note.dart';
|
|
|
|
void main() {
|
|
group('Priority', () {
|
|
test('fromValue maps known, legacy and unknown values', () {
|
|
expect(Priority.fromValue(1), Priority.low);
|
|
expect(Priority.fromValue(2), Priority.medium);
|
|
expect(Priority.fromValue(3), Priority.high);
|
|
expect(Priority.fromValue(0), Priority.medium); // legacy "none"
|
|
expect(Priority.fromValue(null), Priority.medium);
|
|
expect(Priority.fromValue(99), Priority.medium);
|
|
});
|
|
|
|
test('labels and default', () {
|
|
expect(Priority.high.label, 'High');
|
|
expect(Priority.defaultValue, Priority.medium);
|
|
});
|
|
});
|
|
|
|
group('Status', () {
|
|
test('fromValue maps known and unknown values', () {
|
|
expect(Status.fromValue(0), Status.todo);
|
|
expect(Status.fromValue(1), Status.inProgress);
|
|
expect(Status.fromValue(2), Status.done);
|
|
expect(Status.fromValue(3), Status.abandoned);
|
|
expect(Status.fromValue(null), Status.todo);
|
|
expect(Status.fromValue(42), Status.todo);
|
|
});
|
|
|
|
test('labels', () {
|
|
expect(Status.inProgress.label, 'In progress');
|
|
expect(Status.abandoned.label, 'Abandoned');
|
|
});
|
|
});
|
|
|
|
test('fromRow builds a Note from a raw column map', () {
|
|
final note = Note.fromRow({
|
|
'id': 'x',
|
|
'text': 'hello',
|
|
'priority': 3,
|
|
'status': 1,
|
|
'created_at': '2026-06-15T09:00:00.000',
|
|
'updated_at': '2026-06-15T10:00:00.000',
|
|
});
|
|
expect(note.id, 'x');
|
|
expect(note.text, 'hello');
|
|
expect(note.priority, Priority.high);
|
|
expect(note.status, Status.inProgress);
|
|
expect(note.createdAt, DateTime(2026, 6, 15, 9));
|
|
expect(note.updatedAt, DateTime(2026, 6, 15, 10));
|
|
});
|
|
|
|
test('fromRow tolerates a null text column', () {
|
|
final note = Note.fromRow({
|
|
'id': 'x',
|
|
'text': null,
|
|
'priority': 2,
|
|
'status': 0,
|
|
'created_at': '2026-06-15T09:00:00.000',
|
|
'updated_at': '2026-06-15T09:00:00.000',
|
|
});
|
|
expect(note.text, '');
|
|
});
|
|
|
|
test('copyWith replaces selected fields and keeps identity', () {
|
|
final base = Note(
|
|
id: 'id',
|
|
text: 'a',
|
|
priority: Priority.low,
|
|
status: Status.todo,
|
|
createdAt: DateTime(2026, 1, 1),
|
|
updatedAt: DateTime(2026, 1, 1),
|
|
);
|
|
final updated = base.copyWith(
|
|
text: 'b',
|
|
priority: Priority.high,
|
|
status: Status.done,
|
|
updatedAt: DateTime(2026, 2, 2),
|
|
);
|
|
expect(updated.id, 'id');
|
|
expect(updated.createdAt, DateTime(2026, 1, 1)); // unchanged
|
|
expect(updated.text, 'b');
|
|
expect(updated.priority, Priority.high);
|
|
expect(updated.status, Status.done);
|
|
expect(updated.updatedAt, DateTime(2026, 2, 2));
|
|
});
|
|
|
|
test('copyWith with no arguments preserves every field', () {
|
|
// Exercises the `?? this.x` fallback on each field — the path that the
|
|
// selective-replace test above never hits because it always supplies
|
|
// a value.
|
|
final base = Note(
|
|
id: 'id',
|
|
text: 'a',
|
|
priority: Priority.high,
|
|
status: Status.inProgress,
|
|
createdAt: DateTime(2026, 1, 1),
|
|
updatedAt: DateTime(2026, 3, 3),
|
|
);
|
|
final clone = base.copyWith();
|
|
expect(clone.id, base.id);
|
|
expect(clone.text, base.text);
|
|
expect(clone.priority, base.priority);
|
|
expect(clone.status, base.status);
|
|
expect(clone.createdAt, base.createdAt);
|
|
expect(clone.updatedAt, base.updatedAt);
|
|
});
|
|
}
|