Add list filters/sort, status, priority rework, export/import, structured template
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>
2026-06-15 16:52:59 +02:00
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
|
import 'package:todo/sync/sync_settings.dart';
|
|
|
|
|
|
2026-06-15 22:57:05 +02:00
|
|
|
import 'fake_secure_storage.dart';
|
|
|
|
|
|
Add list filters/sort, status, priority rework, export/import, structured template
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>
2026-06-15 16:52:59 +02:00
|
|
|
void main() {
|
2026-06-15 22:57:05 +02:00
|
|
|
// installFakeSecureStorage touches the test binary messenger, which needs the
|
|
|
|
|
// binding up first (widget tests get this for free via testWidgets).
|
|
|
|
|
TestWidgetsFlutterBinding.ensureInitialized();
|
|
|
|
|
|
Add list filters/sort, status, priority rework, export/import, structured template
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>
2026-06-15 16:52:59 +02:00
|
|
|
test(
|
|
|
|
|
'load returns the kuhyx/todo-sync defaults on a fresh install',
|
|
|
|
|
() async {
|
|
|
|
|
SharedPreferences.setMockInitialValues({});
|
2026-06-15 22:57:05 +02:00
|
|
|
installFakeSecureStorage();
|
Add list filters/sort, status, priority rework, export/import, structured template
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>
2026-06-15 16:52:59 +02:00
|
|
|
final s = await SyncSettings.load();
|
|
|
|
|
expect(s.owner, 'kuhyx');
|
|
|
|
|
expect(s.repo, 'todo-sync');
|
|
|
|
|
expect(s.token, '');
|
2026-06-15 22:21:34 +02:00
|
|
|
// Client id defaults to the baked-in OAuth App id (one-tap connect).
|
|
|
|
|
expect(s.clientId, SyncSettings.defaultClientId);
|
Add list filters/sort, status, priority rework, export/import, structured template
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>
2026-06-15 16:52:59 +02:00
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
2026-06-15 22:57:05 +02:00
|
|
|
test('save stores the token in the keystore, not in prefs', () async {
|
Add list filters/sort, status, priority rework, export/import, structured template
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>
2026-06-15 16:52:59 +02:00
|
|
|
SharedPreferences.setMockInitialValues({});
|
2026-06-15 22:57:05 +02:00
|
|
|
installFakeSecureStorage();
|
Add list filters/sort, status, priority rework, export/import, structured template
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>
2026-06-15 16:52:59 +02:00
|
|
|
await const SyncSettings(
|
|
|
|
|
owner: 'me',
|
|
|
|
|
repo: 'notes',
|
|
|
|
|
token: 'tok',
|
|
|
|
|
clientId: 'cid',
|
|
|
|
|
).save();
|
|
|
|
|
|
2026-06-15 22:57:05 +02:00
|
|
|
// Token must not linger in plaintext prefs once secured.
|
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
|
expect(prefs.getString('sync.token'), isNull);
|
|
|
|
|
|
Add list filters/sort, status, priority rework, export/import, structured template
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>
2026-06-15 16:52:59 +02:00
|
|
|
final s = await SyncSettings.load();
|
|
|
|
|
expect(s.owner, 'me');
|
|
|
|
|
expect(s.repo, 'notes');
|
|
|
|
|
expect(s.token, 'tok');
|
|
|
|
|
expect(s.clientId, 'cid');
|
|
|
|
|
});
|
|
|
|
|
|
2026-06-15 22:57:05 +02:00
|
|
|
test('load reads the token straight from the keystore', () async {
|
|
|
|
|
SharedPreferences.setMockInitialValues({});
|
|
|
|
|
installFakeSecureStorage(initial: {'sync.token': 'fromKeystore'});
|
|
|
|
|
final s = await SyncSettings.load();
|
|
|
|
|
expect(s.token, 'fromKeystore');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('load migrates a legacy plaintext token into the keystore', () async {
|
|
|
|
|
SharedPreferences.setMockInitialValues({'sync.token': 'legacy'});
|
|
|
|
|
installFakeSecureStorage();
|
|
|
|
|
|
|
|
|
|
final s = await SyncSettings.load();
|
|
|
|
|
expect(s.token, 'legacy');
|
|
|
|
|
|
|
|
|
|
// The plaintext copy is dropped once the secure write succeeds, and the
|
|
|
|
|
// value now resolves from the keystore on the next load.
|
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
|
expect(prefs.getString('sync.token'), isNull);
|
|
|
|
|
final again = await SyncSettings.load();
|
|
|
|
|
expect(again.token, 'legacy');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test(
|
|
|
|
|
'load keeps the plaintext token when no secret service is available',
|
|
|
|
|
() async {
|
|
|
|
|
SharedPreferences.setMockInitialValues({'sync.token': 'plain'});
|
|
|
|
|
installFakeSecureStorage(throwing: true);
|
|
|
|
|
|
|
|
|
|
final s = await SyncSettings.load();
|
|
|
|
|
expect(s.token, 'plain');
|
|
|
|
|
// Never drop the only copy when the keystore write can't be confirmed.
|
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
|
expect(prefs.getString('sync.token'), 'plain');
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
test('save falls back to plaintext prefs when the keystore fails', () async {
|
|
|
|
|
SharedPreferences.setMockInitialValues({});
|
|
|
|
|
installFakeSecureStorage(throwing: true);
|
|
|
|
|
await const SyncSettings(owner: 'o', repo: 'r', token: 'tok').save();
|
|
|
|
|
|
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
|
expect(prefs.getString('sync.token'), 'tok');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('save with an empty token clears the keystore entry', () async {
|
|
|
|
|
// Seed a keystore token, then save an empty token: it must be deleted and
|
|
|
|
|
// no plaintext copy written.
|
|
|
|
|
SharedPreferences.setMockInitialValues({});
|
|
|
|
|
installFakeSecureStorage(initial: {'sync.token': 'old'});
|
|
|
|
|
await const SyncSettings(owner: 'o', repo: 'r', token: '').save();
|
|
|
|
|
|
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
|
expect(prefs.getString('sync.token'), isNull);
|
|
|
|
|
final s = await SyncSettings.load();
|
|
|
|
|
expect(s.token, '');
|
|
|
|
|
});
|
|
|
|
|
|
Add list filters/sort, status, priority rework, export/import, structured template
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>
2026-06-15 16:52:59 +02:00
|
|
|
test('isConfigured requires owner, repo and token', () {
|
|
|
|
|
expect(
|
|
|
|
|
const SyncSettings(owner: 'o', repo: 'r', token: 't').isConfigured,
|
|
|
|
|
isTrue,
|
|
|
|
|
);
|
|
|
|
|
expect(
|
|
|
|
|
const SyncSettings(owner: 'o', repo: 'r', token: '').isConfigured,
|
|
|
|
|
isFalse,
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('canUseDeviceFlow needs a client id', () {
|
|
|
|
|
expect(
|
|
|
|
|
const SyncSettings(
|
|
|
|
|
owner: '',
|
|
|
|
|
repo: '',
|
|
|
|
|
token: '',
|
|
|
|
|
clientId: 'c',
|
|
|
|
|
).canUseDeviceFlow,
|
|
|
|
|
isTrue,
|
|
|
|
|
);
|
|
|
|
|
expect(
|
|
|
|
|
const SyncSettings(owner: '', repo: '', token: '').canUseDeviceFlow,
|
|
|
|
|
isFalse,
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('copyWith overrides only the given fields', () {
|
|
|
|
|
const base = SyncSettings(owner: 'o', repo: 'r', token: 't', clientId: 'c');
|
|
|
|
|
final next = base.copyWith(token: 'new');
|
|
|
|
|
expect(next.owner, 'o');
|
|
|
|
|
expect(next.repo, 'r');
|
|
|
|
|
expect(next.token, 'new');
|
|
|
|
|
expect(next.clientId, 'c');
|
2026-06-15 17:11:01 +02:00
|
|
|
|
|
|
|
|
// No-arg copy exercises the `?? this.x` fallback on every field.
|
|
|
|
|
final clone = base.copyWith();
|
|
|
|
|
expect(clone.owner, 'o');
|
|
|
|
|
expect(clone.repo, 'r');
|
|
|
|
|
expect(clone.token, 't');
|
|
|
|
|
expect(clone.clientId, 'c');
|
Add list filters/sort, status, priority rework, export/import, structured template
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>
2026-06-15 16:52:59 +02:00
|
|
|
});
|
|
|
|
|
}
|