add regex tests
This commit is contained in:
parent
870892fdcf
commit
67bb7a548a
|
@ -1,3 +1,4 @@
|
||||||
|
/// A lightweight flutter package to linkify texts containing urls, emails and hashtags.
|
||||||
library linkify_text;
|
library linkify_text;
|
||||||
|
|
||||||
export 'src/linkify.dart';
|
export 'src/linkify.dart';
|
||||||
|
|
|
@ -1,24 +1,72 @@
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:linkify_text/src/utils/regex.dart';
|
||||||
|
|
||||||
enum LinkOptions { url, email, hashTag }
|
enum LinkOption { url, email, hashTag }
|
||||||
|
|
||||||
TextSpan formatText(
|
/// Linkify [text] containing urls, emails or hashtag
|
||||||
{required String text, TextStyle? textStyle, TextStyle? linkStyle}) {
|
class LinkifyText extends StatelessWidget {
|
||||||
RegExp re = RegExp(
|
const LinkifyText(this.text,
|
||||||
r"(((ht|f)tp(s?)://)|www.)[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*/?([a-zA-Z0-9\-.\?,'/\\\+&%\$#_=]*)?");
|
{this.textStyle, this.linkStyle, this.options, this.onTap, Key? key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
// return the full text if there's no match
|
/// text to be linkified
|
||||||
if (!re.hasMatch(text)) {
|
final String text;
|
||||||
return TextSpan(text: text, style: textStyle);
|
|
||||||
|
/// [textStyle] applied the text
|
||||||
|
final TextStyle? textStyle;
|
||||||
|
|
||||||
|
/// [textStyle] added to the formatted links in the text
|
||||||
|
final TextStyle? linkStyle;
|
||||||
|
|
||||||
|
/// called when a formatted link is pressed, it returns the link as a parameter
|
||||||
|
/// ```dart
|
||||||
|
/// LinkifyText("#helloWorld", onTap: (value) {
|
||||||
|
/// // do stuff with value
|
||||||
|
/// print("$value hashtag was tapped");
|
||||||
|
/// });
|
||||||
|
/// ```
|
||||||
|
final void Function(String)? onTap;
|
||||||
|
|
||||||
|
/// option to override the links to be formatted in the text, defaults to `[LinkOption.url]`
|
||||||
|
/// so only urls are being linkified in the text
|
||||||
|
final List<LinkOption>? options;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// TODO: add all Text properties
|
||||||
|
return Text.rich(
|
||||||
|
formatText(
|
||||||
|
text: text,
|
||||||
|
linkStyle: linkStyle,
|
||||||
|
onTap: onTap,
|
||||||
|
options: options,
|
||||||
|
),
|
||||||
|
key: key,
|
||||||
|
style: linkStyle,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List<String> texts = text.split(re);
|
TextSpan formatText({
|
||||||
|
String text = '',
|
||||||
|
TextStyle? linkStyle,
|
||||||
|
List<LinkOption>? options = const [LinkOption.url],
|
||||||
|
Function(String)? onTap,
|
||||||
|
}) {
|
||||||
|
RegExp _regExp = constructRegExpFromOptions(options!);
|
||||||
|
|
||||||
|
// return the full text if there's no match or if empty
|
||||||
|
if (!_regExp.hasMatch(text) || text.isEmpty) return TextSpan(text: text);
|
||||||
|
|
||||||
|
List<String> texts = text.split(_regExp);
|
||||||
List<TextSpan> spans = [];
|
List<TextSpan> spans = [];
|
||||||
List<RegExpMatch> links = re.allMatches(text).toList();
|
List<RegExpMatch> links = _regExp.allMatches(text).toList();
|
||||||
|
|
||||||
for (String text in texts) {
|
for (String text in texts) {
|
||||||
spans.add(TextSpan(text: text, style: textStyle));
|
spans.add(TextSpan(
|
||||||
|
text: text,
|
||||||
|
));
|
||||||
if (links.length > 0) {
|
if (links.length > 0) {
|
||||||
RegExpMatch match = links.removeAt(0);
|
RegExpMatch match = links.removeAt(0);
|
||||||
String link = match.input.substring(match.start, match.end);
|
String link = match.input.substring(match.start, match.end);
|
||||||
|
@ -30,10 +78,7 @@ TextSpan formatText(
|
||||||
style: linkStyle,
|
style: linkStyle,
|
||||||
recognizer: TapGestureRecognizer()
|
recognizer: TapGestureRecognizer()
|
||||||
..onTap = () {
|
..onTap = () {
|
||||||
String l = link.startsWith('www') ? 'http://$link' : link;
|
onTap!(link);
|
||||||
if (l.endsWith('.') || l.endsWith(',') || l.endsWith(';')) {
|
|
||||||
l = l.substring(0, l.length - 1);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,7 +1,40 @@
|
||||||
RegExp urlRegex = RegExp(r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+');
|
import 'package:linkify_text/linkify_text.dart';
|
||||||
|
|
||||||
RegExp hashtagRegex =
|
String urlRegExp = r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+';
|
||||||
RegExp(r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+');
|
|
||||||
|
|
||||||
RegExp emailRegex = RegExp(
|
String hashtagRegExp = r'(#+[a-zA-Z0-9(_)]{1,})';
|
||||||
r"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$");
|
|
||||||
|
String emailRegExp =
|
||||||
|
r"([a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+)";
|
||||||
|
|
||||||
|
/// construct regexp. pattern from provided link options
|
||||||
|
RegExp constructRegExpFromOptions(List<LinkOption> options) {
|
||||||
|
// default case where we always want to match url strings
|
||||||
|
if (options.length == 1 && options.first == LinkOption.url)
|
||||||
|
return RegExp(urlRegExp);
|
||||||
|
|
||||||
|
StringBuffer _regexBuffer = StringBuffer();
|
||||||
|
for (var i = 0; i < options.length; i++) {
|
||||||
|
final o = options[i];
|
||||||
|
final isLast = i == options.length - 1;
|
||||||
|
switch (o) {
|
||||||
|
case LinkOption.url:
|
||||||
|
isLast
|
||||||
|
? _regexBuffer.write("($urlRegExp)")
|
||||||
|
: _regexBuffer.write("($urlRegExp)|");
|
||||||
|
break;
|
||||||
|
case LinkOption.hashTag:
|
||||||
|
isLast
|
||||||
|
? _regexBuffer.write("($hashtagRegExp)")
|
||||||
|
: _regexBuffer.write("($hashtagRegExp)|");
|
||||||
|
break;
|
||||||
|
case LinkOption.email:
|
||||||
|
isLast
|
||||||
|
? _regexBuffer.write("($emailRegExp)")
|
||||||
|
: _regexBuffer.write("($emailRegExp)|");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return RegExp(_regexBuffer.toString());
|
||||||
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
nullify(dynamic value) {}
|
|
|
@ -1,49 +0,0 @@
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
import 'package:linkify_text/src/utils/regex.dart';
|
|
||||||
|
|
||||||
// final text =
|
|
||||||
// "My website url: https://blasanka.github.io/Google search using: www.google.com, social media is facebook.com, http://example.com/method?param=flutterstackoverflow.com is my greatest website. DartPad share: https://github.com/dart-lang/dart-pad/wiki/Sharing-Guide, see this example and edit it here";
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
group('Regular Expression', () {
|
|
||||||
test("Should match all emails", () {
|
|
||||||
List<String> _emails = ["hello@world.com", "foo.bar@js.com"];
|
|
||||||
_emails.forEach((e) {
|
|
||||||
bool hasMatch = emailRegex.hasMatch(e);
|
|
||||||
expect(hasMatch, true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
test("Should match all urls", () {
|
|
||||||
List<String> _urls = [
|
|
||||||
"http://domain.com",
|
|
||||||
"http://domain.com/",
|
|
||||||
"https://domain.com",
|
|
||||||
"https://domain.com/",
|
|
||||||
"https://www.domain.com",
|
|
||||||
"www.domain.com",
|
|
||||||
"https://domain.com/Google?",
|
|
||||||
"https://domain.com/Google/search",
|
|
||||||
"https://domain.com/Google?param=",
|
|
||||||
"https://domain.com/Google?param=helloworld",
|
|
||||||
"https://sub.domain.com/Google?param=helloworld#hash",
|
|
||||||
];
|
|
||||||
_urls.forEach((u) {
|
|
||||||
bool hasMatch = emailRegex.hasMatch(u);
|
|
||||||
expect(hasMatch, true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
test("Should match all hashtags", () {
|
|
||||||
List<String> _hashtags = [
|
|
||||||
"#1",
|
|
||||||
"#trending",
|
|
||||||
"#_trending",
|
|
||||||
"#TRENDING",
|
|
||||||
"#trending_topic",
|
|
||||||
];
|
|
||||||
_hashtags.forEach((h) {
|
|
||||||
bool hasMatch = emailRegex.hasMatch(h);
|
|
||||||
expect(hasMatch, true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
import 'package:linkify_text/linkify_text.dart';
|
||||||
|
import 'package:linkify_text/src/utils/regex.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
// TODO: write more testsss
|
||||||
|
void main() {
|
||||||
|
group('Regular Expression', () {
|
||||||
|
/// test values
|
||||||
|
final _urlText =
|
||||||
|
"My website url: https://hello.com/GOOGLE search using: www.google.com, social media is facebook.com, http://example.com/method?param=fullstackoverflow.dev";
|
||||||
|
final _hashtagText = "#helloWorld and #dev are trending";
|
||||||
|
final _emailText =
|
||||||
|
"My email address is hey@stanleee.me and dev@gmail.com, yah!";
|
||||||
|
final _text = _urlText + _hashtagText + _emailText;
|
||||||
|
|
||||||
|
final _emails = ["hello@world.com", "foo.bar@js.com"];
|
||||||
|
|
||||||
|
final List<String> _urls = [
|
||||||
|
"http://domain.com",
|
||||||
|
"http://domain.com/",
|
||||||
|
"https://domain.com",
|
||||||
|
"https://domain.com/",
|
||||||
|
"https://www.domain.com",
|
||||||
|
"www.domain.com",
|
||||||
|
"https://domain.com/Google?",
|
||||||
|
"https://domain.com/Google/search",
|
||||||
|
"https://domain.com/Google?param=",
|
||||||
|
"https://domain.com/Google?param=helloworld",
|
||||||
|
"https://sub.domain.com/Google?param=helloworld#hash",
|
||||||
|
];
|
||||||
|
|
||||||
|
final List<String> _hashtags = [
|
||||||
|
"#123",
|
||||||
|
"#H",
|
||||||
|
"#0",
|
||||||
|
"#trending",
|
||||||
|
"#_trending",
|
||||||
|
"#TRENDING",
|
||||||
|
"#trending_topic",
|
||||||
|
];
|
||||||
|
|
||||||
|
///
|
||||||
|
test("Should match all emails", () {
|
||||||
|
_emails.forEach((e) {
|
||||||
|
bool hasMatch = RegExp(emailRegExp).hasMatch(e);
|
||||||
|
expect(hasMatch, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Should not match all emails", () {
|
||||||
|
List<String> _emails = ["hello@world", "@js.com"];
|
||||||
|
_emails.forEach((e) {
|
||||||
|
bool hasMatch = RegExp(emailRegExp).hasMatch(e);
|
||||||
|
expect(hasMatch, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Should match all urls", () {
|
||||||
|
_urls.forEach((u) {
|
||||||
|
bool hasMatch = RegExp(urlRegExp).hasMatch(u);
|
||||||
|
expect(hasMatch, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Should match all hashtags", () {
|
||||||
|
_hashtags.forEach((h) {
|
||||||
|
bool hasMatch = RegExp(hashtagRegExp).hasMatch(h);
|
||||||
|
expect(hasMatch, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Should construct regex pattern from LinkOptions and match", () {
|
||||||
|
RegExp _urlRegExp = constructRegExpFromOptions([LinkOption.url]);
|
||||||
|
RegExp _hashtagRegExp = constructRegExpFromOptions([LinkOption.hashTag]);
|
||||||
|
RegExp _emailRegExp = constructRegExpFromOptions([LinkOption.email]);
|
||||||
|
RegExp _textRegExp = constructRegExpFromOptions(
|
||||||
|
[LinkOption.url, LinkOption.hashTag, LinkOption.email]);
|
||||||
|
expect(_urlRegExp.allMatches(_urlText).length, 4);
|
||||||
|
expect(_hashtagRegExp.allMatches(_hashtagText).length, 2);
|
||||||
|
expect(_emailRegExp.allMatches(_emailText).length, 2);
|
||||||
|
expect(_textRegExp.allMatches(_text).length, 8);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue