matrix-dart-sdk/test/event_test.dart

2979 lines
93 KiB
Dart

/*
* Famedly Matrix SDK
* Copyright (C) 2019, 2020 Famedly GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import 'dart:convert';
import 'dart:typed_data';
import 'package:test/test.dart';
import 'package:vodozemac/vodozemac.dart' as vod;
import 'package:matrix/encryption.dart';
import 'package:matrix/matrix.dart';
import 'package:matrix/src/models/timeline_chunk.dart';
import 'fake_client.dart';
import 'fake_database.dart';
void main() async {
final client = Client(
'testclient',
httpClient: FakeMatrixApi(),
database: await getDatabase(),
);
/// All Tests related to the Event
group('Event', () {
Logs().level = Level.error;
final timestamp = DateTime.now().millisecondsSinceEpoch;
final id = '!4fsdfjisjf:server.abc';
final senderID = '@alice:server.abc';
final type = 'm.room.message';
final msgtype = 'm.text';
final body = 'Hello World';
final formatted_body = '<b>Hello</b> World';
final contentJson =
'{"msgtype":"$msgtype","body":"$body","formatted_body":"$formatted_body","m.relates_to":{"m.in_reply_to":{"event_id":"\$1234:example.com"}}}';
final jsonObj = <String, dynamic>{
'event_id': id,
'sender': senderID,
'origin_server_ts': timestamp,
'type': type,
'room_id': '!testroom:example.abc',
'status': EventStatus.synced.intValue,
'content': contentJson,
};
final room = Room(id: '!testroom:example.abc', client: client);
final event = Event.fromJson(
jsonObj,
Room(id: '!testroom:example.abc', client: client),
);
tearDownAll(() async => client.dispose());
test('Create from json', () async {
jsonObj['content'] = json.decode(contentJson);
expect(event.toJson(), jsonObj);
jsonObj['content'] = contentJson;
expect(event.eventId, id);
expect(event.senderId, senderID);
expect(event.status, EventStatus.synced);
expect(event.text, body);
expect(event.formattedText, formatted_body);
expect(event.body, body);
expect(event.type, EventTypes.Message);
expect(event.relationshipType, RelationshipTypes.reply);
jsonObj['state_key'] = '';
final state = Event.fromJson(jsonObj, room);
expect(state.eventId, id);
expect(state.stateKey, '');
expect(state.status, EventStatus.synced);
});
test('Test all EventTypes', () async {
Event event;
jsonObj['type'] = 'm.room.avatar';
event = Event.fromJson(jsonObj, room);
expect(event.type, EventTypes.RoomAvatar);
jsonObj['type'] = 'm.room.name';
event = Event.fromJson(jsonObj, room);
expect(event.type, EventTypes.RoomName);
jsonObj['type'] = 'm.room.topic';
event = Event.fromJson(jsonObj, room);
expect(event.type, EventTypes.RoomTopic);
jsonObj['type'] = 'm.room.aliases';
event = Event.fromJson(jsonObj, room);
expect(event.type, EventTypes.RoomAliases);
jsonObj['type'] = 'm.room.canonical_alias';
event = Event.fromJson(jsonObj, room);
expect(event.type, EventTypes.RoomCanonicalAlias);
jsonObj['type'] = 'm.room.create';
event = Event.fromJson(jsonObj, room);
expect(event.type, EventTypes.RoomCreate);
jsonObj['type'] = 'm.room.join_rules';
event = Event.fromJson(jsonObj, room);
expect(event.type, EventTypes.RoomJoinRules);
jsonObj['type'] = 'm.room.member';
event = Event.fromJson(jsonObj, room);
expect(event.type, EventTypes.RoomMember);
jsonObj['type'] = 'm.room.power_levels';
event = Event.fromJson(jsonObj, room);
expect(event.type, EventTypes.RoomPowerLevels);
jsonObj['type'] = 'm.room.guest_access';
event = Event.fromJson(jsonObj, room);
expect(event.type, EventTypes.GuestAccess);
jsonObj['type'] = 'm.room.history_visibility';
event = Event.fromJson(jsonObj, room);
expect(event.type, EventTypes.HistoryVisibility);
jsonObj['type'] = 'm.room.message';
jsonObj['content'] = json.decode(jsonObj['content']);
jsonObj['content'].remove('m.relates_to');
jsonObj['content']['msgtype'] = 'm.notice';
event = Event.fromJson(jsonObj, room);
expect(event.messageType, MessageTypes.Notice);
jsonObj['content']['msgtype'] = 'm.emote';
event = Event.fromJson(jsonObj, room);
expect(event.messageType, MessageTypes.Emote);
jsonObj['content']['msgtype'] = 'm.image';
event = Event.fromJson(jsonObj, room);
expect(event.messageType, MessageTypes.Image);
jsonObj['content']['msgtype'] = 'm.video';
event = Event.fromJson(jsonObj, room);
expect(event.messageType, MessageTypes.Video);
jsonObj['content']['msgtype'] = 'm.audio';
event = Event.fromJson(jsonObj, room);
expect(event.messageType, MessageTypes.Audio);
jsonObj['content']['msgtype'] = 'm.file';
event = Event.fromJson(jsonObj, room);
expect(event.messageType, MessageTypes.File);
jsonObj['content']['msgtype'] = 'm.location';
event = Event.fromJson(jsonObj, room);
expect(event.messageType, MessageTypes.Location);
jsonObj['type'] = 'm.sticker';
jsonObj['content']['msgtype'] = null;
event = Event.fromJson(jsonObj, room);
expect(event.messageType, MessageTypes.Sticker);
jsonObj['type'] = 'm.room.message';
jsonObj['content']['msgtype'] = 'm.text';
jsonObj['content']['m.relates_to'] = <String, dynamic>{};
jsonObj['content']['m.relates_to']['m.in_reply_to'] = {
'event_id': '1234',
};
event = Event.fromJson(jsonObj, room);
expect(event.messageType, MessageTypes.Text);
expect(event.relationshipType, RelationshipTypes.reply);
expect(event.relationshipEventId, '1234');
});
test('relationship types', () async {
Event event;
jsonObj['content'] = <String, dynamic>{
'msgtype': 'm.text',
'text': 'beep',
};
event = Event.fromJson(jsonObj, room);
expect(event.relationshipType, null);
expect(event.relationshipEventId, null);
jsonObj['content']['m.relates_to'] = <String, dynamic>{
'rel_type': 'm.replace',
'event_id': 'abc',
};
event = Event.fromJson(jsonObj, room);
expect(event.relationshipType, RelationshipTypes.edit);
expect(event.relationshipEventId, 'abc');
jsonObj['content']['m.relates_to']['rel_type'] = 'm.annotation';
event = Event.fromJson(jsonObj, room);
expect(event.relationshipType, RelationshipTypes.reaction);
expect(event.relationshipEventId, 'abc');
jsonObj['content']['m.relates_to'] = {
'm.in_reply_to': {
'event_id': 'def',
},
};
event = Event.fromJson(jsonObj, room);
expect(event.relationshipType, RelationshipTypes.reply);
expect(event.relationshipEventId, 'def');
});
test('redact', () async {
final redactJsonObj = Map<String, dynamic>.from(jsonObj);
final testTypes = [
EventTypes.RoomMember,
EventTypes.RoomCreate,
EventTypes.RoomJoinRules,
EventTypes.RoomPowerLevels,
EventTypes.RoomAliases,
EventTypes.HistoryVisibility,
];
for (final testType in testTypes) {
redactJsonObj['type'] = testType;
final room = Room(
id: '1234',
client: Client(
'testclient',
database: await getDatabase(),
),
);
final redactionEventJson = {
'content': {'reason': 'Spamming'},
'event_id': '143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'redacts': id,
'room_id': '1234',
'sender': '@example:example.org',
'type': 'm.room.redaction',
'unsigned': {'age': 1234},
'status': 1,
};
final redactedBecause = Event.fromJson(redactionEventJson, room);
final event = Event.fromJson(redactJsonObj, room);
event.setRedactionEvent(redactedBecause);
expect(event.redacted, true);
expect(event.redactedBecause?.toJson(), redactedBecause.toJson());
expect(event.content.isEmpty, true);
expect(event.unsigned?['redacted_because'], redactionEventJson);
await client.dispose();
}
});
test('remove', () async {
final event = Event.fromJson(
jsonObj,
Room(
id: '1234',
client: Client(
'testclient',
database: await getDatabase(),
),
),
);
expect(() async => await event.cancelSend(), throwsException);
event.status = EventStatus.sending;
await event.cancelSend();
await room.client.dispose();
});
test('sendAgain', () async {
try {
vodInit ??= vod.init(
wasmPath: './pkg/',
libraryPath: './rust/target/debug/',
);
await vodInit;
} catch (_) {
Logs().d('Encryption via Vodozemac not enabled');
}
final matrix = Client(
'testclient',
httpClient: FakeMatrixApi(),
database: await getDatabase(),
);
await matrix.checkHomeserver(
Uri.parse('https://fakeserver.notexisting'),
checkWellKnown: false,
);
await matrix.login(
LoginType.mLoginPassword,
identifier: AuthenticationUserIdentifier(user: 'test'),
password: '1234',
);
final event = Event.fromJson(
jsonObj,
Room(id: '!1234:example.com', client: matrix),
);
final resp1 = await event.sendAgain();
event.status = EventStatus.error;
final resp2 = await event.sendAgain(txid: '1234');
expect(resp1, null);
expect(resp2?.startsWith('\$event'), true);
await matrix.dispose(closeDatabase: true);
});
test('requestKey', tags: 'olm', () async {
final matrix = Client(
'testclient',
httpClient: FakeMatrixApi(),
database: await getDatabase(),
);
await matrix.checkHomeserver(
Uri.parse('https://fakeserver.notexisting'),
checkWellKnown: false,
);
await matrix.login(
LoginType.mLoginPassword,
identifier: AuthenticationUserIdentifier(user: 'test'),
password: '1234',
);
final event = Event.fromJson(
jsonObj,
Room(id: '!1234:example.com', client: matrix),
);
String? exception;
try {
await event.requestKey();
} catch (e) {
exception = e.toString();
}
expect(exception, 'Session key not requestable');
final event2 = Event.fromJson(
{
'event_id': id,
'sender': senderID,
'origin_server_ts': timestamp,
'type': 'm.room.encrypted',
'room_id': '1234',
'status': EventStatus.synced.intValue,
'content': json.encode({
'msgtype': 'm.bad.encrypted',
'body': DecryptException.unknownSession,
'can_request_session': true,
'algorithm': AlgorithmTypes.megolmV1AesSha2,
'ciphertext': 'AwgAEnACgAkLmt6qF84IK++J7UDH2Za1YVchHyprqTqsg...',
'device_id': 'RJYKSTBOIE',
'sender_key': 'IlRMeOPX2e0MurIyfWEucYBRVOEEUMrOHqn/8mLqMjA',
'session_id': 'X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ',
}),
},
Room(id: '!1234:example.com', client: matrix),
);
await event2.requestKey();
await matrix.dispose(closeDatabase: true);
});
test('requestKey', tags: 'olm', () async {
final client = await getClient();
jsonObj['state_key'] = '@alice:example.com';
final event = Event.fromJson(
jsonObj,
Room(id: '!localpart:server.abc', client: client),
);
expect(event.stateKeyUser?.id, '@alice:example.com');
});
test('canRedact', () async {
final client = await getClient();
jsonObj['sender'] = client.userID!;
final event = Event.fromJson(
jsonObj,
Room(
id: '!localpart:server.abc',
client: client,
),
);
expect(event.canRedact, true);
await client.dispose();
});
test('getLocalizedBody, isEventKnown', () async {
final matrix = Client(
'testclient',
httpClient: FakeMatrixApi(),
database: await getDatabase(),
);
final room = Room(id: '!1234:example.com', client: matrix);
var event = Event.fromJson(
{
'content': {
'avatar_url': 'mxc://example.org/SEsfnsuifSDFSSEF',
'displayname': 'Alice Margatroid',
'membership': 'join',
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'state_key': '@alice:example.org',
'type': 'm.room.member',
'unsigned': {
'age': 1234,
'redacted_because': {
'content': {'reason': 'Spamming'},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'redacts': '\$143273582443PhrSn:example.org',
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'type': 'm.room.redaction',
'unsigned': {'age': 1234},
},
},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Removed by Example',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {
'avatar_url':
'mxc://pixelthefox.net/bmGuC44Eeb3BomfkZTP02DVnGaRp4dek',
'displayname': [
[
[[]],
]
],
'membership': 'join',
},
'origin_server_ts': 1636487843183,
'room_id': '!watercooler-v9:maunium.net',
'sender': '@nyaaori:pixelthefox.net',
'state_key': '@nyaaori:pixelthefox.net',
'type': 'm.room.member',
'unsigned': {
'prev_content': {
'avatar_url':
'mxc://pixelthefox.net/bmGuC44Eeb3BomfkZTP02DVnGaRp4dek',
'displayname': 1,
'membership': 'join',
},
'prev_sender': '@nyaaori:pixelthefox.net',
'replaces_state': '\$kcqn2k6kXQKOM45t_p8OA03PQRR3KB2N_PN4HUq1GiY',
},
'event_id': '\$21DJjleMGcviLoT4L9wvxawMlOXSQ9yW6R8mrhlbhfU',
'user_id': '@nyaaori:pixelthefox.net',
'replaces_state': '\$kcqn2k6kXQKOM45t_p8OA03PQRR3KB2N_PN4HUq1GiY',
'prev_content': {
'avatar_url':
'mxc://pixelthefox.net/bmGuC44Eeb3BomfkZTP02DVnGaRp4dek',
'displayname': 1,
'membership': 'join',
},
},
room,
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {
'body': 'Landing',
'info': {
'h': 200,
'mimetype': 'image/png',
'size': 73602,
'thumbnail_info': {
'h': 200,
'mimetype': 'image/png',
'size': 73602,
'w': 140,
},
'thumbnail_url': 'mxc://matrix.org/sHhqkFCvSkFwtmvtETOtKnLP',
'w': 140,
},
'url': 'mxc://matrix.org/sHhqkFCvSkFwtmvtETOtKnLP',
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'type': 'm.sticker',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example sent a sticker',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {'reason': 'Spamming'},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'redacts': '\$143273582443PhrSn:example.org',
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'type': 'm.room.redaction',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example redacted an event',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {
'aliases': ['#somewhere:example.org', '#another:example.org'],
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'state_key': 'example.org',
'type': 'm.room.aliases',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example changed the room aliases',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {
'aliases': ['#somewhere:example.org', '#another:example.org'],
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'state_key': 'example.org',
'type': 'm.room.aliases',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example changed the room aliases',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {'alias': '#somewhere:localhost'},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'state_key': '',
'type': 'm.room.canonical_alias',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example changed the room invitation link',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {
'creator': '@example:example.org',
'm.federate': true,
'predecessor': {
'event_id': '\$something:example.org',
'room_id': '!oldroom:example.org',
},
'room_version': '1',
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'state_key': '',
'type': 'm.room.create',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example created the chat',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {
'body': 'This room has been replaced',
'replacement_room': '!newroom:example.org',
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'state_key': '',
'type': 'm.room.tombstone',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Room has been upgraded',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {'join_rule': 'public'},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'state_key': '',
'type': 'm.room.join_rules',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example changed the join rules to Anyone can join',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {
'avatar_url': 'mxc://example.org/SEsfnsuifSDFSSEF',
'displayname': 'Alice Margatroid',
'membership': 'join',
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'state_key': '@alice:example.org',
'type': 'm.room.member',
'unsigned': {'age': 1234},
},
room,
);
expect(event.roomMemberChangeType, RoomMemberChangeType.join);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Alice joined the chat',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {'membership': 'invite'},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'state_key': '@alice:example.org',
'type': 'm.room.member',
},
room,
);
expect(event.roomMemberChangeType, RoomMemberChangeType.invite);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example has invited Alice',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {'membership': 'leave'},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'state_key': '@alice:example.org',
'type': 'm.room.member',
'unsigned': {
'prev_content': {'membership': 'join'},
},
},
room,
);
expect(event.roomMemberChangeType, RoomMemberChangeType.kick);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example kicked Alice',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {'membership': 'ban'},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'state_key': '@alice:example.org',
'type': 'm.room.member',
'unsigned': {
'prev_content': {'membership': 'join'},
},
},
room,
);
expect(event.roomMemberChangeType, RoomMemberChangeType.ban);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example banned Alice',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {'membership': 'join'},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'state_key': '@alice:example.org',
'type': 'm.room.member',
'unsigned': {
'prev_content': {'membership': 'invite'},
},
},
room,
);
expect(event.roomMemberChangeType, RoomMemberChangeType.acceptInvite);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Alice accepted the invitation',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {'membership': 'invite'},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'state_key': '@alice:example.org',
'type': 'm.room.member',
'unsigned': {
'prev_content': {'membership': 'join'},
},
},
room,
);
expect(event.roomMemberChangeType, RoomMemberChangeType.invite);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example has invited Alice',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {'membership': 'leave'},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'state_key': '@alice:example.org',
'type': 'm.room.member',
'unsigned': {
'prev_content': {'membership': 'invite'},
},
},
room,
);
expect(
event.roomMemberChangeType,
RoomMemberChangeType.withdrawInvitation,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example has withdrawn the invitation for Alice',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {'membership': 'leave'},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@alice:example.org',
'state_key': '@alice:example.org',
'type': 'm.room.member',
'unsigned': {
'prev_content': {'membership': 'invite'},
},
},
room,
);
expect(event.roomMemberChangeType, RoomMemberChangeType.rejectInvite);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Alice rejected the invitation',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {
'ban': 50,
'events': {'m.room.name': 100, 'm.room.power_levels': 100},
'events_default': 0,
'invite': 50,
'kick': 50,
'notifications': {'room': 20},
'redact': 50,
'state_default': 50,
'users': {'@example:localhost': 100},
'users_default': 0,
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'state_key': '',
'type': 'm.room.power_levels',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example changed the chat permissions',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {'name': 'The room name'},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'state_key': '',
'type': 'm.room.name',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example changed the chat name to The room name',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {'topic': 'A room topic'},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'state_key': '',
'type': 'm.room.topic',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example changed the chat description to A room topic',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {
'info': {
'h': 398,
'mimetype': 'image/jpeg',
'size': 31037,
'w': 394,
},
'url': 'mxc://example.org/JWEIFJgwEIhweiWJE',
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'state_key': '',
'type': 'm.room.avatar',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example changed the chat avatar',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {'history_visibility': 'shared'},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'state_key': '',
'type': 'm.room.history_visibility',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example changed the history visibility to Visible for all participants',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {
'algorithm': AlgorithmTypes.megolmV1AesSha2,
'rotation_period_ms': 604800000,
'rotation_period_msgs': 100,
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'state_key': '',
'type': 'm.room.encryption',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example activated end to end encryption. Need pantalaimon',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {
'body': 'This is an example text message',
'format': 'org.matrix.custom.html',
'formatted_body': '<b>This is an example text message</b>',
'msgtype': 'm.text',
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'type': 'm.room.message',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'This is an example text message',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {
'body': 'thinks this is an example emote',
'format': 'org.matrix.custom.html',
'formatted_body': 'thinks <b>this</b> is an example emote',
'msgtype': 'm.emote',
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'type': 'm.room.message',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'* thinks this is an example emote',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {
'body': 'This is an example notice',
'format': 'org.matrix.custom.html',
'formatted_body': 'This is an <strong>example</strong> notice',
'msgtype': 'm.notice',
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'type': 'm.room.message',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'This is an example notice',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {
'body': 'filename.jpg',
'info': {
'h': 398,
'mimetype': 'image/jpeg',
'size': 31037,
'w': 394,
},
'msgtype': 'm.image',
'url': 'mxc://example.org/JWEIFJgwEIhweiWJE',
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'type': 'm.room.message',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example sent a picture',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {
'body': 'something-important.doc',
'filename': 'something-important.doc',
'info': {'mimetype': 'application/msword', 'size': 46144},
'msgtype': 'm.file',
'url': 'mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe',
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'type': 'm.room.message',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example sent a file',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {
'body': 'Bee Gees - Stayin Alive',
'info': {
'duration': 2140786,
'mimetype': 'audio/mpeg',
'size': 1563685,
},
'msgtype': 'm.audio',
'url': 'mxc://example.org/ffed755USFFxlgbQYZGtryd',
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'type': 'm.room.message',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example sent an audio',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {
'body': 'Big Ben, London, UK',
'geo_uri': 'geo:51.5008,0.1247',
'info': {
'thumbnail_info': {
'h': 300,
'mimetype': 'image/jpeg',
'size': 46144,
'w': 300,
},
'thumbnail_url': 'mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe',
},
'msgtype': 'm.location',
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'type': 'm.room.message',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example shared the location',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {
'body': 'Gangnam Style',
'info': {
'duration': 2140786,
'h': 320,
'mimetype': 'video/mp4',
'size': 1563685,
'thumbnail_info': {
'h': 300,
'mimetype': 'image/jpeg',
'size': 46144,
'w': 300,
},
'thumbnail_url': 'mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe',
'w': 480,
},
'msgtype': 'm.video',
'url': 'mxc://example.org/a526eYUSFFxlgbQYZmo442',
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'type': 'm.room.message',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example sent a video',
);
expect(event.isEventTypeKnown, true);
event = Event.fromJson(
{
'content': {'beep': 'boop'},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'type': 'unknown.event.type',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Unknown event unknown.event.type',
);
expect(event.isEventTypeKnown, false);
await matrix.dispose(closeDatabase: true);
});
test('getLocalizedBody, parameters', () async {
final matrix = Client(
'testclient',
httpClient: FakeMatrixApi(),
database: await getDatabase(),
);
final room = Room(id: '!1234:example.com', client: matrix);
var event = Event.fromJson(
{
'content': {
'body': 'This is an example text message',
'format': 'org.matrix.custom.html',
'formatted_body': '<b>This is an example text message</b>',
'msgtype': 'm.text',
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'type': 'm.room.message',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(
MatrixDefaultLocalizations(),
plaintextBody: true,
),
'**This is an example text message**',
);
event = Event.fromJson(
{
'content': {
'body': '* This is an example text message',
'format': 'org.matrix.custom.html',
'formatted_body': '* <b>This is an example text message</b>',
'msgtype': 'm.text',
'm.relates_to': <String, dynamic>{
'rel_type': 'm.replace',
'event_id': '\$some_event',
},
'm.new_content': <String, dynamic>{
'body': 'This is an example text message',
'format': 'org.matrix.custom.html',
'formatted_body': '<b>This is an example text message</b>',
'msgtype': 'm.text',
},
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'type': 'm.room.message',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(
MatrixDefaultLocalizations(),
hideEdit: true,
),
'This is an example text message',
);
expect(
await event.calcLocalizedBody(
MatrixDefaultLocalizations(),
hideEdit: true,
plaintextBody: true,
),
'**This is an example text message**',
);
event = Event.fromJson(
{
'content': {
'body': '> <@user:example.org> beep\n\nhmm, fox',
'format': 'org.matrix.custom.html',
'formatted_body': '<mx-reply>beep</mx-reply>hmm, <em>fox</em>',
'msgtype': 'm.text',
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'type': 'm.room.message',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(
MatrixDefaultLocalizations(),
hideReply: true,
),
'hmm, fox',
);
expect(
await event.calcLocalizedBody(
MatrixDefaultLocalizations(),
hideReply: true,
plaintextBody: true,
),
'hmm, *fox*',
);
event = Event.fromJson(
{
'content': {
'body':
'> Quote\n# Title\nsome text and [link](https://example.com)\nokay and this is **important**',
'format': 'org.matrix.custom.html',
'formatted_body':
'<blockquote><p>Quote</p></blockquote><h1>Title</h1>\n<p>some text and <a href="https://example.com">link</a><br>okay and this is <strong>important</strong></p>\n',
'msgtype': 'm.text',
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'type': 'm.room.message',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(
MatrixDefaultLocalizations(),
removeMarkdown: true,
),
'Quote\n\nTitle\nsome text and link\nokay and this is important',
);
expect(
await event.calcLocalizedBody(
MatrixDefaultLocalizations(),
removeMarkdown: true,
plaintextBody: true,
),
'Quote\n\nTitle\nsome text and 🔗link\nokay and this is important',
);
expect(
await event.calcLocalizedBody(
MatrixDefaultLocalizations(),
removeMarkdown: true,
withSenderNamePrefix: true,
),
'Example: Quote\n\nTitle\nsome text and link\nokay and this is important',
);
expect(
await event.calcLocalizedBody(
MatrixDefaultLocalizations(),
removeMarkdown: true,
plaintextBody: true,
withSenderNamePrefix: true,
),
'Example: Quote\n\nTitle\nsome text and 🔗link\nokay and this is important',
);
event = Event.fromJson(
{
'content': {
'body':
'Alice is requesting to verify your device, but your client does not support verification, so you may need to use a different verification method.',
'from_device': 'AliceDevice2',
'methods': ['m.sas.v1'],
'msgtype': 'm.key.verification.request',
'to': '@bob:example.org',
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': '!jEsUZKDJdhlrceRyVU:example.org',
'sender': '@example:example.org',
'type': 'm.room.message',
'unsigned': {'age': 1234},
},
room,
);
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example requested key verification',
);
event.content['msgtype'] = 'm.key.verification.ready';
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example is ready for key verification',
);
event.content['msgtype'] = 'm.key.verification.start';
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example started key verification',
);
event.content['msgtype'] = 'm.key.verification.cancel';
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example canceled key verification',
);
event.content['msgtype'] = 'm.key.verification.done';
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example completed key verification',
);
event.content['msgtype'] = 'm.key.verification.accept';
expect(
await event.calcLocalizedBody(MatrixDefaultLocalizations()),
'Example accepted key verification request',
);
await matrix.dispose(closeDatabase: true);
});
test('aggregations', () {
final room = Room(id: '!1234', client: client);
final event = Event.fromJson(
{
'content': {
'body': 'blah',
'msgtype': 'm.text',
},
'type': 'm.room.message',
'sender': '@example:example.org',
'event_id': '\$source',
},
room,
);
final edit1 = Event.fromJson(
{
'content': {
'body': 'blah',
'msgtype': 'm.text',
'm.relates_to': {
'event_id': '\$source',
'rel_type': RelationshipTypes.edit,
},
},
'type': 'm.room.message',
'sender': '@example:example.org',
'event_id': '\$edit1',
},
room,
);
final edit2 = Event.fromJson(
{
'content': {
'body': 'blah',
'msgtype': 'm.text',
'm.relates_to': {
'event_id': '\$source',
'rel_type': RelationshipTypes.edit,
},
},
'type': 'm.room.message',
'sender': '@example:example.org',
'event_id': '\$edit2',
},
room,
);
final timeline = Timeline(
chunk: TimelineChunk(events: <Event>[event, edit1, edit2]),
room: room,
);
expect(event.hasAggregatedEvents(timeline, RelationshipTypes.edit), true);
expect(
event.aggregatedEvents(timeline, RelationshipTypes.edit),
{edit1, edit2},
);
expect(
event.aggregatedEvents(timeline, RelationshipTypes.reaction),
<Event>{},
);
expect(
event.hasAggregatedEvents(timeline, RelationshipTypes.reaction),
false,
);
timeline.removeAggregatedEvent(edit2);
expect(event.aggregatedEvents(timeline, RelationshipTypes.edit), {edit1});
timeline.addAggregatedEvent(edit2);
expect(
event.aggregatedEvents(timeline, RelationshipTypes.edit),
{edit1, edit2},
);
timeline.removeAggregatedEvent(event);
expect(
event.aggregatedEvents(timeline, RelationshipTypes.edit),
<Event>{},
);
});
test('plaintextBody', () {
final event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'body': 'blah',
'msgtype': 'm.text',
'format': 'org.matrix.custom.html',
'formatted_body': '<b>blah</b>',
},
'event_id': '\$source',
'sender': '@alice:example.org',
},
room,
);
expect(event.plaintextBody, '**blah**');
});
test('body', () {
final event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'body': 'blah',
'msgtype': 'm.text',
'format': 'org.matrix.custom.html',
'formatted_body': '<b>blub</b>',
},
'event_id': '\$source',
'sender': '@alice:example.org',
},
room,
);
expect(event.body, 'blah');
final event2 = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'body': '',
'msgtype': 'm.text',
'format': 'org.matrix.custom.html',
'formatted_body': '<b>blub</b>',
},
'event_id': '\$source',
'sender': '@alice:example.org',
},
room,
);
expect(event2.body, 'm.room.message');
});
group('unlocalized body reply stripping', () {
int i = 0;
void testUnlocalizedBody({
required Object? body,
required Object? formattedBody,
required bool html,
Object? editBody,
Object? editFormattedBody,
bool editHtml = false,
bool isEdit = false,
required String expectation,
required bool plaintextBody,
}) {
i += 1;
test('$i', () {
final event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'msgtype': 'm.text',
if (body != null) 'body': body,
if (formattedBody != null) 'formatted_body': formattedBody,
if (html) 'format': 'org.matrix.custom.html',
if (isEdit) ...{
'm.new_content': {
if (editBody != null) 'body': editBody,
if (editFormattedBody != null)
'formatted_body': editFormattedBody,
if (editHtml) 'format': 'org.matrix.custom.html',
},
'm.relates_to': {
'event_id': '\$source2',
'rel_type': RelationshipTypes.edit,
},
},
},
'event_id': '\$source',
'sender': '@alice:example.org',
},
room,
);
expect(
event.calcUnlocalizedBody(
hideReply: true,
hideEdit: true,
plaintextBody: plaintextBody,
),
expectation,
reason:
'event was ${event.toJson()} and plaintextBody ${plaintextBody ? "was" : "was not"} set',
);
});
}
// everything where we expect the body to be returned
testUnlocalizedBody(
expectation: 'body',
plaintextBody: false,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: false,
isEdit: false,
editBody: null,
editFormattedBody: null,
editHtml: false,
);
testUnlocalizedBody(
// not sure we actually want m.room.message here and not an empty string
expectation: 'm.room.message',
plaintextBody: false,
body: '',
formattedBody: '<b>formatted body</b>',
html: false,
isEdit: false,
editBody: null,
editFormattedBody: null,
editHtml: false,
);
testUnlocalizedBody(
expectation: 'm.room.message',
plaintextBody: false,
body: null,
formattedBody: '<b>formatted body</b>',
html: false,
isEdit: false,
editBody: null,
editFormattedBody: null,
editHtml: false,
);
testUnlocalizedBody(
expectation: 'm.room.message',
plaintextBody: false,
body: 5,
formattedBody: '<b>formatted body</b>',
html: false,
isEdit: false,
editBody: null,
editFormattedBody: null,
editHtml: false,
);
testUnlocalizedBody(
expectation: 'body',
plaintextBody: false,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: false,
isEdit: true,
editBody: null,
editFormattedBody: null,
editHtml: false,
);
testUnlocalizedBody(
expectation: 'body',
plaintextBody: false,
body: 'body',
formattedBody: null,
html: true,
isEdit: false,
editBody: null,
editFormattedBody: null,
editHtml: false,
);
testUnlocalizedBody(
expectation: 'body',
plaintextBody: true,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: false,
isEdit: false,
editBody: null,
editFormattedBody: null,
editHtml: false,
);
testUnlocalizedBody(
expectation: 'body',
plaintextBody: true,
body: 'body',
formattedBody: null,
html: true,
isEdit: false,
editBody: null,
editFormattedBody: null,
editHtml: false,
);
testUnlocalizedBody(
expectation: 'body',
plaintextBody: true,
body: 'body',
// do we actually expect this to then use the body?
formattedBody: '',
html: true,
isEdit: false,
editBody: null,
editFormattedBody: null,
editHtml: false,
);
testUnlocalizedBody(
expectation: 'body',
plaintextBody: true,
body: 'body',
formattedBody: 5,
html: true,
isEdit: false,
editBody: null,
editFormattedBody: null,
editHtml: false,
);
testUnlocalizedBody(
expectation: 'body',
plaintextBody: false,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: true,
isEdit: true,
editBody: null,
editFormattedBody: null,
editHtml: false,
);
testUnlocalizedBody(
expectation: 'body',
plaintextBody: false,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: true,
isEdit: true,
editBody: null,
editFormattedBody: null,
editHtml: true,
);
testUnlocalizedBody(
expectation: '**formatted body**',
plaintextBody: true,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: true,
isEdit: true,
editBody: null,
editFormattedBody: null,
editHtml: false,
);
testUnlocalizedBody(
expectation: '**formatted body**',
plaintextBody: true,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: true,
isEdit: true,
editBody: null,
editFormattedBody: null,
editHtml: true,
);
// everything where we expect the formatted body to be returned
testUnlocalizedBody(
expectation: '**formatted body**',
plaintextBody: true,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: true,
isEdit: false,
editBody: null,
editFormattedBody: null,
editHtml: false,
);
testUnlocalizedBody(
expectation: '**formatted body**',
plaintextBody: true,
body: 5,
formattedBody: '<b>formatted body</b>',
html: true,
isEdit: false,
editBody: null,
editFormattedBody: null,
editHtml: false,
);
// everything where we expect the edit body to be returned
testUnlocalizedBody(
expectation: 'edit body',
plaintextBody: false,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: false,
isEdit: true,
editBody: 'edit body',
editFormattedBody: null,
editHtml: false,
);
testUnlocalizedBody(
expectation: 'edit body',
plaintextBody: false,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: false,
isEdit: true,
editBody: 'edit body',
editFormattedBody: '<b>edit formatted body</b>',
editHtml: true,
);
testUnlocalizedBody(
expectation: 'edit body',
plaintextBody: false,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: true,
isEdit: true,
editBody: 'edit body',
editFormattedBody: '<b>edit formatted body</b>',
editHtml: true,
);
testUnlocalizedBody(
expectation: 'edit body',
plaintextBody: false,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: false,
isEdit: true,
editBody: 'edit body',
editFormattedBody: '<b>edit formatted body</b>',
editHtml: false,
);
testUnlocalizedBody(
expectation: 'edit body',
plaintextBody: false,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: false,
isEdit: true,
editBody: 'edit body',
editFormattedBody: null,
editHtml: true,
);
testUnlocalizedBody(
expectation: 'edit body',
plaintextBody: false,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: true,
isEdit: true,
editBody: 'edit body',
editFormattedBody: 5,
editHtml: true,
);
testUnlocalizedBody(
expectation: 'edit body',
plaintextBody: false,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: true,
isEdit: true,
editBody: 'edit body',
editFormattedBody: null,
editHtml: false,
);
testUnlocalizedBody(
expectation: 'edit body',
plaintextBody: false,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: true,
isEdit: true,
editBody: 'edit body',
editFormattedBody: '<b>edit formatted body</b>',
editHtml: false,
);
testUnlocalizedBody(
expectation: 'edit body',
plaintextBody: true,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: false,
isEdit: true,
editBody: 'edit body',
editFormattedBody: null,
editHtml: false,
);
testUnlocalizedBody(
expectation: 'edit body',
plaintextBody: true,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: false,
isEdit: true,
editBody: 'edit body',
editFormattedBody: '<b>edit formatted body</b>',
editHtml: false,
);
testUnlocalizedBody(
expectation: 'edit body',
plaintextBody: true,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: false,
isEdit: true,
editBody: 'edit body',
editFormattedBody: null,
editHtml: true,
);
testUnlocalizedBody(
expectation: 'edit body',
plaintextBody: true,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: true,
isEdit: true,
editBody: 'edit body',
editFormattedBody: null,
editHtml: false,
);
testUnlocalizedBody(
expectation: 'edit body',
plaintextBody: true,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: true,
isEdit: true,
editBody: 'edit body',
editFormattedBody: '<b>edit formatted body</b>',
editHtml: false,
);
testUnlocalizedBody(
expectation: 'edit body',
plaintextBody: true,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: true,
isEdit: true,
editBody: 'edit body',
editFormattedBody: null,
editHtml: true,
);
testUnlocalizedBody(
expectation: 'edit body',
plaintextBody: true,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: true,
isEdit: true,
editBody: 'edit body',
editFormattedBody: null,
editHtml: false,
);
// everything where we expect the edit formatted body to be returned
testUnlocalizedBody(
expectation: '**edit formatted body**',
plaintextBody: true,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: true,
isEdit: true,
editBody: null,
editFormattedBody: '<b>edit formatted body</b>',
editHtml: true,
);
testUnlocalizedBody(
expectation: '**edit formatted body**',
plaintextBody: true,
body: 'body',
formattedBody: '<b>formatted body</b>',
html: true,
isEdit: true,
editBody: 'edit body',
editFormattedBody: '<b>edit formatted body</b>',
editHtml: true,
);
testUnlocalizedBody(
expectation: '**edit formatted body**',
plaintextBody: true,
body: null,
formattedBody: '<b>formatted body</b>',
html: true,
isEdit: true,
editBody: 'edit body',
editFormattedBody: '<b>edit formatted body</b>',
editHtml: true,
);
testUnlocalizedBody(
expectation: '**edit formatted body**',
plaintextBody: true,
body: 'body',
formattedBody: null,
html: true,
isEdit: true,
editBody: 'edit body',
editFormattedBody: '<b>edit formatted body</b>',
editHtml: true,
);
// test with reply fallback
testUnlocalizedBody(
expectation: 'body',
plaintextBody: false,
body: '> <@some:user.id> acb def\n\nbody',
formattedBody:
'<mx-reply><blockquote>abc</blockquote></mx-reply><b>formatted body</b>',
html: true,
isEdit: false,
// strictly speaking there is no quote in edits, but we have to handle them anyway because of other clients doing it wrong
editBody: '> <@some:user.id> acb def\n\nedit body',
editFormattedBody:
'<mx-reply><blockquote>abc</blockquote></mx-reply><b>edit formatted body</b>',
editHtml: true,
);
testUnlocalizedBody(
expectation: 'body',
plaintextBody: true,
body: '> <@some:user.id> acb def\n\nbody',
formattedBody:
'<mx-reply><blockquote>abc</blockquote></mx-reply><b>formatted body</b>',
html: false,
isEdit: false,
// strictly speaking there is no quote in edits, but we have to handle them anyway because of other clients doing it wrong
editBody: '> <@some:user.id> acb def\n\nedit body',
editFormattedBody:
'<mx-reply><blockquote>abc</blockquote></mx-reply><b>edit formatted body</b>',
editHtml: true,
);
testUnlocalizedBody(
expectation: 'body',
plaintextBody: true,
body: '> <@some:user.id> acb def\n\nbody',
formattedBody: null,
html: true,
isEdit: false,
// strictly speaking there is no quote in edits, but we have to handle them anyway because of other clients doing it wrong
editBody: '> <@some:user.id> acb def\n\nedit body',
editFormattedBody:
'<mx-reply><blockquote>abc</blockquote></mx-reply><b>edit formatted body</b>',
editHtml: true,
);
testUnlocalizedBody(
expectation: '**formatted body**',
plaintextBody: true,
body: '> <@some:user.id> acb def\n\nbody',
formattedBody:
'<mx-reply><blockquote>abc</blockquote></mx-reply><b>formatted body</b>',
html: true,
isEdit: false,
// strictly speaking there is no quote in edits, but we have to handle them anyway because of other clients doing it wrong
editBody: '> <@some:user.id> acb def\n\nedit body',
editFormattedBody:
'<mx-reply><blockquote>abc</blockquote></mx-reply><b>edit formatted body</b>',
editHtml: true,
);
testUnlocalizedBody(
expectation: 'edit body',
plaintextBody: false,
body: '> <@some:user.id> acb def\n\nbody',
formattedBody:
'<mx-reply><blockquote>abc</blockquote></mx-reply><b>formatted body</b>',
html: true,
isEdit: true,
// strictly speaking there is no quote in edits, but we have to handle them anyway because of other clients doing it wrong
editBody: '> <@some:user.id> acb def\n\nedit body',
editFormattedBody:
'<mx-reply><blockquote>abc</blockquote></mx-reply><b>edit formatted body</b>',
editHtml: true,
);
testUnlocalizedBody(
expectation: 'edit body',
plaintextBody: true,
body: '> <@some:user.id> acb def\n\nbody',
formattedBody:
'<mx-reply><blockquote>abc</blockquote></mx-reply><b>formatted body</b>',
html: true,
isEdit: true,
// strictly speaking there is no quote in edits, but we have to handle them anyway because of other clients doing it wrong
editBody: '> <@some:user.id> acb def\n\nedit body',
editFormattedBody:
'<mx-reply><blockquote>abc</blockquote></mx-reply><b>edit formatted body</b>',
editHtml: false,
);
testUnlocalizedBody(
expectation: '**edit formatted body**',
plaintextBody: true,
body: '> <@some:user.id> acb def\n\nbody',
formattedBody:
'<mx-reply><blockquote>abc</blockquote></mx-reply><b>formatted body</b>',
html: true,
isEdit: true,
// strictly speaking there is no quote in edits, but we have to handle them anyway because of other clients doing it wrong
editBody: '> <@some:user.id> acb def\n\nedit body',
editFormattedBody:
'<mx-reply><blockquote>abc</blockquote></mx-reply><b>edit formatted body</b>',
editHtml: true,
);
});
test('getDisplayEvent', () {
final room = Room(id: '!1234', client: client);
var event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'body': 'blah',
'msgtype': 'm.text',
},
'event_id': '\$source',
'sender': '@alice:example.org',
},
room,
);
final edit1 = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'body': '* edit 1',
'msgtype': 'm.text',
'm.new_content': {
'body': 'edit 1',
'msgtype': 'm.text',
},
'm.relates_to': {
'event_id': '\$source',
'rel_type': RelationshipTypes.edit,
},
},
'event_id': '\$edit1',
'sender': '@alice:example.org',
},
room,
);
final edit2 = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'body': '* edit 2',
'msgtype': 'm.text',
'm.new_content': {
'body': 'edit 2',
'msgtype': 'm.text',
},
'm.relates_to': {
'event_id': '\$source',
'rel_type': RelationshipTypes.edit,
},
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
final edit3 = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'body': '* edit 3',
'msgtype': 'm.text',
'm.new_content': {
'body': 'edit 3',
'msgtype': 'm.text',
},
'm.relates_to': {
'event_id': '\$source',
'rel_type': RelationshipTypes.edit,
},
},
'event_id': '\$edit3',
'sender': '@bob:example.org',
},
room,
);
// no edits
var displayEvent = event.getDisplayEvent(
Timeline(chunk: TimelineChunk(events: <Event>[event]), room: room),
);
expect(displayEvent.body, 'blah');
// one edit
displayEvent = event.getDisplayEvent(
Timeline(
chunk: TimelineChunk(events: <Event>[event, edit1]),
room: room,
),
);
expect(displayEvent.body, 'edit 1');
// two edits
displayEvent = event.getDisplayEvent(
Timeline(
chunk: TimelineChunk(events: <Event>[event, edit1, edit2]),
room: room,
),
);
expect(displayEvent.body, 'edit 2');
// foreign edit
displayEvent = event.getDisplayEvent(
Timeline(
chunk: TimelineChunk(events: <Event>[event, edit3]),
room: room,
),
);
expect(displayEvent.body, 'blah');
// mixed foreign and non-foreign
displayEvent = event.getDisplayEvent(
Timeline(
chunk: TimelineChunk(events: <Event>[event, edit1, edit2, edit3]),
room: room,
),
);
expect(displayEvent.body, 'edit 2');
event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'body': 'blah',
'msgtype': 'm.text',
},
'event_id': '\$source',
'sender': '@alice:example.org',
'unsigned': {
'redacted_because': {
'event_id': '\$redact',
'sender': '@alice:example.org',
'type': 'm.room.redaction',
},
},
},
room,
);
displayEvent = event.getDisplayEvent(
Timeline(
chunk: TimelineChunk(events: <Event>[event, edit1, edit2, edit3]),
room: room,
),
);
expect(displayEvent.body, 'Redacted');
});
test('attachments', () async {
final FILE_BUFF = Uint8List.fromList([0]);
final THUMBNAIL_BUFF = Uint8List.fromList([2]);
Future<Uint8List> downloadCallback(Uri uri) async {
return {
'/_matrix/client/v1/media/download/example.org/file': FILE_BUFF,
'/_matrix/client/v1/media/download/example.org/thumb': THUMBNAIL_BUFF,
}[uri.path]!;
}
await client.checkHomeserver(
Uri.parse('https://fakeserver.notexisting'),
checkWellKnown: false,
);
final room = Room(id: '!localpart:server.abc', client: client);
var event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'body': 'image',
'msgtype': 'm.image',
'url': 'mxc://example.org/file',
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
var buffer = await event.downloadAndDecryptAttachment(
downloadCallback: downloadCallback,
);
expect(buffer.bytes, FILE_BUFF);
expect(
event.attachmentOrThumbnailMxcUrl().toString(),
'mxc://example.org/file',
);
expect(
event.attachmentOrThumbnailMxcUrl(getThumbnail: true).toString(),
'mxc://example.org/file',
);
event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'body': 'image',
'msgtype': 'm.image',
'url': 'mxc://example.org/file',
'info': {
'size': 8000000,
'thumbnail_url': 'mxc://example.org/thumb',
'thumbnail_info': {
'mimetype': 'thumbnail/mimetype',
},
'mimetype': 'application/octet-stream',
},
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
expect(event.hasAttachment, true);
expect(event.hasThumbnail, true);
expect(event.isAttachmentEncrypted, false);
expect(event.isThumbnailEncrypted, false);
expect(event.attachmentMimetype, 'application/octet-stream');
expect(event.thumbnailMimetype, 'thumbnail/mimetype');
expect(event.attachmentMxcUrl.toString(), 'mxc://example.org/file');
expect(event.thumbnailMxcUrl.toString(), 'mxc://example.org/thumb');
expect(
event.attachmentOrThumbnailMxcUrl().toString(),
'mxc://example.org/file',
);
expect(
event.attachmentOrThumbnailMxcUrl(getThumbnail: true).toString(),
'mxc://example.org/thumb',
);
expect(
(await event.getAttachmentUri()).toString(),
'https://fakeserver.notexisting/_matrix/client/v1/media/download/example.org/file',
);
expect(
(await event.getAttachmentUri(getThumbnail: true)).toString(),
'https://fakeserver.notexisting/_matrix/client/v1/media/thumbnail/example.org/file?width=800&height=800&method=scale&animated=false',
);
expect(
(await event.getAttachmentUri(useThumbnailMxcUrl: true)).toString(),
'https://fakeserver.notexisting/_matrix/client/v1/media/download/example.org/thumb',
);
expect(
(await event.getAttachmentUri(
getThumbnail: true,
useThumbnailMxcUrl: true,
))
.toString(),
'https://fakeserver.notexisting/_matrix/client/v1/media/thumbnail/example.org/thumb?width=800&height=800&method=scale&animated=false',
);
expect(
(await event.getAttachmentUri(
getThumbnail: true,
minNoThumbSize: 9000000,
))
.toString(),
'https://fakeserver.notexisting/_matrix/client/v1/media/download/example.org/file',
);
buffer = await event.downloadAndDecryptAttachment(
downloadCallback: downloadCallback,
);
expect(buffer.bytes, FILE_BUFF);
buffer = await event.downloadAndDecryptAttachment(
getThumbnail: true,
downloadCallback: downloadCallback,
);
expect(buffer.bytes, THUMBNAIL_BUFF);
});
test(
'encrypted attachments',
tags: 'olm',
() async {
final FILE_BUFF_ENC =
Uint8List.fromList([0x3B, 0x6B, 0xB2, 0x8C, 0xAF]);
final FILE_BUFF_DEC =
Uint8List.fromList([0x74, 0x65, 0x73, 0x74, 0x0A]);
final THUMB_BUFF_ENC =
Uint8List.fromList([0x55, 0xD7, 0xEB, 0x72, 0x05, 0x13]);
final THUMB_BUFF_DEC =
Uint8List.fromList([0x74, 0x68, 0x75, 0x6D, 0x62, 0x0A]);
Future<Uint8List> downloadCallback(Uri uri) async {
return {
'/_matrix/client/v1/media/download/example.com/file': FILE_BUFF_ENC,
'/_matrix/client/v1/media/download/example.com/thumb':
THUMB_BUFF_ENC,
}[uri.path]!;
}
final room =
Room(id: '!localpart:server.abc', client: await getClient());
var event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'body': 'image',
'msgtype': 'm.image',
'file': {
'v': 'v2',
'key': {
'alg': 'A256CTR',
'ext': true,
'k': '7aPRNIDPeUAUqD6SPR3vVX5W9liyMG98NexVJ9udnCc',
'key_ops': ['encrypt', 'decrypt'],
'kty': 'oct',
},
'iv': 'Wdsf+tnOHIoAAAAAAAAAAA',
'hashes': {
'sha256': 'WgC7fw2alBC5t+xDx+PFlZxfFJXtIstQCg+j0WDaXxE',
},
'url': 'mxc://example.com/file',
'mimetype': 'text/plain',
},
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
var buffer = await event.downloadAndDecryptAttachment(
downloadCallback: downloadCallback,
);
expect(buffer.bytes, FILE_BUFF_DEC);
event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'body': 'image',
'msgtype': 'm.image',
'file': {
'v': 'v2',
'key': {
'alg': 'A256CTR',
'ext': true,
'k': '7aPRNIDPeUAUqD6SPR3vVX5W9liyMG98NexVJ9udnCc',
'key_ops': ['encrypt', 'decrypt'],
'kty': 'oct',
},
'iv': 'Wdsf+tnOHIoAAAAAAAAAAA',
'hashes': {
'sha256': 'WgC7fw2alBC5t+xDx+PFlZxfFJXtIstQCg+j0WDaXxE',
},
'url': 'mxc://example.com/file',
'mimetype': 'text/plain',
},
'info': {
'thumbnail_file': {
'v': 'v2',
'key': {
'alg': 'A256CTR',
'ext': true,
'k': 'TmF-rZYetZbxpL5yjDPE21UALQJcpEE6X-nvUDD5rA0',
'key_ops': ['encrypt', 'decrypt'],
'kty': 'oct',
},
'iv': '41ZqNRZSLFUAAAAAAAAAAA',
'hashes': {
'sha256': 'zccOwXiOTAYhGXyk0Fra7CRreBF6itjiCKdd+ov8mO4',
},
'url': 'mxc://example.com/thumb',
'mimetype': 'text/plain',
},
},
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
expect(event.hasAttachment, true);
expect(event.hasThumbnail, true);
expect(event.isAttachmentEncrypted, true);
expect(event.isThumbnailEncrypted, true);
expect(event.attachmentMimetype, 'text/plain');
expect(event.thumbnailMimetype, 'text/plain');
expect(event.attachmentMxcUrl.toString(), 'mxc://example.com/file');
expect(event.thumbnailMxcUrl.toString(), 'mxc://example.com/thumb');
buffer = await event.downloadAndDecryptAttachment(
downloadCallback: downloadCallback,
);
expect(buffer.bytes, FILE_BUFF_DEC);
buffer = await event.downloadAndDecryptAttachment(
getThumbnail: true,
downloadCallback: downloadCallback,
);
expect(buffer.bytes, THUMB_BUFF_DEC);
await room.client.dispose(closeDatabase: true);
},
);
test('downloadAndDecryptAttachment store', tags: 'olm', () async {
final FILE_BUFF = Uint8List.fromList([0]);
var serverHits = 0;
Future<Uint8List> downloadCallback(Uri uri) async {
serverHits++;
return {
'/_matrix/client/v1/media/download/example.org/newfile': FILE_BUFF,
}[uri.path]!;
}
await client.checkHomeserver(
Uri.parse('https://fakeserver.notexisting'),
checkWellKnown: false,
);
final room = Room(id: '!localpart:server.abc', client: await getClient());
final event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'body': 'image',
'msgtype': 'm.image',
'url': 'mxc://example.org/newfile',
'info': {
'size': 5,
},
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
expect(await event.isAttachmentInLocalStore(), false);
var buffer = await event.downloadAndDecryptAttachment(
downloadCallback: downloadCallback,
);
expect(
await event.isAttachmentInLocalStore(),
event.room.client.database.supportsFileStoring,
);
expect(buffer.bytes, FILE_BUFF);
expect(serverHits, 1);
buffer = await event.downloadAndDecryptAttachment(
downloadCallback: downloadCallback,
);
expect(buffer.bytes, FILE_BUFF);
expect(
serverHits,
event.room.client.database.supportsFileStoring ? 1 : 2,
);
await room.client.dispose(closeDatabase: true);
});
test('downloadAndDecryptAttachment store only', tags: 'olm', () async {
final FILE_BUFF = Uint8List.fromList([0]);
var serverHits = 0;
Future<Uint8List> downloadCallback(Uri uri) async {
serverHits++;
return {
'/_matrix/client/v1/media/download/example.org/newfile': FILE_BUFF,
}[uri.path]!;
}
await client.checkHomeserver(
Uri.parse('https://fakeserver.notexisting'),
checkWellKnown: false,
);
final room = Room(id: '!localpart:server.abc', client: await getClient());
final event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'body': 'image',
'msgtype': 'm.image',
'url': 'mxc://example.org/newfile',
'info': {
'size': 5,
},
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
var buffer = await event.downloadAndDecryptAttachment(
downloadCallback: downloadCallback,
);
expect(
await event.isAttachmentInLocalStore(),
event.room.client.database.supportsFileStoring,
);
expect(buffer.bytes, FILE_BUFF);
expect(serverHits, 1);
if (event.room.client.database.supportsFileStoring == true) {
buffer = await event.downloadAndDecryptAttachment(
downloadCallback: downloadCallback,
fromLocalStoreOnly: true,
);
expect(buffer.bytes, FILE_BUFF);
} else {
expect(
() async => await event.downloadAndDecryptAttachment(
downloadCallback: downloadCallback,
fromLocalStoreOnly: true,
),
throwsA(anything),
);
}
expect(serverHits, 1);
await room.client.dispose(closeDatabase: true);
});
test('downloadAndDecryptAttachment store only without file', tags: 'olm',
() async {
final FILE_BUFF = Uint8List.fromList([0]);
var serverHits = 0;
Future<Uint8List> downloadCallback(Uri uri) async {
serverHits++;
return {
'/_matrix/client/v1/media/download/example.org/newfile': FILE_BUFF,
}[uri.path]!;
}
await client.checkHomeserver(
Uri.parse('https://fakeserver.notexisting'),
checkWellKnown: false,
);
final room = Room(id: '!localpart:server.abc', client: await getClient());
final event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'body': 'image',
'msgtype': 'm.image',
'url': 'mxc://example.org/newfile',
'info': {
'size': 5,
},
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
expect(
() async => await event.downloadAndDecryptAttachment(
downloadCallback: downloadCallback,
fromLocalStoreOnly: true,
),
throwsA(anything),
);
expect(serverHits, 0);
await room.client.dispose(closeDatabase: true);
});
test('emote detection', () async {
var event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'msgtype': 'm.text',
'body': 'normal message',
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
expect(event.onlyEmotes, false);
expect(event.numberEmotes, 0);
event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'msgtype': 'm.text',
'body': 'normal message\n\nvery normal',
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
expect(event.onlyEmotes, false);
expect(event.numberEmotes, 0);
event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'msgtype': 'm.text',
'body': 'normal message with emoji 🦊',
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
expect(event.onlyEmotes, false);
expect(event.numberEmotes, 1);
event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'msgtype': 'm.text',
'body': '🦊',
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
expect(event.onlyEmotes, true);
expect(event.numberEmotes, 1);
event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'msgtype': 'm.text',
'body': '🦊🦊 🦊\n🦊🦊',
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
expect(event.onlyEmotes, true);
expect(event.numberEmotes, 5);
event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'msgtype': 'm.text',
'body': 'rich message',
'format': 'org.matrix.custom.html',
'formatted_body': 'rich message',
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
expect(event.onlyEmotes, false);
expect(event.numberEmotes, 0);
event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'msgtype': 'm.text',
'body': '🦊',
'format': 'org.matrix.custom.html',
'formatted_body': '🦊',
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
expect(event.onlyEmotes, true);
expect(event.numberEmotes, 1);
event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'msgtype': 'm.text',
'body': ':blah:',
'format': 'org.matrix.custom.html',
'formatted_body': '<img data-mx-emoticon src="mxc://blah/blubb">',
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
expect(event.onlyEmotes, true);
expect(event.numberEmotes, 1);
event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'msgtype': 'm.text',
'body': '🦊 :blah:',
'format': 'org.matrix.custom.html',
'formatted_body':
'🦊 <img data-mx-emoticon src="mxc://blah/blubb">',
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
expect(event.onlyEmotes, true);
expect(event.numberEmotes, 2);
// with variant selector
event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'msgtype': 'm.text',
'body': '❤️',
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
expect(event.onlyEmotes, true);
expect(event.numberEmotes, 1);
event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'msgtype': 'm.text',
'body': '''> <@alice:example.org> 😒😒
❤❤❤''',
'format': 'org.matrix.custom.html',
'formatted_body':
'<mx-reply><blockquote><a href="https://fakeserver.notexisting/\$jEsUZKDJdhlrceRyVU">In reply to</a> <a href="https://fakeserver.notexisting/@alice:example.org">@alice:example.org</a><br>😒😒</blockquote></mx-reply>❤❤❤',
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
expect(event.onlyEmotes, true);
expect(event.numberEmotes, 3);
event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'msgtype': 'm.text',
'body': '''> <@alice:example.org> A 😒
❤❤''',
'format': 'org.matrix.custom.html',
'formatted_body':
'<mx-reply><blockquote><a href="https://fakeserver.notexisting/\$jEsUZKDJdhlrceRyVU">In reply to</a> <a href="https://fakeserver.notexisting/@alice:example.org">@alice:example.org</a><br>A 😒</blockquote></mx-reply>❤❤',
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
expect(event.onlyEmotes, true);
expect(event.numberEmotes, 2);
event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'msgtype': 'm.text',
'body': '''> <@alice:example.org> 😒😒😒
❤A❤''',
'format': 'org.matrix.custom.html',
'formatted_body':
'<mx-reply><blockquote><a href="https://fakeserver.notexisting/\$jEsUZKDJdhlrceRyVU">In reply to</a> <a href="https://fakeserver.notexisting/@alice:example.org">@alice:example.org</a><br>😒😒😒</blockquote></mx-reply>❤A❤',
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
expect(event.onlyEmotes, false);
expect(event.numberEmotes, 2);
event = Event.fromJson(
{
'type': EventTypes.Message,
'content': {
'msgtype': 'm.text',
'body': '''> <@alice:example.org> A😒
❤A❤''',
'format': 'org.matrix.custom.html',
'formatted_body':
'<mx-reply><blockquote><a href="https://fakeserver.notexisting/\$jEsUZKDJdhlrceRyVU">In reply to</a> <a href="https://fakeserver.notexisting/@alice:example.org">@alice:example.org</a><br>A😒</blockquote></mx-reply>❤A❤',
},
'event_id': '\$edit2',
'sender': '@alice:example.org',
},
room,
);
expect(event.onlyEmotes, false);
expect(event.numberEmotes, 2);
});
test('Check conversion between types', () {
final matrixEvent = MatrixEvent.fromJson(
{
'content': {
'body': 'filename.jpg',
'info': {
'h': 398,
'mimetype': 'image/jpeg',
'size': 31037,
'w': 394,
},
'msgtype': 'm.image',
'url': 'mxc://example.org/JWEIFJgwEIhweiWJE',
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': room.id,
'sender': '@example:example.org',
'type': 'm.room.message',
'unsigned': {'age': 1234},
'redacts': 'abcd',
'prev_content': <String, Object?>{
'foo': 'bar',
},
},
);
final event = Event.fromMatrixEvent(matrixEvent, room);
expect(
event.toJson()..remove('status'),
matrixEvent.toJson(),
);
});
test('getReplyEvent fallback', () async {
final event = Event.fromJson(
{
'content': {
'msgtype': 'text',
'body': 'Hello world',
'm.relates_to': {
'rel_type': 'm.thread',
'event_id': '\$root',
'm.in_reply_to': {'event_id': '\$target'},
'is_falling_back': true,
},
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': room.id,
'sender': '@example:example.org',
'type': 'm.room.message',
'unsigned': {'age': 1234},
'redacts': 'abcd',
'prev_content': <String, Object?>{
'foo': 'bar',
},
},
room,
);
expect(event.relationshipType, RelationshipTypes.thread);
expect(event.relationshipEventId, '\$root');
final targetEvent = Event(
eventId: '\$target',
senderId: '@example:example.org',
type: 'm.room.message',
content: {
'msgtype': 'text',
'body': 'Hello world',
},
originServerTs: DateTime.now(),
room: room,
);
final timeline =
Timeline(room: room, chunk: TimelineChunk(events: [targetEvent]));
expect(await event.getReplyEvent(timeline), targetEvent);
});
test('getMentions', () {
final event = Event.fromJson(
{
'content': {
'msgtype': 'text',
'body': 'Hello world @alice:matrix.org',
'm.mentions': {
'user_ids': ['@alice:matrix.org'],
'room': false,
}
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
'room_id': room.id,
'sender': '@example:example.org',
'type': 'm.room.message',
'unsigned': {'age': 1234},
},
room,
);
expect(event.mentions.userIds, ['@alice:matrix.org']);
expect(event.mentions.room, false);
});
});
}