import 'package:flutter_test/flutter_test.dart'; import 'package:mews/mews/document.dart'; import 'package:mews/mews/gemtext.dart'; const page = ''' # A capsule Some opening text. A second line of it. ## A section * one * two => index.gmi The index => retro.gmi 2026-10-08 Collecting retro phones => first.gmi 2026-09-21 First post > a quoted line > and another ```rust fn main() {} ``` #### four hashes are text '''; void main() { test('gemtext converts to the document model', () { final parsed = parseGemtextPage(source: page, url: 'gemini://capsule.example/index.gmi'); expect(parsed.url, 'gemini://capsule.example/index.gmi'); expect(parsed.specVersion, gemtextSpecVersion); expect(parsed.isMews, isTrue); expect(parsed.title, 'A capsule'); expect(parsed.direction, TextDirection.ltr); final headings = parsed.body .whereType() .map((h) => (h.level, (h.inline.single as MewsText).text)) .toList(); expect(headings, [ (1, 'A capsule'), (2, 'A section'), ]); // Each text line is its own paragraph. final paragraphs = parsed.body.whereType().length; expect(paragraphs, 3); // Two opening lines and the four-hashes line. final lists = parsed.body.whereType().toList(); expect(lists, hasLength(2)); expect(lists.first.items, hasLength(2)); expect( ((lists.first.items.first.blocks.single as ParagraphBlock).inline.single as MewsText).text, 'one'); // Consecutive link lines form one list, so a menu reads as a menu. expect(lists.last.items, hasLength(3)); final quotes = parsed.body.whereType().toList(); expect(quotes, hasLength(1)); expect(quotes.first.children, hasLength(2)); final pre = parsed.body.whereType().single; expect(pre.text, 'fn main() {}'); }); test('dated links become entries, resolved absolute', () { final parsed = parseGemtextPage(source: page, url: 'gemini://capsule.example/index.gmi'); expect( parsed.entries.map((e) => (e.url, e.date, e.title)).toList(), [ ('gemini://capsule.example/retro.gmi', DateTime(2026, 10, 8), 'Collecting retro phones'), ('gemini://capsule.example/first.gmi', DateTime(2026, 9, 21), 'First post'), ], ); }); test('a link without a label shows its address', () { final parsed = parseGemtextPage( source: '=> gemini://else.example/x.gmi', url: 'gemini://capsule.example/'); final list = parsed.body.whereType().single; final link = ((list.items.single.blocks.single as ParagraphBlock).inline.single as MewsLink); expect(link.href, 'gemini://else.example/x.gmi'); expect((link.children.single as MewsText).text, 'gemini://else.example/x.gmi'); }); test('an unterminated pre block keeps its lines', () { final parsed = parseGemtextPage( source: '```\nstill inside', url: 'gemini://capsule.example/'); expect(parsed.body.whereType().single.text, 'still inside'); }); test('right-to-left text is written in its own direction', () { final parsed = parseGemtextPage( source: '# שלום\nטקסט בעברית', url: 'gemini://capsule.example/he.gmi'); expect(parsed.direction, TextDirection.rtl); expect(parsed.lang, 'he'); expect(parsed.title, 'שלום'); }); }