feat: add Gemini and Spartan protocol support
This commit is contained in:
parent
a4a49f41d7
commit
fa7aa5557c
48 changed files with 4992 additions and 161 deletions
109
app/test/gemtext_test.dart
Normal file
109
app/test/gemtext_test.dart
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
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, 'שלום');
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue