New export target: a single offline .html rendered from the deck's Marp Markdown. Bundles (inlines) marked, highlight.js, MathJax (tex-svg, no font files) and mermaid, so code highlighting, LaTeX math and mermaid diagrams all render in any browser with no network access. - MarpHtmlService splits the deck on `---`, strips front-matter, and inlines the vendored libraries (assets/web_export/) with a </script> breakout guard. The asset loader is injectable for testing. - ExportFormat.html wired through ExportService (no rasterization needed), the export dialog (new button, skips slide rendering) and app_shell (passes the generated Markdown). Export dialog is now scrollable. Note: rendered with marked, not Marp Core, so theme fidelity differs from the in-app preview / PDF / PPTX; the win is a portable, dependency-free deck. Tests: slide splitting, library inlining, breakout escaping, and an end-to-end .html export. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
76 lines
2.1 KiB
Dart
76 lines
2.1 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:ocideck/services/marp_html_service.dart';
|
|
|
|
/// Reads the vendored libraries straight from the repo (tests run at the root).
|
|
Future<String> _diskLoader(String asset) => File(asset).readAsString();
|
|
|
|
void main() {
|
|
group('marpSlides', () {
|
|
test('drops the YAML front-matter and splits on --- separators', () {
|
|
const md = '''
|
|
---
|
|
marp: true
|
|
theme: ocideck
|
|
---
|
|
|
|
# Slide one
|
|
|
|
---
|
|
|
|
## Slide two
|
|
''';
|
|
final slides = MarpHtmlService.marpSlides(md);
|
|
expect(slides, hasLength(2));
|
|
expect(slides[0], contains('# Slide one'));
|
|
expect(slides[0], isNot(contains('marp: true')));
|
|
expect(slides[1], contains('## Slide two'));
|
|
});
|
|
|
|
test('a deck without front-matter keeps every slide', () {
|
|
final slides = MarpHtmlService.marpSlides(
|
|
'# A\n\n---\n\n# B\n\n---\n\n# C',
|
|
);
|
|
expect(slides, hasLength(3));
|
|
});
|
|
});
|
|
|
|
test('build() inlines the libraries and the slide content', () async {
|
|
final service = MarpHtmlService(loadAsset: _diskLoader);
|
|
const md = '''
|
|
---
|
|
marp: true
|
|
---
|
|
|
|
# Titel
|
|
|
|
\$\$E=mc^2\$\$
|
|
|
|
```dart
|
|
void main() {}
|
|
```
|
|
''';
|
|
final html = await service.build(md);
|
|
|
|
expect(html, startsWith('<!doctype html>'));
|
|
// Slide payload is embedded for the in-browser renderer.
|
|
expect(html, contains('# Titel'));
|
|
expect(html, contains(r'E=mc^2'));
|
|
// Each engine is inlined (offline): marked, highlight.js, MathJax, mermaid.
|
|
expect(html, contains('marked'));
|
|
expect(html, contains('hljs'));
|
|
expect(html, contains('MathJax'));
|
|
expect(html, contains('mermaid'));
|
|
// Everything is inlined: there must be no external <script src=...> tags.
|
|
expect(html, isNot(contains('<script src')));
|
|
});
|
|
|
|
test('build() neutralises a closing-script breakout in content', () async {
|
|
final service = MarpHtmlService(loadAsset: _diskLoader);
|
|
final html = await service.build('# X\n\nfoo </script> bar');
|
|
// The literal breakout must be escaped so it cannot terminate the payload.
|
|
expect(html, isNot(contains('foo </script> bar')));
|
|
expect(html, contains(r'<\/script'));
|
|
});
|
|
}
|