Skip to content

autocomplete: Add and test MentionAutocompleteQuery #86

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions lib/model/autocomplete.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import '../api/model/model.dart';

class MentionAutocompleteQuery {
MentionAutocompleteQuery(this.raw)
: _lowercaseWords = raw.toLowerCase().split(' ');

final String raw;

final List<String> _lowercaseWords;

bool testUser(User user) {
// TODO test email too, not just name
// TODO test with diacritics stripped, where appropriate

// TODO cache, elsewhere
final List<String> nameWords = user.fullName.toLowerCase().split(' ');

int nameWordsIndex = 0;
int queryWordsIndex = 0;
while (true) {
if (queryWordsIndex == _lowercaseWords.length) {
return true;
}
if (nameWordsIndex == nameWords.length) {
return false;
}

if (nameWords[nameWordsIndex].startsWith(_lowercaseWords[queryWordsIndex])) {
queryWordsIndex++;
}
nameWordsIndex++;
}
}

@override
bool operator ==(Object other) {
return other is MentionAutocompleteQuery && other.raw == raw;
}

@override
int get hashCode => Object.hash('MentionAutocompleteQuery', raw);
}
21 changes: 21 additions & 0 deletions test/example_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,27 @@ final Uri realmUrl = Uri.parse('https://chat.example/');
const String recentZulipVersion = '6.1';
const int recentZulipFeatureLevel = 164;

User user({int? userId, String? fullName}) {
return User(
userId: userId ?? 123, // TODO generate example IDs
deliveryEmailStaleDoNotUse: '[email protected]',
email: '[email protected]', // TODO generate example emails
fullName: fullName ?? 'A user',// TODO generate example names
dateJoined: '2023-04-28',
isActive: true,
isOwner: false,
isAdmin: false,
isGuest: false,
isBillingAdmin: false,
isBot: false,
role: 400,
timezone: 'UTC',
avatarUrl: null,
avatarVersion: 0,
profileData: null,
);
}

final Account selfAccount = Account(
id: 1001,
realmUrl: realmUrl,
Expand Down
44 changes: 44 additions & 0 deletions test/model/autocomplete_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import 'package:checks/checks.dart';
import 'package:test/scaffolding.dart';
import 'package:zulip/api/model/model.dart';
import 'package:zulip/model/autocomplete.dart';

import '../example_data.dart' as eg;

void main() {
test('MentionAutocompleteQuery.testUser', () {
doCheck(String rawQuery, User user, bool expected) {
final result = MentionAutocompleteQuery(rawQuery).testUser(user);
expected ? check(result).isTrue() : check(result).isFalse();
}

doCheck('', eg.user(fullName: 'Full Name'), true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also include tests with empty fullName ­— one where query is also empty, one where nonempty.

That isn't a super likely case, but it wouldn't be hard to have a bug where the search crashed on it.

doCheck('', eg.user(fullName: ''), true); // Unlikely case, but should not crash
doCheck('Full Name', eg.user(fullName: 'Full Name'), true);
doCheck('full name', eg.user(fullName: 'Full Name'), true);
doCheck('Full Name', eg.user(fullName: 'full name'), true);
doCheck('Full', eg.user(fullName: 'Full Name'), true);
doCheck('Name', eg.user(fullName: 'Full Name'), true);
doCheck('Full Name', eg.user(fullName: 'Fully Named'), true);
doCheck('Full Four', eg.user(fullName: 'Full Name Four Words'), true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also test that "F Full" and "Four F" don't match this name, while "Full F" and "F Four" do.

doCheck('Name Words', eg.user(fullName: 'Full Name Four Words'), true);
doCheck('Full F', eg.user(fullName: 'Full Name Four Words'), true);
doCheck('F Four', eg.user(fullName: 'Full Name Four Words'), true);
doCheck('full full', eg.user(fullName: 'Full Full Name'), true);
doCheck('full full', eg.user(fullName: 'Full Name Full'), true);

doCheck('F', eg.user(fullName: ''), false); // Unlikely case, but should not crash
doCheck('Fully Named', eg.user(fullName: 'Full Name'), false);
doCheck('Full Name', eg.user(fullName: 'Full'), false);
doCheck('Full Name', eg.user(fullName: 'Name'), false);
doCheck('ull ame', eg.user(fullName: 'Full Name'), false);
doCheck('ull Name', eg.user(fullName: 'Full Name'), false);
doCheck('Full ame', eg.user(fullName: 'Full Name'), false);
doCheck('Full Full', eg.user(fullName: 'Full Name'), false);
doCheck('Name Name', eg.user(fullName: 'Full Name'), false);
Comment on lines +37 to +38
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also test that "full full" does match "Full Full Name" and "Full Name Full".

doCheck('Name Full', eg.user(fullName: 'Full Name'), false);
doCheck('Name Four Full Words', eg.user(fullName: 'Full Name Four Words'), false);
doCheck('F Full', eg.user(fullName: 'Full Name Four Words'), false);
doCheck('Four F', eg.user(fullName: 'Full Name Four Words'), false);
});
}