the-mews/app/test/gemtext_test.dart
2026-10-11 22:07:35 +03:00

109 lines
3.3 KiB
Dart

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<HeadingBlock>()
.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<ParagraphBlock>().length;
expect(paragraphs, 3); // Two opening lines and the four-hashes line.
final lists = parsed.body.whereType<ListBlock>().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<QuoteBlock>().toList();
expect(quotes, hasLength(1));
expect(quotes.first.children, hasLength(2));
final pre = parsed.body.whereType<PreBlock>().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<ListBlock>().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<PreBlock>().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, 'שלום');
});
}