/* * 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 . */ import 'dart:convert'; import 'dart:typed_data'; import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart'; import 'package:olm/olm.dart' as olm; import 'package:test/test.dart'; import 'fake_client.dart'; import 'fake_matrix_api.dart'; import 'matrix_default_localizations.dart'; void main() { /// All Tests related to the Event group('Event', () { Logs().level = Level.error; var olmEnabled = true; 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 = 'Hello 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 = { 'event_id': id, 'sender': senderID, 'origin_server_ts': timestamp, 'type': type, 'room_id': '!testroom:example.abc', 'status': EventStatus.synced.intValue, 'content': contentJson, }; final client = Client('testclient', httpClient: FakeMatrixApi()); final room = Room(id: '!testroom:example.abc', client: client); final event = Event.fromJson( jsonObj, Room(id: '!testroom:example.abc', client: client)); test('setup', () async { try { await olm.init(); olm.get_library_version(); } catch (e) { olmEnabled = false; Logs().w('[LibOlm] Failed to load LibOlm', e); } Logs().i('[LibOlm] Enabled: $olmEnabled'); }); test('Create from json', () async { jsonObj.remove('status'); 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'] = {}; 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'] = { 'msgtype': 'm.text', 'text': 'beep', }; event = Event.fromJson(jsonObj, room); expect(event.relationshipType, null); expect(event.relationshipEventId, null); jsonObj['content']['m.relates_to'] = { '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.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')); 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} }; 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); redactionEventJson.remove('redacts'); expect(event.unsigned?['redacted_because'], redactionEventJson); } }); test('remove', () async { final event = Event.fromJson( jsonObj, Room(id: '1234', client: Client('testclient'))); final removed1 = await event.remove(); event.status = EventStatus.sending; final removed2 = await event.remove(); expect(removed1, false); expect(removed2, true); }); test('sendAgain', () async { final matrix = Client('testclient', httpClient: FakeMatrixApi()); await matrix.checkHomeserver('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', () async { final matrix = Client('testclient', httpClient: FakeMatrixApi()); await matrix.checkHomeserver('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', () async { 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 { expect(event.canRedact, true); }); test('getLocalizedBody, isEventKnown', () async { final matrix = Client('testclient', httpClient: FakeMatrixApi()); 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(event.getLocalizedBody(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(event.getLocalizedBody(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(event.getLocalizedBody(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(event.getLocalizedBody(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(event.getLocalizedBody(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(event.getLocalizedBody(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(event.getLocalizedBody(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(event.getLocalizedBody(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(event.getLocalizedBody(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.getLocalizedBody(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.getLocalizedBody(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.getLocalizedBody(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.getLocalizedBody(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.getLocalizedBody(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.getLocalizedBody(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.getLocalizedBody(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.getLocalizedBody(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(event.getLocalizedBody(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(event.getLocalizedBody(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(event.getLocalizedBody(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(event.getLocalizedBody(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(event.getLocalizedBody(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(event.getLocalizedBody(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': 'This is an example text message', '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(event.getLocalizedBody(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 this 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(event.getLocalizedBody(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 example 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(event.getLocalizedBody(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(event.getLocalizedBody(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(event.getLocalizedBody(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(event.getLocalizedBody(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(event.getLocalizedBody(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(event.getLocalizedBody(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(event.getLocalizedBody(MatrixDefaultLocalizations()), 'Unknown event unknown.event.type'); expect(event.isEventTypeKnown, false); }); test('getLocalizedBody, parameters', () { final matrix = Client('testclient', httpClient: FakeMatrixApi()); 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': 'This is an example text message', '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( event.getLocalizedBody(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': '* This is an example text message', 'msgtype': 'm.text', 'm.relates_to': { 'rel_type': 'm.replace', 'event_id': '\$some_event', }, 'm.new_content': { 'body': 'This is an example text message', 'format': 'org.matrix.custom.html', 'formatted_body': 'This is an example text message', '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( event.getLocalizedBody(MatrixDefaultLocalizations(), hideEdit: true), 'This is an example text message'); expect( event.getLocalizedBody(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': 'beephmm, fox', '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( event.getLocalizedBody(MatrixDefaultLocalizations(), hideReply: true), 'hmm, fox'); expect( event.getLocalizedBody(MatrixDefaultLocalizations(), hideReply: true, plaintextBody: true), 'hmm, *fox*'); }); 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(events: [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), {}); 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), {}); }); test('plaintextBody', () { final event = Event.fromJson({ 'type': EventTypes.Message, 'content': { 'body': 'blah', 'msgtype': 'm.text', 'format': 'org.matrix.custom.html', 'formatted_body': 'blah', }, 'event_id': '\$source', 'sender': '@alice:example.org', }, room); expect(event.plaintextBody, '**blah**'); }); 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(events: [event], room: room)); expect(displayEvent.body, 'blah'); // one edit displayEvent = event .getDisplayEvent(Timeline(events: [event, edit1], room: room)); expect(displayEvent.body, 'edit 1'); // two edits displayEvent = event.getDisplayEvent( Timeline(events: [event, edit1, edit2], room: room)); expect(displayEvent.body, 'edit 2'); // foreign edit displayEvent = event .getDisplayEvent(Timeline(events: [event, edit3], room: room)); expect(displayEvent.body, 'blah'); // mixed foreign and non-foreign displayEvent = event.getDisplayEvent( Timeline(events: [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(events: [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]); final downloadCallback = (Uri uri) async { return { '/_matrix/media/r0/download/example.org/file': FILE_BUFF, '/_matrix/media/r0/download/example.org/thumb': THUMBNAIL_BUFF, }[uri.path]!; }; await client.checkHomeserver('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(event.getAttachmentUrl().toString(), 'https://fakeserver.notexisting/_matrix/media/r0/download/example.org/file'); expect(event.getAttachmentUrl(getThumbnail: true).toString(), 'https://fakeserver.notexisting/_matrix/media/r0/thumbnail/example.org/file?width=800&height=800&method=scale&animated=false'); expect(event.getAttachmentUrl(useThumbnailMxcUrl: true).toString(), 'https://fakeserver.notexisting/_matrix/media/r0/download/example.org/thumb'); expect( event .getAttachmentUrl(getThumbnail: true, useThumbnailMxcUrl: true) .toString(), 'https://fakeserver.notexisting/_matrix/media/r0/thumbnail/example.org/thumb?width=800&height=800&method=scale&animated=false'); expect( event .getAttachmentUrl(getThumbnail: true, minNoThumbSize: 9000000) .toString(), 'https://fakeserver.notexisting/_matrix/media/r0/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', () async { if (!olmEnabled) return; 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]); final downloadCallback = (Uri uri) async { return { '/_matrix/media/r0/download/example.com/file': FILE_BUFF_ENC, '/_matrix/media/r0/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', () async { final FILE_BUFF = Uint8List.fromList([0]); var serverHits = 0; final downloadCallback = (Uri uri) async { serverHits++; return { '/_matrix/media/r0/download/example.org/newfile': FILE_BUFF, }[uri.path]!; }; await client.checkHomeserver('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('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': '' }, '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': '🦊 ' }, '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': '\
In reply to @alice:example.org
😒😒
❤❤❤' }, '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': '\
In reply to @alice:example.org
A 😒
❤❤' }, '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': '\
In reply to @alice:example.org
😒😒😒
❤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': '\
In reply to @alice:example.org
A😒
❤A❤' }, 'event_id': '\$edit2', 'sender': '@alice:example.org', }, room); expect(event.onlyEmotes, false); expect(event.numberEmotes, 2); }); }); }