From 8505ed865a6fba5af04a9f0e3574691924de8d58 Mon Sep 17 00:00:00 2001 From: otsmr Date: Wed, 5 Aug 2026 21:47:26 +0200 Subject: [PATCH] fix url parsing --- .../visual/elements/better_text.element.dart | 2 +- .../elements/better_text_element_test.dart | 63 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 test/visual/elements/better_text_element_test.dart diff --git a/lib/src/visual/elements/better_text.element.dart b/lib/src/visual/elements/better_text.element.dart index 3e92b08e..3a1647ac 100644 --- a/lib/src/visual/elements/better_text.element.dart +++ b/lib/src/visual/elements/better_text.element.dart @@ -13,7 +13,7 @@ class BetterText extends StatelessWidget { Widget build(BuildContext context) { // Regular expression to find URLs and domains final urlRegExp = RegExp( - r'(?:(?:https?://|www\.)[^\s]+|(?:[a-zA-Z0-9-]+\.[a-zA-Z]{2,}))', + r'''(?:(?:https?://|www\.)(?:[^\s()<>]+|\([^\s()<>]+\))+(?:\([^\s()<>]+\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))|(?:(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,})''', caseSensitive: false, ); diff --git a/test/visual/elements/better_text_element_test.dart b/test/visual/elements/better_text_element_test.dart new file mode 100644 index 00000000..eca25644 --- /dev/null +++ b/test/visual/elements/better_text_element_test.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:twonly/src/visual/elements/better_text.element.dart'; + +void main() { + testWidgets('BetterText parses URLs correctly', (WidgetTester tester) async { + const text = + 'Test: (https://google.com) and another link https://example.com/#fragment, plus www.test.com. Also check https://wikipedia.org/wiki/Test_(disambiguation) !'; + + await tester.pumpWidget( + const MaterialApp( + home: Scaffold( + body: BetterText( + text: text, + textColor: Colors.black, + ), + ), + ), + ); + + final richTextFinder = find.byType(RichText); + expect(richTextFinder, findsWidgets); + + final richTexts = tester.widgetList(richTextFinder); + final parsedTexts = []; + + void extractTexts(InlineSpan span) { + if (span is TextSpan) { + if (span.text != null) parsedTexts.add(span.text!); + if (span.children != null) { + span.children!.forEach(extractTexts); + } + } + } + + for (final richText in richTexts) { + extractTexts(richText.text); + } + + // BetterText creates one span for text, one for link, etc. + // The URLs will be parsed as individual TextSpans inside the top-level TextSpan. + expect( + parsedTexts.contains('https://google.com'), + isTrue, + reason: 'Parenthesis should not be in the URL', + ); + expect( + parsedTexts.contains('https://example.com/#fragment'), + isTrue, + reason: 'Hashtag/fragment should be in the URL', + ); + expect( + parsedTexts.contains('www.test.com'), + isTrue, + reason: 'Trailing period should not be in the URL', + ); + expect( + parsedTexts.contains('https://wikipedia.org/wiki/Test_(disambiguation)'), + isTrue, + reason: 'Should parse URLs with parentheses correctly', + ); + }); +}