import 'package:flutter_test/flutter_test.dart';
import 'package:mews/mews/document.dart';
import 'package:mews/mews/parser.dart';
const conformingPage = '''
());
final paragraph = page.body[1] as ParagraphBlock;
expect(paragraph.inline, hasLength(5));
final link = paragraph.inline[3] as MewsLink;
expect(link.href, 'https://example.com/about.html');
});
test('rejects a page that is not well-formed', () {
expect(
() => parseMewsPage(
source: 'xoops',
url: 'https://example.com/'),
throwsA(isA()),
);
});
test('treats a page without the conformance marker as non-Mews', () {
final source = '''
Plain
Just XHTML.
''';
final page = parseMewsPage(source: source, url: 'https://example.com/plain.html');
expect(page.isMews, isFalse);
expect(page.specVersion, isNull);
expect(page.variant, MewsVariant.plain);
});
test('resolves relative URLs against the page', () {
final page = parseMewsPage(source: conformingPage, url: 'https://example.com/posts/index.html');
final paragraph = page.body[1] as ParagraphBlock;
final link = paragraph.inline[3] as MewsLink;
expect(link.href, 'https://example.com/posts/about.html');
});
test('allows same-site images and refuses off-site and data URIs', () {
final source = '''
Pics
and 


''';
final page = parseMewsPage(source: source, url: 'https://example.com/pics.html');
final images = page.body.whereType().toList();
expect(images, hasLength(4));
expect(images[0].src, 'https://example.com/cat.png');
expect(images[0].allowed, isTrue);
expect(images[1].src, 'https://img.example.com/dog.png');
expect(images[1].allowed, isTrue, reason: 'subdomains share the registered domain');
expect(images[2].allowed, isFalse, reason: 'another site');
expect(images[3].allowed, isFalse, reason: 'data URI');
});
test('drops scripts and styles entirely', () {
final source = '''
Spooky
Keep this.
''';
final page = parseMewsPage(source: source, url: 'https://example.com/spooky.html');
expect(page.body, hasLength(1));
final paragraph = page.body[0] as ParagraphBlock;
expect((paragraph.inline[0] as MewsText).text, 'Keep this.');
});
test('honors dir=rtl', () {
final source = '''
Shalom
Text.
''';
final page = parseMewsPage(source: source, url: 'https://example.com/he.html');
expect(page.direction, TextDirection.rtl);
});
test('builds form fields with labels', () {
final source = '''
Guestbook
''';
final page = parseMewsPage(source: source, url: 'https://example.com/guest.html');
final form = page.body.single as FormBlock;
expect(form.action, 'https://example.com/sign');
expect(form.method, 'post');
final nameField = form.fields[0] as MewsTextField;
expect(nameField.name, 'name');
expect(nameField.label, 'Your name');
final hidden = form.fields[1] as MewsHiddenField;
expect(hidden.value, '1');
final submit = form.fields[2] as MewsSubmitField;
expect(submit.label, 'Sign guestbook');
});
test('mewsPlainText recovers text from broken markup', () {
const broken = 'HalfVisible & intact.
';
final text = mewsPlainText(broken);
expect(text, contains('Visible & intact.'));
});
test('keeps bare inline content in list items and quotes', () {
final source = '''
Bare
Quoted words verbatim.
''';
final page = parseMewsPage(source: source, url: 'https://example.com/bare.html');
final list = page.body[0] as ListBlock;
final first = (list.items[0].blocks.single as ParagraphBlock).inline;
final link = first.single as MewsLink;
expect(link.href, 'https://example.com/2026-10-08-post.html');
expect((link.children.single as MewsText).text, '2026-10-08 A dated link');
expect((list.items[1].blocks.single as ParagraphBlock).inline.single,
isA());
// Text before a nested list stays with the item; the list becomes a block.
expect(list.items[2].blocks, hasLength(2));
expect(list.items[2].blocks[0], isA());
expect(list.items[2].blocks[1], isA());
final quote = page.body[1] as QuoteBlock;
final quoteText = (quote.children.single as ParagraphBlock).inline;
expect(quoteText, hasLength(3));
expect((quoteText[0] as MewsText).text, 'Quoted words ');
});
}