clickable http links in the Claude conversation; bundle markdown hooks (T-253)
http(s) links (typed or autolinked) in the conversation now open via the OS URL handler (OsBridge.openURL) on click, with a hover underline + pointer; non-http schemes stay inert. Works across prose, lists, tables, and headings. Refactor: ClideMarkdown's growing set of inline-interaction callbacks (onRecordTap, onImageToken, onLinkTap) is bundled into one ClideMarkdownHooks value threaded as a single param — no more per-callback threading, and the hooks now reach every context uniformly (links/images previously only worked in some). The public widget API is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
/// Tests for ClideMarkdown link handling (T-253): http(s) links are tappable and
|
||||
/// hand the URL to the caller; other schemes stay inert.
|
||||
library;
|
||||
|
||||
import 'package:clide/widgets/widgets.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../../helpers/kernel_fixture.dart';
|
||||
import '../../helpers/widget_harness.dart';
|
||||
|
||||
void main() {
|
||||
late KernelFixture f;
|
||||
setUp(() async => f = await KernelFixture.create());
|
||||
tearDown(() => f.dispose());
|
||||
|
||||
testWidgets('an autolinked bare URL is tappable and opens via onLinkTap', (tester) async {
|
||||
String? opened;
|
||||
await tester.pumpWidget(harness(f, ClideMarkdown('see https://example.com/x for more', onLinkTap: (u) => opened = u)));
|
||||
await tester.pump();
|
||||
|
||||
await tester.tap(find.text('https://example.com/x'));
|
||||
await tester.pump();
|
||||
expect(opened, 'https://example.com/x');
|
||||
});
|
||||
|
||||
testWidgets('a markdown [text](url) link opens the href, not the text', (tester) async {
|
||||
String? opened;
|
||||
await tester.pumpWidget(harness(f, ClideMarkdown('[the docs](https://clide.dev/docs)', onLinkTap: (u) => opened = u)));
|
||||
await tester.pump();
|
||||
|
||||
await tester.tap(find.text('the docs'));
|
||||
await tester.pump();
|
||||
expect(opened, 'https://clide.dev/docs');
|
||||
});
|
||||
|
||||
testWidgets('a non-http scheme is not tappable (no handler fired)', (tester) async {
|
||||
var calls = 0;
|
||||
await tester.pumpWidget(harness(f, ClideMarkdown('[mail](mailto:a@b.com)', onLinkTap: (_) => calls++)));
|
||||
await tester.pump();
|
||||
|
||||
await tester.tap(find.text('mail'), warnIfMissed: false);
|
||||
await tester.pump();
|
||||
expect(calls, 0);
|
||||
});
|
||||
|
||||
testWidgets('with no onLinkTap, a link still renders (inert, no crash)', (tester) async {
|
||||
await tester.pumpWidget(harness(f, const ClideMarkdown('see https://example.com here')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.textContaining('https://example.com'), findsOneWidget);
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user