Files
flutter__agent-plugins/tool/test/services/markdown_converter_test.dart
Jaime Wren 75d8d9671a Improve CLI error handling, validation edge cases, and resource paths
Addresses several points of failure during the skill generation and validation lifecycle to prevent crashes and provide better error visibility. Additionally, it expands the capability of the `resources` field to accept local relative file paths in addition to URLs.
Key changes include:
- **Expanded Resource Support:** Updated `BaseSkillCommand` and `SkillParams` to allow the `resources` list to take both URLs and relative file paths, improving local testing and generation workflows.
- **Service Robustness:** Enhanced `GeminiService` and API interactions to gracefully handle errors, empty responses, and malformed JSON payloads.
- **Command Reliability:** Updated `GenerateSkillCommand` and `ValidateSkillCommand` to detect and gracefully log generic exceptions and empty content fetches instead of failing silently or crashing.
- **HTML/Markdown Parsing:** Broadened test coverage to cover conversions for tables, definition lists (dl/dt/dd), video sources, iframes, and details/summary HTML tags.
- **Comprehensive Testing:** Added unit tests across `generate_skills_test.dart`, `validate_skills_test.dart`, and `gemini_service_test.dart` to explicitly cover invalid API responses, missing metadata fallbacks, and retry logic.
- **Documentation:** Updated `README.md` to reflect the refined parameters, new resource path capabilities, and handling logic.
2026-03-01 23:16:47 -08:00

157 lines
4.4 KiB
Dart

// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:skills/src/services/markdown_converter.dart';
import 'package:test/test.dart';
void main() {
group('MarkdownConverter', () {
late MarkdownConverter converter;
setUp(() {
converter = MarkdownConverter();
});
test('converts headers', () {
expect(converter.convert('<h1>Title</h1>'), contains('# Title'));
expect(converter.convert('<h2>Subtitle</h2>'), contains('## Subtitle'));
expect(converter.convert('<h3>Section</h3>'), contains('### Section'));
});
test('converts paragraphs', () {
expect(converter.convert('<p>Hello World</p>'), contains('Hello World'));
});
test('converts links', () {
expect(
converter.convert('<a href="https://example.com">Link</a>'),
contains('[Link](https://example.com)'),
);
});
test('converts bold and italic', () {
expect(converter.convert('<b>Bold</b>'), contains('**Bold**'));
expect(
converter.convert('<strong>Strong</strong>'),
contains('**Strong**'),
);
expect(converter.convert('<i>Italic</i>'), contains('*Italic*'));
expect(
converter.convert('<em>Emphasized</em>'),
contains('*Emphasized*'),
);
});
test('converts code', () {
expect(
converter.convert('<code>print("hello")</code>'),
contains('`print("hello")`'),
);
expect(
converter.convert('<pre>void main() {}</pre>'),
allOf(contains('```'), contains('void main() {}')),
);
});
test('converts lists', () {
const html = '''
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
''';
final md = converter.convert(html);
expect(md, contains('- Item 1'));
expect(md, contains('- Item 2'));
const htmlOl = '''
<ol>
<li>First</li>
<li>Second</li>
</ol>
''';
final mdOl = converter.convert(htmlOl);
// Currently the converter uses simplified list handling (returning - for both)
expect(mdOl, contains('- First'));
expect(mdOl, contains('- Second'));
});
test('converts line breaks', () {
expect(converter.convert('Line 1<br>Line 2'), contains('Line 1\nLine 2'));
});
test('converts structural elements', () {
expect(
converter.convert('<div>Div Content</div>'),
contains('Div Content'),
);
expect(
converter.convert('<section>Section Content</section>'),
contains('Section Content'),
);
expect(
converter.convert('<main>Main Content</main>'),
contains('Main Content'),
);
expect(
converter.convert('<article>Article Content</article>'),
contains('Article Content'),
);
});
test('handles empty body', () {
expect(converter.convert(''), isEmpty);
});
test('converts table without thead or tbody', () {
const html = '''
<table>
<tr><td>Data 1</td><td>Data 2</td></tr>
<tr><td>Data 3</td><td>Data 4</td></tr>
</table>
''';
expect(converter.convert(html), contains('| Data 1 | Data 2 |'));
expect(converter.convert(html), contains('| Data 3 | Data 4 |'));
});
test('converts video tag with nested source', () {
expect(
converter.convert(
'<video><source src="https://example.com/video.mp4"></video>',
),
contains('[Video](https://example.com/video.mp4)'),
);
});
test('converts definition lists (dl, dt, dd)', () {
const html = '''
<dl>
<dt>Term 1</dt>
<dd>Definition 1</dd>
</dl>
''';
final md = converter.convert(html);
expect(md, contains('**Term 1**'));
expect(md, contains('Definition 1'));
});
test('converts iframes', () {
expect(
converter.convert('<iframe src="https://example.com/embed"></iframe>'),
contains('[Iframe](https://example.com/embed)'),
);
});
test('retains details and summary as HTML', () {
const html = '''
<details>
<summary>Click to expand</summary>
Hidden content
</details>
''';
final md = converter.convert(html);
expect(md, contains('<details>'));
});
});
}