mirror of
https://github.com/kuhyx/todo-app.git
synced 2026-07-04 15:03:01 +02:00
Notes were previously only openable via a quick-actions sheet; you could not read or edit a note in full. Add a shared NoteEditor used by both the capture and detail screens, plus selectable templates and a rendered Markdown view. - note_template.dart: pure assemble/parse layer over a Markdown subset (# title, ## sections + italic guidance, dropping empty sections). assemble(parse(text)) is idempotent for conforming text; non-conforming / legacy / freeform text is reported so the UI falls back to raw, untouched. Two templates: llm-design-spec (default) and blank. - note_editor.dart: View / Guided / Raw modes. Guided is an inline Stepper (one step per section with its guidance); View renders the note via MarkdownView; Raw is the verbatim text. Guided is offered only for structured templates; switching to it is blocked when the raw text no longer conforms. - markdown_view.dart: lean read-only renderer for the note subset, wrapped in a SelectionArea for copy-out. - note_detail_screen.dart: full-screen note; opens in View, edits persist immediately, priority/status dropdowns, delete. - capture_screen / notes_list_screen wired to the new editor and detail screen (tap a note opens it; quick actions move to the overflow button). The editor is a view over plain text, so CRDT storage and Markdown export/sync are unaffected. 138 tests, 100% line coverage. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
72 lines
2.3 KiB
Dart
72 lines
2.3 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:todo/ui/markdown_view.dart';
|
|
|
|
void main() {
|
|
Future<void> pumpView(WidgetTester tester, String text) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(body: MarkdownView(text: text)),
|
|
),
|
|
);
|
|
await tester.pump();
|
|
}
|
|
|
|
/// Finds the rendered Text widget whose data equals [data] and returns it.
|
|
Text textWidget(WidgetTester tester, String data) =>
|
|
tester.widget<Text>(find.text(data));
|
|
|
|
testWidgets('renders an h1 title bold and large', (tester) async {
|
|
await pumpView(tester, '# Big title');
|
|
final t = textWidget(tester, 'Big title');
|
|
expect(t.style?.fontWeight, FontWeight.bold);
|
|
});
|
|
|
|
testWidgets('renders an h2 section heading without its marker', (
|
|
tester,
|
|
) async {
|
|
await pumpView(tester, '## what');
|
|
expect(find.text('what'), findsOneWidget);
|
|
expect(textWidget(tester, 'what').style?.fontWeight, FontWeight.bold);
|
|
});
|
|
|
|
testWidgets('renders a whole-line italic guidance line', (tester) async {
|
|
await pumpView(tester, '_guidance text_');
|
|
final t = textWidget(tester, 'guidance text');
|
|
expect(t.style?.fontStyle, FontStyle.italic);
|
|
});
|
|
|
|
testWidgets('renders dash and star bullets with a bullet glyph', (
|
|
tester,
|
|
) async {
|
|
await pumpView(tester, '- first\n* second');
|
|
expect(find.text('first'), findsOneWidget);
|
|
expect(find.text('second'), findsOneWidget);
|
|
expect(find.textContaining('•'), findsNWidgets(2));
|
|
});
|
|
|
|
testWidgets('renders a plain paragraph and blank-line spacers', (
|
|
tester,
|
|
) async {
|
|
await pumpView(tester, 'a paragraph\n\nanother');
|
|
expect(find.text('a paragraph'), findsOneWidget);
|
|
expect(find.text('another'), findsOneWidget);
|
|
// The blank line between paragraphs becomes a spacer box.
|
|
expect(find.byType(SizedBox), findsWidgets);
|
|
});
|
|
|
|
testWidgets('a full assembled note renders all element types', (
|
|
tester,
|
|
) async {
|
|
await pumpView(
|
|
tester,
|
|
'# Title\n\n## what\n_why we need it_\n\n- one\n- two\nplain tail',
|
|
);
|
|
expect(find.text('Title'), findsOneWidget);
|
|
expect(find.text('what'), findsOneWidget);
|
|
expect(find.text('why we need it'), findsOneWidget);
|
|
expect(find.text('one'), findsOneWidget);
|
|
expect(find.text('plain tail'), findsOneWidget);
|
|
});
|
|
}
|