/*
 *   Ansible inventory script used at Famedly GmbH for managing many hosts
 *   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:famedlysdk/famedlysdk.dart';
import 'package:famedlysdk/matrix_api.dart';
import 'package:famedlysdk/encryption.dart';
import 'package:famedlysdk/src/event.dart';
import 'package:famedlysdk/matrix_api/utils/logs.dart';
import 'package:logger/logger.dart';
import 'package:test/test.dart';
import 'package:olm/olm.dart' as olm;
import 'fake_client.dart';
import 'fake_matrix_api.dart';
import 'fake_matrix_localizations.dart';
void main() {
  /// All Tests related to the Event
  group('Event', () {
    Logs().level = Level.error;
    var olmEnabled = true;
    try {
      olm.init();
      olm.Account();
    } catch (e) {
      olmEnabled = false;
      Logs().w('[LibOlm] Failed to load LibOlm', e);
    }
    Logs().i('[LibOlm] Enabled: $olmEnabled');
    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"}}}';
    var jsonObj = {
      'event_id': id,
      'sender': senderID,
      'origin_server_ts': timestamp,
      'type': type,
      'room_id': '1234',
      'status': 2,
      'content': contentJson,
    };
    var client = Client('testclient', httpClient: FakeMatrixApi());
    var event = Event.fromJson(
        jsonObj, Room(id: '!localpart:server.abc', client: client));
    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, 2);
      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'] = '';
      var state = Event.fromJson(jsonObj, null);
      expect(state.eventId, id);
      expect(state.stateKey, '');
      expect(state.status, 2);
    });
    test('Test all EventTypes', () async {
      Event event;
      jsonObj['type'] = 'm.room.avatar';
      event = Event.fromJson(jsonObj, null);
      expect(event.type, EventTypes.RoomAvatar);
      jsonObj['type'] = 'm.room.name';
      event = Event.fromJson(jsonObj, null);
      expect(event.type, EventTypes.RoomName);
      jsonObj['type'] = 'm.room.topic';
      event = Event.fromJson(jsonObj, null);
      expect(event.type, EventTypes.RoomTopic);
      jsonObj['type'] = 'm.room.aliases';
      event = Event.fromJson(jsonObj, null);
      expect(event.type, EventTypes.RoomAliases);
      jsonObj['type'] = 'm.room.canonical_alias';
      event = Event.fromJson(jsonObj, null);
      expect(event.type, EventTypes.RoomCanonicalAlias);
      jsonObj['type'] = 'm.room.create';
      event = Event.fromJson(jsonObj, null);
      expect(event.type, EventTypes.RoomCreate);
      jsonObj['type'] = 'm.room.join_rules';
      event = Event.fromJson(jsonObj, null);
      expect(event.type, EventTypes.RoomJoinRules);
      jsonObj['type'] = 'm.room.member';
      event = Event.fromJson(jsonObj, null);
      expect(event.type, EventTypes.RoomMember);
      jsonObj['type'] = 'm.room.power_levels';
      event = Event.fromJson(jsonObj, null);
      expect(event.type, EventTypes.RoomPowerLevels);
      jsonObj['type'] = 'm.room.guest_access';
      event = Event.fromJson(jsonObj, null);
      expect(event.type, EventTypes.GuestAccess);
      jsonObj['type'] = 'm.room.history_visibility';
      event = Event.fromJson(jsonObj, null);
      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, null);
      expect(event.messageType, MessageTypes.Notice);
      jsonObj['content']['msgtype'] = 'm.emote';
      event = Event.fromJson(jsonObj, null);
      expect(event.messageType, MessageTypes.Emote);
      jsonObj['content']['msgtype'] = 'm.image';
      event = Event.fromJson(jsonObj, null);
      expect(event.messageType, MessageTypes.Image);
      jsonObj['content']['msgtype'] = 'm.video';
      event = Event.fromJson(jsonObj, null);
      expect(event.messageType, MessageTypes.Video);
      jsonObj['content']['msgtype'] = 'm.audio';
      event = Event.fromJson(jsonObj, null);
      expect(event.messageType, MessageTypes.Audio);
      jsonObj['content']['msgtype'] = 'm.file';
      event = Event.fromJson(jsonObj, null);
      expect(event.messageType, MessageTypes.File);
      jsonObj['content']['msgtype'] = 'm.location';
      event = Event.fromJson(jsonObj, null);
      expect(event.messageType, MessageTypes.Location);
      jsonObj['type'] = 'm.sticker';
      jsonObj['content']['msgtype'] = null;
      event = Event.fromJson(jsonObj, null);
      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, null);
      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, null);
      expect(event.relationshipType, null);
      expect(event.relationshipEventId, null);
      jsonObj['content']['m.relates_to'] = {
        'rel_type': 'm.replace',
        'event_id': 'abc',
      };
      event = Event.fromJson(jsonObj, null);
      expect(event.relationshipType, RelationshipTypes.Edit);
      expect(event.relationshipEventId, 'abc');
      jsonObj['content']['m.relates_to']['rel_type'] = 'm.annotation';
      event = Event.fromJson(jsonObj, null);
      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, null);
      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}
        };
        var redactedBecause = Event.fromJson(redactionEventJson, room);
        var 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 {
      var event = Event.fromJson(
          jsonObj, Room(id: '1234', client: Client('testclient')));
      final removed1 = await event.remove();
      event.status = 0;
      final removed2 = await event.remove();
      expect(removed1, false);
      expect(removed2, true);
    });
    test('sendAgain', () async {
      var matrix = Client('testclient', httpClient: FakeMatrixApi());
      await matrix.checkHomeserver('https://fakeServer.notExisting');
      await matrix.login(user: 'test', password: '1234');
      var event = Event.fromJson(
          jsonObj, Room(id: '!1234:example.com', client: matrix));
      final resp1 = await event.sendAgain();
      event.status = -1;
      final resp2 = await event.sendAgain(txid: '1234');
      expect(resp1, null);
      expect(resp2.startsWith('\$event'), true);
      await matrix.dispose(closeDatabase: true);
    });
    test('requestKey', () async {
      var matrix = Client('testclient', httpClient: FakeMatrixApi());
      await matrix.checkHomeserver('https://fakeServer.notExisting');
      await matrix.login(user: 'test', password: '1234');
      var 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');
      var event2 = Event.fromJson({
        'event_id': id,
        'sender': senderID,
        'origin_server_ts': timestamp,
        'type': 'm.room.encrypted',
        'room_id': '1234',
        'status': 2,
        'content': json.encode({
          'msgtype': 'm.bad.encrypted',
          'body': DecryptError.UNKNOWN_SESSION,
          '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';
      var 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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()),
          'Example activatedEndToEndEncryption. needPantalaimonWarning');
      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(FakeMatrixLocalizations()),
          '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(FakeMatrixLocalizations()),
          '* 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(FakeMatrixLocalizations()),
          '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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      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(FakeMatrixLocalizations()), null);
      expect(event.isEventTypeKnown, false);
    });
    test('aggregations', () {
      var event = Event.fromJson({
        'content': {
          'body': 'blah',
          'msgtype': 'm.text',
        },
        'event_id': '\$source',
      }, null);
      var edit1 = Event.fromJson({
        'content': {
          'body': 'blah',
          'msgtype': 'm.text',
          'm.relates_to': {
            'event_id': '\$source',
            'rel_type': RelationshipTypes.Edit,
          },
        },
        'event_id': '\$edit1',
      }, null);
      var edit2 = Event.fromJson({
        'content': {
          'body': 'blah',
          'msgtype': 'm.text',
          'm.relates_to': {
            'event_id': '\$source',
            'rel_type': RelationshipTypes.Edit,
          },
        },
        'event_id': '\$edit2',
      }, null);
      var room = Room(client: client);
      var 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('getDisplayEvent', () {
      var event = Event.fromJson({
        'type': EventTypes.Message,
        'content': {
          'body': 'blah',
          'msgtype': 'm.text',
        },
        'event_id': '\$source',
        'sender': '@alice:example.org',
      }, null);
      event.sortOrder = 0;
      var 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',
      }, null);
      edit1.sortOrder = 1;
      var 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',
      }, null);
      edit2.sortOrder = 2;
      var 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',
      }, null);
      edit3.sortOrder = 3;
      var room = Room(client: client);
      // 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': {
            'evnet_id': '\$redact',
            'sender': '@alice:example.org',
          },
        },
      }, null);
      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 = (String url) async {
        return {
          'https://fakeserver.notexisting/_matrix/media/r0/download/example.org/file':
              FILE_BUFF,
          'https://fakeserver.notexisting/_matrix/media/r0/download/example.org/thumb':
              THUMBNAIL_BUFF,
        }[url];
      };
      await client.checkHomeserver('https://fakeServer.notExisting');
      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(), 'mxc://example.org/file');
      expect(event.attachmentOrThumbnailMxcUrl(getThumbnail: true),
          '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, 'mxc://example.org/file');
      expect(event.thumbnailMxcUrl, 'mxc://example.org/thumb');
      expect(event.attachmentOrThumbnailMxcUrl(), 'mxc://example.org/file');
      expect(event.attachmentOrThumbnailMxcUrl(getThumbnail: true),
          'mxc://example.org/thumb');
      expect(event.getAttachmentUrl(),
          'https://fakeserver.notexisting/_matrix/media/r0/download/example.org/file');
      expect(event.getAttachmentUrl(getThumbnail: true),
          'https://fakeserver.notexisting/_matrix/media/r0/thumbnail/example.org/file?width=800&height=800&method=scale');
      expect(event.getAttachmentUrl(useThumbnailMxcUrl: true),
          'https://fakeserver.notexisting/_matrix/media/r0/download/example.org/thumb');
      expect(
          event.getAttachmentUrl(getThumbnail: true, useThumbnailMxcUrl: true),
          'https://fakeserver.notexisting/_matrix/media/r0/thumbnail/example.org/thumb?width=800&height=800&method=scale');
      expect(
          event.getAttachmentUrl(getThumbnail: true, minNoThumbSize: 9000000),
          '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 = (String url) async {
        return {
          'https://fakeserver.notexisting/_matrix/media/r0/download/example.com/file':
              FILE_BUFF_ENC,
          'https://fakeserver.notexisting/_matrix/media/r0/download/example.com/thumb':
              THUMB_BUFF_ENC,
        }[url];
      };
      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, 'mxc://example.com/file');
      expect(event.thumbnailMxcUrl, '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 = (String url) async {
        serverHits++;
        return {
          'https://fakeserver.notexisting/_matrix/media/r0/download/example.org/newfile':
              FILE_BUFF,
        }[url];
      };
      await client.checkHomeserver('https://fakeServer.notExisting');
      final room = Room(id: '!localpart:server.abc', client: await getClient());
      var 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(), true);
      expect(buffer.bytes, FILE_BUFF);
      expect(serverHits, 1);
      buffer = await event.downloadAndDecryptAttachment(
          downloadCallback: downloadCallback);
      expect(buffer.bytes, FILE_BUFF);
      expect(serverHits, 1);
      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',
      }, null);
      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',
      }, null);
      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',
      }, null);
      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',
      }, null);
      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',
      }, null);
      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',
      }, null);
      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',
      }, null);
      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',
      }, null);
      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',
      }, null);
      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',
      }, null);
      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',
      }, null);
      expect(event.onlyEmotes, true);
      expect(event.numberEmotes, 1);
    });
  });
}
'
        },
        'event_id': '\$edit2',
        'sender': '@alice:example.org',
      }, null);
      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',
      }, null);
      expect(event.onlyEmotes, true);
      expect(event.numberEmotes, 1);
    });
  });
}