todo-app/test/markdown_view_test.dart
Krzysztof kuhy Rudnicki abd4ba3bd7 Add full note view/editor with templates and Markdown render
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>
2026-06-15 21:59:31 +02:00

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);
});
}