implemented in-chat search with text highlighting, added search navigation UI, and integrated scrollable list for message jumping
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:marianum_mobile/api/marianumcloud/talk/chat/get_chat_response.dart';
|
||||
import 'package:marianum_mobile/api/marianumcloud/talk/room/get_room_response.dart';
|
||||
import 'package:marianum_mobile/view/pages/talk/data/chat_search_controller.dart';
|
||||
|
||||
GetChatResponseObject _msg({
|
||||
required int id,
|
||||
required int timestamp,
|
||||
String actorDisplayName = 'Anyone',
|
||||
String message = '',
|
||||
String systemMessage = '',
|
||||
GetRoomResponseObjectMessageType type =
|
||||
GetRoomResponseObjectMessageType.comment,
|
||||
Map<String, RichObjectString>? params,
|
||||
}) =>
|
||||
GetChatResponseObject(
|
||||
id,
|
||||
'token',
|
||||
GetRoomResponseObjectMessageActorType.user,
|
||||
'actor-id',
|
||||
actorDisplayName,
|
||||
timestamp,
|
||||
systemMessage,
|
||||
type,
|
||||
true,
|
||||
'',
|
||||
message,
|
||||
params,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
|
||||
GetChatResponse _response(List<GetChatResponseObject> messages) =>
|
||||
GetChatResponse(messages.toSet());
|
||||
|
||||
void main() {
|
||||
group('ChatSearchController.findMatches', () {
|
||||
test('empty query returns no matches', () {
|
||||
final response = _response([
|
||||
_msg(id: 1, timestamp: 100, message: 'Hallo'),
|
||||
]);
|
||||
expect(ChatSearchController.findMatches(response, ''), isEmpty);
|
||||
expect(ChatSearchController.findMatches(response, ' '), isEmpty);
|
||||
});
|
||||
|
||||
test('matches message text case-insensitively', () {
|
||||
final response = _response([
|
||||
_msg(id: 1, timestamp: 100, message: 'Hallo Welt'),
|
||||
_msg(id: 2, timestamp: 200, message: 'nichts hier'),
|
||||
]);
|
||||
final matches = ChatSearchController.findMatches(response, 'WELT');
|
||||
expect(matches.length, 1);
|
||||
expect(matches.first.messageId, 1);
|
||||
});
|
||||
|
||||
test('matches actor display name', () {
|
||||
final response = _response([
|
||||
_msg(
|
||||
id: 1,
|
||||
timestamp: 100,
|
||||
actorDisplayName: 'Lisa Maier',
|
||||
message: 'irgendwas',
|
||||
),
|
||||
_msg(
|
||||
id: 2,
|
||||
timestamp: 200,
|
||||
actorDisplayName: 'Tom Weber',
|
||||
message: 'auch was',
|
||||
),
|
||||
]);
|
||||
final matches = ChatSearchController.findMatches(response, 'lisa');
|
||||
expect(matches.length, 1);
|
||||
expect(matches.first.messageId, 1);
|
||||
});
|
||||
|
||||
test('system messages match on text but not on actor', () {
|
||||
final response = _response([
|
||||
_msg(
|
||||
id: 1,
|
||||
timestamp: 100,
|
||||
actorDisplayName: 'Lisa',
|
||||
message: 'Lisa ist beigetreten',
|
||||
type: GetRoomResponseObjectMessageType.system,
|
||||
),
|
||||
]);
|
||||
// Match on text content
|
||||
expect(
|
||||
ChatSearchController.findMatches(response, 'beigetreten').length,
|
||||
1,
|
||||
);
|
||||
// Actor name alone (not in text) should not match for system messages
|
||||
final actorOnlyResponse = _response([
|
||||
_msg(
|
||||
id: 1,
|
||||
timestamp: 100,
|
||||
actorDisplayName: 'Lisa',
|
||||
message: 'jemand ist beigetreten',
|
||||
type: GetRoomResponseObjectMessageType.system,
|
||||
),
|
||||
]);
|
||||
expect(
|
||||
ChatSearchController.findMatches(actorOnlyResponse, 'lisa'),
|
||||
isEmpty,
|
||||
);
|
||||
});
|
||||
|
||||
test('reaction and poll_voted system messages are filtered out', () {
|
||||
final response = _response([
|
||||
_msg(
|
||||
id: 1,
|
||||
timestamp: 100,
|
||||
message: 'Treffer',
|
||||
systemMessage: 'reaction',
|
||||
type: GetRoomResponseObjectMessageType.system,
|
||||
),
|
||||
_msg(
|
||||
id: 2,
|
||||
timestamp: 200,
|
||||
message: 'Treffer',
|
||||
systemMessage: 'poll_voted',
|
||||
type: GetRoomResponseObjectMessageType.system,
|
||||
),
|
||||
_msg(id: 3, timestamp: 300, message: 'Treffer'),
|
||||
]);
|
||||
final matches = ChatSearchController.findMatches(response, 'Treffer');
|
||||
expect(matches.length, 1);
|
||||
expect(matches.first.messageId, 3);
|
||||
});
|
||||
|
||||
test('rich object parameters are searchable (e.g. file names)', () {
|
||||
final response = _response([
|
||||
_msg(
|
||||
id: 1,
|
||||
timestamp: 100,
|
||||
message: '{file}',
|
||||
params: {
|
||||
'file': RichObjectString(
|
||||
RichObjectStringObjectType.file,
|
||||
'42',
|
||||
'hausaufgaben.pdf',
|
||||
null,
|
||||
null,
|
||||
),
|
||||
},
|
||||
),
|
||||
]);
|
||||
final matches = ChatSearchController.findMatches(response, 'hausaufgab');
|
||||
expect(matches.length, 1);
|
||||
expect(matches.first.messageId, 1);
|
||||
});
|
||||
|
||||
test('matches are sorted newest first', () {
|
||||
final response = _response([
|
||||
_msg(id: 1, timestamp: 100, message: 'X'),
|
||||
_msg(id: 2, timestamp: 300, message: 'X'),
|
||||
_msg(id: 3, timestamp: 200, message: 'X'),
|
||||
]);
|
||||
final matches = ChatSearchController.findMatches(response, 'x');
|
||||
expect(matches.map((m) => m.messageId).toList(), [2, 3, 1]);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user