183 lines
7.0 KiB
Dart
183 lines
7.0 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:ikar/core/links/max_link.dart';
|
|
import 'package:ikar/frontend/widgets/link_text.dart';
|
|
|
|
void main() {
|
|
group('linkPattern', () {
|
|
List<String> matches(String text) =>
|
|
linkPattern.allMatches(text).map((m) => m.group(0)!).toList();
|
|
|
|
test('picks up a bare ikar.kusoft.xyz link inside plain text', () {
|
|
expect(
|
|
matches('Ваша ссылка 👇\nikar.kusoft.xyz/id100000000001_bot?start=abc123\n'),
|
|
['ikar.kusoft.xyz/id100000000001_bot?start=abc123'],
|
|
);
|
|
expect(matches('зайди на ikar.kusoft.xyz и посмотри'), ['ikar.kusoft.xyz']);
|
|
});
|
|
|
|
test('still picks up schemed and www links', () {
|
|
expect(matches('https://ikar.kusoft.xyz/somebot?start=x'), [
|
|
'https://ikar.kusoft.xyz/somebot?start=x',
|
|
]);
|
|
expect(matches('ikar.kusoft.xyz/somebot'), ['ikar.kusoft.xyz/somebot']);
|
|
});
|
|
|
|
test('does not match look-alike hosts or emails', () {
|
|
expect(matches('evil.ikar.kusoft.xyz/phish'), isEmpty);
|
|
expect(matches('ikar.kusoft.xyz.evil.com/phish'), isEmpty);
|
|
expect(matches('bot@ikar.kusoft.xyz'), isEmpty);
|
|
expect(matches('ikar.kusoft.xyzles/somebot'), isEmpty);
|
|
});
|
|
|
|
test('linkTarget adds a scheme only when missing', () {
|
|
expect(linkTarget('ikar.kusoft.xyz/somebot'), 'https://ikar.kusoft.xyz/somebot');
|
|
expect(linkTarget('ikar.kusoft.xyz/somebot'), 'https://ikar.kusoft.xyz/somebot');
|
|
expect(linkTarget('http://ikar.kusoft.xyz/somebot'), 'http://ikar.kusoft.xyz/somebot');
|
|
expect(linkTarget('https://ikar.kusoft.xyz/somebot'), 'https://ikar.kusoft.xyz/somebot');
|
|
});
|
|
});
|
|
|
|
group('MaxLink.parse — не наши ссылки', () {
|
|
test('rejects other hosts and non-links', () {
|
|
expect(MaxLink.parse('https://example.com/somebot'), isNull);
|
|
expect(MaxLink.parse('evil.ikar.kusoft.xyz/phish'), isNull);
|
|
expect(MaxLink.parse('mailto:bot@ikar.kusoft.xyz'), isNull);
|
|
expect(MaxLink.parse(''), isNull);
|
|
});
|
|
|
|
test('rejects reserved site pages', () {
|
|
expect(MaxLink.parse('https://ikar.kusoft.xyz/login'), isNull);
|
|
expect(MaxLink.parse('https://ikar.kusoft.xyz/tos'), isNull);
|
|
});
|
|
});
|
|
|
|
group('MaxLink.parse — контентные ссылки', () {
|
|
test('bare host, http and www all normalize to the root link', () {
|
|
expect(MaxLink.parse('ikar.kusoft.xyz'), isA<MaxRootLink>());
|
|
expect(MaxLink.parse('http://ikar.kusoft.xyz/'), isA<MaxRootLink>());
|
|
expect(MaxLink.parse('https://ikar.kusoft.xyz'), isA<MaxRootLink>());
|
|
expect(MaxLink.parse('max://ikar.kusoft.xyz/'), isA<MaxRootLink>());
|
|
});
|
|
|
|
test('public link keeps the canonical https url', () {
|
|
final link = MaxLink.parse('ikar.kusoft.xyz/somebot') as MaxContentLink;
|
|
|
|
expect(link.kind, MaxContentKind.public);
|
|
expect(link.url, 'https://ikar.kusoft.xyz/somebot');
|
|
expect(link.baseUrl, 'https://ikar.kusoft.xyz/somebot');
|
|
expect(link.startPayload, isNull);
|
|
expect(link.messageId, isNull);
|
|
});
|
|
|
|
test('@nickname is a public link too', () {
|
|
final link = MaxLink.parse('https://ikar.kusoft.xyz/@somebot') as MaxContentLink;
|
|
|
|
expect(link.kind, MaxContentKind.public);
|
|
expect(link.baseUrl, 'https://ikar.kusoft.xyz/somebot');
|
|
});
|
|
|
|
test('bot start payload is parsed and the base url drops it', () {
|
|
final link =
|
|
MaxLink.parse('http://ikar.kusoft.xyz/id100000000001_bot?start=a%20b')
|
|
as MaxContentLink;
|
|
|
|
expect(link.startPayload, 'a b');
|
|
expect(link.url, 'https://ikar.kusoft.xyz/id100000000001_bot?start=a%20b');
|
|
expect(link.baseUrl, 'https://ikar.kusoft.xyz/id100000000001_bot');
|
|
});
|
|
|
|
test('empty start payload is ignored', () {
|
|
final link = MaxLink.parse('ikar.kusoft.xyz/somebot?start=') as MaxContentLink;
|
|
expect(link.startPayload, isNull);
|
|
});
|
|
|
|
test('startapp opens a mini app and is cut at the first &', () {
|
|
final link =
|
|
MaxLink.parse('ikar.kusoft.xyz/somebot?startapp=deal%2F42&ref=x')
|
|
as MaxWebAppLink;
|
|
|
|
expect(link.startApp, 'deal/42');
|
|
});
|
|
|
|
test('message links carry the message id', () {
|
|
final byName =
|
|
MaxLink.parse('ikar.kusoft.xyz/somechannel/900000000000000001')
|
|
as MaxContentLink;
|
|
expect(byName.kind, MaxContentKind.public);
|
|
expect(byName.messageId, 900000000000000001);
|
|
expect(byName.baseUrl, 'https://ikar.kusoft.xyz/somechannel');
|
|
|
|
final byId =
|
|
MaxLink.parse('ikar.kusoft.xyz/c/424242/900000000000000001') as MaxContentLink;
|
|
expect(byId.kind, MaxContentKind.content);
|
|
expect(byId.messageId, 900000000000000001);
|
|
expect(byId.baseUrl, 'https://ikar.kusoft.xyz/c/424242');
|
|
});
|
|
|
|
test('invite, call and sticker links keep their own types', () {
|
|
expect(
|
|
(MaxLink.parse('ikar.kusoft.xyz/join/AbCdEf') as MaxContentLink).kind,
|
|
MaxContentKind.invite,
|
|
);
|
|
expect(MaxLink.parse('ikar.kusoft.xyz/joincall/AbCdEf'), isA<MaxCallLink>());
|
|
expect(
|
|
(MaxLink.parse('ikar.kusoft.xyz/stickerset/512-abc') as MaxStickerSetLink).path,
|
|
'stickerset/512-abc',
|
|
);
|
|
});
|
|
|
|
test('uid and cid open a contact and a chat', () {
|
|
expect(
|
|
(MaxLink.parse('max://ikar.kusoft.xyz/?uid=434343') as MaxContactIdLink).userId,
|
|
434343,
|
|
);
|
|
final chat = MaxLink.parse('max://ikar.kusoft.xyz/?cid=424242') as MaxChatIdLink;
|
|
expect(chat.chatId, 424242);
|
|
expect(chat.messageId, isNull);
|
|
});
|
|
});
|
|
|
|
group('MaxLink.parse — внутренние маршруты', () {
|
|
test('auth keeps the full url with its token', () {
|
|
final link = MaxLink.parse('https://ikar.kusoft.xyz/:auth/tok3n') as MaxAuthLink;
|
|
expect(link.url, 'https://ikar.kusoft.xyz/:auth/tok3n');
|
|
});
|
|
|
|
test('share and share-self-out are separate targets', () {
|
|
final share =
|
|
MaxLink.parse('https://ikar.kusoft.xyz/:share?text=%D0%BF%D1%80%D0%B8%D0%B2')
|
|
as MaxShareTextLink;
|
|
expect(share.text, 'прив');
|
|
expect(
|
|
MaxLink.parse('https://ikar.kusoft.xyz/:share-self-out'),
|
|
isA<MaxShareSelfLink>(),
|
|
);
|
|
});
|
|
|
|
test('folder needs an id, otherwise it stays a plain route', () {
|
|
expect(
|
|
(MaxLink.parse('ikar.kusoft.xyz/:folder?id=42') as MaxFolderLink).folderId,
|
|
'42',
|
|
);
|
|
expect(MaxLink.parse('ikar.kusoft.xyz/:folder'), isA<MaxRouteLink>());
|
|
});
|
|
|
|
test('current is a no-op target', () {
|
|
expect(MaxLink.parse('ikar.kusoft.xyz/:current'), isA<MaxCurrentLink>());
|
|
});
|
|
|
|
test('other routes keep their path and query params', () {
|
|
final route =
|
|
MaxLink.parse('max://ikar.kusoft.xyz/:profile?id=123&type=CHAT')
|
|
as MaxRouteLink;
|
|
|
|
expect(route.route, ':profile');
|
|
expect(route.params, {'id': '123', 'type': 'CHAT'});
|
|
|
|
final nested =
|
|
MaxLink.parse('https://ikar.kusoft.xyz/:Settings/Appearance') as MaxRouteLink;
|
|
expect(nested.route, ':settings/appearance');
|
|
});
|
|
});
|
|
}
|