From 4c1e4f5127bd7fc8a3ab852d630242a704ffbda2 Mon Sep 17 00:00:00 2001
From: Clark Fischer <439978+clarkf@users.noreply.github.com>
Date: Mon, 30 Jan 2023 14:31:32 +0000
Subject: [PATCH] Fix "[object Promise]" appearing in HTML exports (#9975)

Fixes https://github.com/vector-im/element-web/issues/24272
---
 src/DateUtils.ts                              |   2 +-
 src/components/structures/MessagePanel.tsx    |   4 +-
 .../dialogs/MessageEditHistoryDialog.tsx      |   2 +-
 .../views/rooms/SearchResultTile.tsx          |   7 +-
 src/utils/exportUtils/HtmlExport.tsx          |  43 ++-
 src/utils/exportUtils/exportCSS.ts            |   4 +-
 .../dialogs/MessageEditHistoryDialog-test.tsx |  83 +++++
 .../MessageEditHistoryDialog-test.tsx.snap    | 322 ++++++++++++++++++
 .../views/rooms/SearchResultTile-test.tsx     | 110 +++---
 test/test-utils/test-utils.ts                 |   4 +
 test/utils/exportUtils/HTMLExport-test.ts     | 286 +++++++++++++++-
 .../__snapshots__/HTMLExport-test.ts.snap     |  86 +++++
 test/utils/exportUtils/exportCSS-test.ts      |  26 ++
 13 files changed, 895 insertions(+), 84 deletions(-)
 create mode 100644 test/components/views/dialogs/MessageEditHistoryDialog-test.tsx
 create mode 100644 test/components/views/dialogs/__snapshots__/MessageEditHistoryDialog-test.tsx.snap
 create mode 100644 test/utils/exportUtils/exportCSS-test.ts

diff --git a/src/DateUtils.ts b/src/DateUtils.ts
index 5973a7c5f2..c1aa69aacd 100644
--- a/src/DateUtils.ts
+++ b/src/DateUtils.ts
@@ -175,7 +175,7 @@ function withinCurrentYear(prevDate: Date, nextDate: Date): boolean {
     return prevDate.getFullYear() === nextDate.getFullYear();
 }
 
-export function wantsDateSeparator(prevEventDate: Date, nextEventDate: Date): boolean {
+export function wantsDateSeparator(prevEventDate: Date | undefined, nextEventDate: Date | undefined): boolean {
     if (!nextEventDate || !prevEventDate) {
         return false;
     }
diff --git a/src/components/structures/MessagePanel.tsx b/src/components/structures/MessagePanel.tsx
index 98e8f79ec7..2dd432cb92 100644
--- a/src/components/structures/MessagePanel.tsx
+++ b/src/components/structures/MessagePanel.tsx
@@ -72,7 +72,7 @@ const groupedStateEvents = [
 // check if there is a previous event and it has the same sender as this event
 // and the types are the same/is in continuedTypes and the time between them is <= CONTINUATION_MAX_INTERVAL
 export function shouldFormContinuation(
-    prevEvent: MatrixEvent,
+    prevEvent: MatrixEvent | null,
     mxEvent: MatrixEvent,
     showHiddenEvents: boolean,
     threadsEnabled: boolean,
@@ -821,7 +821,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
             // here.
             return !this.props.canBackPaginate;
         }
-        return wantsDateSeparator(prevEvent.getDate(), nextEventDate);
+        return wantsDateSeparator(prevEvent.getDate() || undefined, nextEventDate);
     }
 
     // Get a list of read receipts that should be shown next to this event
diff --git a/src/components/views/dialogs/MessageEditHistoryDialog.tsx b/src/components/views/dialogs/MessageEditHistoryDialog.tsx
index 943e7f58d2..8775b4eb5c 100644
--- a/src/components/views/dialogs/MessageEditHistoryDialog.tsx
+++ b/src/components/views/dialogs/MessageEditHistoryDialog.tsx
@@ -130,7 +130,7 @@ export default class MessageEditHistoryDialog extends React.PureComponent<IProps
         }
         const baseEventId = this.props.mxEvent.getId();
         allEvents.forEach((e, i) => {
-            if (!lastEvent || wantsDateSeparator(lastEvent.getDate(), e.getDate())) {
+            if (!lastEvent || wantsDateSeparator(lastEvent.getDate() || undefined, e.getDate() || undefined)) {
                 nodes.push(
                     <li key={e.getTs() + "~"}>
                         <DateSeparator roomId={e.getRoomId()} ts={e.getTs()} />
diff --git a/src/components/views/rooms/SearchResultTile.tsx b/src/components/views/rooms/SearchResultTile.tsx
index 067cbaee38..3ec68b989f 100644
--- a/src/components/views/rooms/SearchResultTile.tsx
+++ b/src/components/views/rooms/SearchResultTile.tsx
@@ -84,7 +84,7 @@ export default class SearchResultTile extends React.Component<IProps> {
                 // is this a continuation of the previous message?
                 const continuation =
                     prevEv &&
-                    !wantsDateSeparator(prevEv.getDate(), mxEv.getDate()) &&
+                    !wantsDateSeparator(prevEv.getDate() || undefined, mxEv.getDate() || undefined) &&
                     shouldFormContinuation(
                         prevEv,
                         mxEv,
@@ -96,7 +96,10 @@ export default class SearchResultTile extends React.Component<IProps> {
                 let lastInSection = true;
                 const nextEv = timeline[j + 1];
                 if (nextEv) {
-                    const willWantDateSeparator = wantsDateSeparator(mxEv.getDate(), nextEv.getDate());
+                    const willWantDateSeparator = wantsDateSeparator(
+                        mxEv.getDate() || undefined,
+                        nextEv.getDate() || undefined,
+                    );
                     lastInSection =
                         willWantDateSeparator ||
                         mxEv.getSender() !== nextEv.getSender() ||
diff --git a/src/utils/exportUtils/HtmlExport.tsx b/src/utils/exportUtils/HtmlExport.tsx
index 6b4240375c..e915d18025 100644
--- a/src/utils/exportUtils/HtmlExport.tsx
+++ b/src/utils/exportUtils/HtmlExport.tsx
@@ -1,5 +1,5 @@
 /*
-Copyright 2021 The Matrix.org Foundation C.I.C.
+Copyright 2021, 2023 The Matrix.org Foundation C.I.C.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -66,7 +66,7 @@ export default class HTMLExporter extends Exporter {
     }
 
     protected async getRoomAvatar(): Promise<ReactNode> {
-        let blob: Blob;
+        let blob: Blob | undefined = undefined;
         const avatarUrl = Avatar.avatarUrlForRoom(this.room, 32, 32, "crop");
         const avatarPath = "room.png";
         if (avatarUrl) {
@@ -85,7 +85,7 @@ export default class HTMLExporter extends Exporter {
                 height={32}
                 name={this.room.name}
                 title={this.room.name}
-                url={blob ? avatarPath : null}
+                url={blob ? avatarPath : ""}
                 resizeMethod="crop"
             />
         );
@@ -96,9 +96,9 @@ export default class HTMLExporter extends Exporter {
         const roomAvatar = await this.getRoomAvatar();
         const exportDate = formatFullDateNoDayNoTime(new Date());
         const creator = this.room.currentState.getStateEvents(EventType.RoomCreate, "")?.getSender();
-        const creatorName = this.room?.getMember(creator)?.rawDisplayName || creator;
-        const exporter = this.client.getUserId();
-        const exporterName = this.room?.getMember(exporter)?.rawDisplayName;
+        const creatorName = (creator ? this.room.getMember(creator)?.rawDisplayName : creator) || creator;
+        const exporter = this.client.getUserId()!;
+        const exporterName = this.room.getMember(exporter)?.rawDisplayName;
         const topic = this.room.currentState.getStateEvents(EventType.RoomTopic, "")?.getContent()?.topic || "";
         const createdText = _t("%(creatorName)s created this room.", {
             creatorName,
@@ -217,20 +217,19 @@ export default class HTMLExporter extends Exporter {
         </html>`;
     }
 
-    protected getAvatarURL(event: MatrixEvent): string {
+    protected getAvatarURL(event: MatrixEvent): string | undefined {
         const member = event.sender;
-        return (
-            member.getMxcAvatarUrl() && mediaFromMxc(member.getMxcAvatarUrl()).getThumbnailOfSourceHttp(30, 30, "crop")
-        );
+        const avatarUrl = member?.getMxcAvatarUrl();
+        return avatarUrl ? mediaFromMxc(avatarUrl).getThumbnailOfSourceHttp(30, 30, "crop") : undefined;
     }
 
     protected async saveAvatarIfNeeded(event: MatrixEvent): Promise<void> {
-        const member = event.sender;
+        const member = event.sender!;
         if (!this.avatars.has(member.userId)) {
             try {
                 const avatarUrl = this.getAvatarURL(event);
                 this.avatars.set(member.userId, true);
-                const image = await fetch(avatarUrl);
+                const image = await fetch(avatarUrl!);
                 const blob = await image.blob();
                 this.addFile(`users/${member.userId.replace(/:/g, "-")}.png`, blob);
             } catch (err) {
@@ -239,19 +238,19 @@ export default class HTMLExporter extends Exporter {
         }
     }
 
-    protected async getDateSeparator(event: MatrixEvent): Promise<string> {
+    protected getDateSeparator(event: MatrixEvent): string {
         const ts = event.getTs();
         const dateSeparator = (
             <li key={ts}>
-                <DateSeparator forExport={true} key={ts} roomId={event.getRoomId()} ts={ts} />
+                <DateSeparator forExport={true} key={ts} roomId={event.getRoomId()!} ts={ts} />
             </li>
         );
         return renderToStaticMarkup(dateSeparator);
     }
 
-    protected async needsDateSeparator(event: MatrixEvent, prevEvent: MatrixEvent): Promise<boolean> {
-        if (prevEvent == null) return true;
-        return wantsDateSeparator(prevEvent.getDate(), event.getDate());
+    protected needsDateSeparator(event: MatrixEvent, prevEvent: MatrixEvent | null): boolean {
+        if (!prevEvent) return true;
+        return wantsDateSeparator(prevEvent.getDate() || undefined, event.getDate() || undefined);
     }
 
     public getEventTile(mxEv: MatrixEvent, continuation: boolean): JSX.Element {
@@ -264,9 +263,7 @@ export default class HTMLExporter extends Exporter {
                         isRedacted={mxEv.isRedacted()}
                         replacingEventId={mxEv.replacingEventId()}
                         forExport={true}
-                        readReceipts={null}
                         alwaysShowTimestamps={true}
-                        readReceiptMap={null}
                         showUrlPreview={false}
                         checkUnmounting={() => false}
                         isTwelveHour={false}
@@ -275,7 +272,6 @@ export default class HTMLExporter extends Exporter {
                         permalinkCreator={this.permalinkCreator}
                         lastSuccessful={false}
                         isSelectedEvent={false}
-                        getRelationsForEvent={null}
                         showReactions={false}
                         layout={Layout.Group}
                         showReadReceipts={false}
@@ -286,7 +282,8 @@ export default class HTMLExporter extends Exporter {
     }
 
     protected async getEventTileMarkup(mxEv: MatrixEvent, continuation: boolean, filePath?: string): Promise<string> {
-        const hasAvatar = !!this.getAvatarURL(mxEv);
+        const avatarUrl = this.getAvatarURL(mxEv);
+        const hasAvatar = !!avatarUrl;
         if (hasAvatar) await this.saveAvatarIfNeeded(mxEv);
         const EventTile = this.getEventTile(mxEv, continuation);
         let eventTileMarkup: string;
@@ -312,8 +309,8 @@ export default class HTMLExporter extends Exporter {
         eventTileMarkup = eventTileMarkup.replace(/<span class="mx_MFileBody_info_icon".*?>.*?<\/span>/, "");
         if (hasAvatar) {
             eventTileMarkup = eventTileMarkup.replace(
-                encodeURI(this.getAvatarURL(mxEv)).replace(/&/g, "&amp;"),
-                `users/${mxEv.sender.userId.replace(/:/g, "-")}.png`,
+                encodeURI(avatarUrl).replace(/&/g, "&amp;"),
+                `users/${mxEv.sender!.userId.replace(/:/g, "-")}.png`,
             );
         }
         return eventTileMarkup;
diff --git a/src/utils/exportUtils/exportCSS.ts b/src/utils/exportUtils/exportCSS.ts
index f92e339b02..2a6a098a14 100644
--- a/src/utils/exportUtils/exportCSS.ts
+++ b/src/utils/exportUtils/exportCSS.ts
@@ -58,8 +58,8 @@ const getExportCSS = async (usedClasses: Set<string>): Promise<string> => {
 
     // If the light theme isn't loaded we will have to fetch & parse it manually
     if (!stylesheets.some(isLightTheme)) {
-        const href = document.querySelector<HTMLLinkElement>('link[rel="stylesheet"][href$="theme-light.css"]').href;
-        stylesheets.push(await getRulesFromCssFile(href));
+        const href = document.querySelector<HTMLLinkElement>('link[rel="stylesheet"][href$="theme-light.css"]')?.href;
+        if (href) stylesheets.push(await getRulesFromCssFile(href));
     }
 
     let css = "";
diff --git a/test/components/views/dialogs/MessageEditHistoryDialog-test.tsx b/test/components/views/dialogs/MessageEditHistoryDialog-test.tsx
new file mode 100644
index 0000000000..cadb92e488
--- /dev/null
+++ b/test/components/views/dialogs/MessageEditHistoryDialog-test.tsx
@@ -0,0 +1,83 @@
+/*
+Copyright 2023 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from "react";
+import { render, RenderResult } from "@testing-library/react";
+import { EventType, MatrixEvent } from "matrix-js-sdk/src/matrix";
+
+import type { MatrixClient } from "matrix-js-sdk/src/matrix";
+import { flushPromises, mkMessage, stubClient } from "../../../test-utils";
+import MessageEditHistoryDialog from "../../../../src/components/views/dialogs/MessageEditHistoryDialog";
+
+describe("<MessageEditHistory />", () => {
+    const roomId = "!aroom:example.com";
+    let client: jest.Mocked<MatrixClient>;
+    let event: MatrixEvent;
+
+    beforeEach(() => {
+        client = stubClient() as jest.Mocked<MatrixClient>;
+        event = mkMessage({
+            event: true,
+            user: "@user:example.com",
+            room: "!room:example.com",
+            msg: "My Great Message",
+        });
+    });
+
+    async function renderComponent(): Promise<RenderResult> {
+        const result = render(<MessageEditHistoryDialog mxEvent={event} onFinished={jest.fn()} />);
+        await flushPromises();
+        return result;
+    }
+
+    function mockEdits(...edits: { msg: string; ts: number | undefined }[]) {
+        client.relations.mockImplementation(() =>
+            Promise.resolve({
+                events: edits.map(
+                    (e) =>
+                        new MatrixEvent({
+                            type: EventType.RoomMessage,
+                            room_id: roomId,
+                            origin_server_ts: e.ts,
+                            content: {
+                                body: e.msg,
+                            },
+                        }),
+                ),
+            }),
+        );
+    }
+
+    it("should match the snapshot", async () => {
+        mockEdits({ msg: "My Great Massage", ts: 1234 });
+
+        const { container } = await renderComponent();
+
+        expect(container).toMatchSnapshot();
+    });
+
+    it("should support events with ", async () => {
+        mockEdits(
+            { msg: "My Great Massage", ts: undefined },
+            { msg: "My Great Massage?", ts: undefined },
+            { msg: "My Great Missage", ts: undefined },
+        );
+
+        const { container } = await renderComponent();
+
+        expect(container).toMatchSnapshot();
+    });
+});
diff --git a/test/components/views/dialogs/__snapshots__/MessageEditHistoryDialog-test.tsx.snap b/test/components/views/dialogs/__snapshots__/MessageEditHistoryDialog-test.tsx.snap
new file mode 100644
index 0000000000..0eb2683003
--- /dev/null
+++ b/test/components/views/dialogs/__snapshots__/MessageEditHistoryDialog-test.tsx.snap
@@ -0,0 +1,322 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`<MessageEditHistory /> should match the snapshot 1`] = `
+<div>
+  <div
+    data-focus-guard="true"
+    style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
+    tabindex="0"
+  />
+  <div
+    aria-labelledby="mx_BaseDialog_title"
+    class="mx_MessageEditHistoryDialog mx_Dialog_fixedWidth"
+    data-focus-lock-disabled="false"
+    role="dialog"
+  >
+    <div
+      class="mx_Dialog_header mx_Dialog_headerWithCancel"
+    >
+      <h2
+        class="mx_Heading_h2 mx_Dialog_title"
+        id="mx_BaseDialog_title"
+      >
+        Message edits
+      </h2>
+      <div
+        aria-label="Close dialog"
+        class="mx_AccessibleButton mx_Dialog_cancelButton"
+        role="button"
+        tabindex="0"
+      />
+    </div>
+    <div
+      class="mx_AutoHideScrollbar mx_ScrollPanel mx_MessageEditHistoryDialog_scrollPanel"
+      tabindex="-1"
+    >
+      <div
+        class="mx_RoomView_messageListWrapper"
+      >
+        <ol
+          aria-live="polite"
+          class="mx_RoomView_MessageList"
+        >
+          <ul
+            class="mx_MessageEditHistoryDialog_edits"
+          >
+            <li>
+              <div
+                aria-label="Thu, Jan 1 1970"
+                class="mx_DateSeparator"
+                role="separator"
+                tabindex="-1"
+              >
+                <hr
+                  role="none"
+                />
+                <h2
+                  aria-hidden="true"
+                >
+                  Thu, Jan 1 1970
+                </h2>
+                <hr
+                  role="none"
+                />
+              </div>
+            </li>
+            <li>
+              <div
+                class="mx_EventTile"
+              >
+                <div
+                  class="mx_EventTile_line"
+                >
+                  <span
+                    class="mx_MessageTimestamp"
+                  >
+                    00:00
+                  </span>
+                  <div
+                    class="mx_EventTile_content"
+                  >
+                    <span
+                      class="mx_EventTile_body"
+                      dir="auto"
+                    >
+                      My Great Massage
+                    </span>
+                  </div>
+                  <div
+                    class="mx_MessageActionBar"
+                  >
+                    <div
+                      class="mx_AccessibleButton"
+                      role="button"
+                      tabindex="0"
+                    >
+                      Remove
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </li>
+          </ul>
+        </ol>
+      </div>
+    </div>
+  </div>
+  <div
+    data-focus-guard="true"
+    style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
+    tabindex="0"
+  />
+</div>
+`;
+
+exports[`<MessageEditHistory /> should support events with  1`] = `
+<div>
+  <div
+    data-focus-guard="true"
+    style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
+    tabindex="0"
+  />
+  <div
+    aria-labelledby="mx_BaseDialog_title"
+    class="mx_MessageEditHistoryDialog mx_Dialog_fixedWidth"
+    data-focus-lock-disabled="false"
+    role="dialog"
+  >
+    <div
+      class="mx_Dialog_header mx_Dialog_headerWithCancel"
+    >
+      <h2
+        class="mx_Heading_h2 mx_Dialog_title"
+        id="mx_BaseDialog_title"
+      >
+        Message edits
+      </h2>
+      <div
+        aria-label="Close dialog"
+        class="mx_AccessibleButton mx_Dialog_cancelButton"
+        role="button"
+        tabindex="0"
+      />
+    </div>
+    <div
+      class="mx_AutoHideScrollbar mx_ScrollPanel mx_MessageEditHistoryDialog_scrollPanel"
+      tabindex="-1"
+    >
+      <div
+        class="mx_RoomView_messageListWrapper"
+      >
+        <ol
+          aria-live="polite"
+          class="mx_RoomView_MessageList"
+        >
+          <ul
+            class="mx_MessageEditHistoryDialog_edits"
+          >
+            <li>
+              <div
+                aria-label=",  NaN NaN"
+                class="mx_DateSeparator"
+                role="separator"
+                tabindex="-1"
+              >
+                <hr
+                  role="none"
+                />
+                <h2
+                  aria-hidden="true"
+                >
+                  ,  NaN NaN
+                </h2>
+                <hr
+                  role="none"
+                />
+              </div>
+            </li>
+            <li>
+              <div
+                class="mx_EventTile"
+              >
+                <div
+                  class="mx_EventTile_line"
+                >
+                  <span
+                    class="mx_MessageTimestamp"
+                  >
+                    NaN:NaN
+                  </span>
+                  <div
+                    class="mx_EventTile_content"
+                  >
+                    <span
+                      class="mx_EventTile_body markdown-body"
+                      dir="auto"
+                    >
+                      <span>
+                        My Great Massage
+                        <span
+                          class="mx_EditHistoryMessage_deletion"
+                        >
+                          ?
+                        </span>
+                      </span>
+                    </span>
+                  </div>
+                  <div
+                    class="mx_MessageActionBar"
+                  >
+                    <div
+                      class="mx_AccessibleButton"
+                      role="button"
+                      tabindex="0"
+                    >
+                      Remove
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </li>
+            <li>
+              <div
+                class="mx_EventTile"
+              >
+                <div
+                  class="mx_EventTile_line"
+                >
+                  <span
+                    class="mx_MessageTimestamp"
+                  >
+                    NaN:NaN
+                  </span>
+                  <div
+                    class="mx_EventTile_content"
+                  >
+                    <span
+                      class="mx_EventTile_body markdown-body"
+                      dir="auto"
+                    >
+                      <span>
+                        My Great M
+                        <span
+                          class="mx_EditHistoryMessage_deletion"
+                        >
+                          i
+                        </span>
+                        <span
+                          class="mx_EditHistoryMessage_insertion"
+                        >
+                          a
+                        </span>
+                        ssage
+                        <span
+                          class="mx_EditHistoryMessage_insertion"
+                        >
+                          ?
+                        </span>
+                      </span>
+                    </span>
+                  </div>
+                  <div
+                    class="mx_MessageActionBar"
+                  >
+                    <div
+                      class="mx_AccessibleButton"
+                      role="button"
+                      tabindex="0"
+                    >
+                      Remove
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </li>
+            <li>
+              <div
+                class="mx_EventTile"
+              >
+                <div
+                  class="mx_EventTile_line"
+                >
+                  <span
+                    class="mx_MessageTimestamp"
+                  >
+                    NaN:NaN
+                  </span>
+                  <div
+                    class="mx_EventTile_content"
+                  >
+                    <span
+                      class="mx_EventTile_body"
+                      dir="auto"
+                    >
+                      My Great Missage
+                    </span>
+                  </div>
+                  <div
+                    class="mx_MessageActionBar"
+                  >
+                    <div
+                      class="mx_AccessibleButton"
+                      role="button"
+                      tabindex="0"
+                    >
+                      Remove
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </li>
+          </ul>
+        </ol>
+      </div>
+    </div>
+  </div>
+  <div
+    data-focus-guard="true"
+    style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
+    tabindex="0"
+  />
+</div>
+`;
diff --git a/test/components/views/rooms/SearchResultTile-test.tsx b/test/components/views/rooms/SearchResultTile-test.tsx
index 59f6381f8c..10983e32e4 100644
--- a/test/components/views/rooms/SearchResultTile-test.tsx
+++ b/test/components/views/rooms/SearchResultTile-test.tsx
@@ -1,5 +1,5 @@
 /*
-Copyright 2022 The Matrix.org Foundation C.I.C.
+Copyright 2022 - 2023 The Matrix.org Foundation C.I.C.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@ limitations under the License.
 import * as React from "react";
 import { MatrixEvent } from "matrix-js-sdk/src/models/event";
 import { EventType } from "matrix-js-sdk/src/@types/event";
-import { render } from "@testing-library/react";
+import { render, type RenderResult } from "@testing-library/react";
 import { Room } from "matrix-js-sdk/src/models/room";
 
 import { stubClient } from "../../../test-utils";
@@ -26,6 +26,8 @@ import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
 
 const ROOM_ID = "!qPewotXpIctQySfjSy:localhost";
 
+type Props = React.ComponentPropsWithoutRef<typeof SearchResultTile>;
+
 describe("SearchResultTile", () => {
     beforeAll(() => {
         stubClient();
@@ -35,50 +37,72 @@ describe("SearchResultTile", () => {
         jest.spyOn(cli, "getRoom").mockReturnValue(room);
     });
 
+    function renderComponent(props: Partial<Props>): RenderResult {
+        return render(<SearchResultTile timeline={[]} ourEventsIndexes={[1]} {...props} />);
+    }
+
     it("Sets up appropriate callEventGrouper for m.call. events", () => {
-        const { container } = render(
-            <SearchResultTile
-                timeline={[
-                    new MatrixEvent({
-                        type: EventType.CallInvite,
-                        sender: "@user1:server",
-                        room_id: ROOM_ID,
-                        origin_server_ts: 1432735824652,
-                        content: { call_id: "call.1" },
-                        event_id: "$1:server",
-                    }),
-                    new MatrixEvent({
-                        content: {
-                            body: "This is an example text message",
-                            format: "org.matrix.custom.html",
-                            formatted_body: "<b>This is an example text message</b>",
-                            msgtype: "m.text",
-                        },
-                        event_id: "$144429830826TWwbB:localhost",
-                        origin_server_ts: 1432735824653,
-                        room_id: ROOM_ID,
-                        sender: "@example:example.org",
-                        type: "m.room.message",
-                        unsigned: {
-                            age: 1234,
-                        },
-                    }),
-                    new MatrixEvent({
-                        type: EventType.CallAnswer,
-                        sender: "@user2:server",
-                        room_id: ROOM_ID,
-                        origin_server_ts: 1432735824654,
-                        content: { call_id: "call.1" },
-                        event_id: "$2:server",
-                    }),
-                ]}
-                ourEventsIndexes={[1]}
-            />,
-        );
+        const { container } = renderComponent({
+            timeline: [
+                new MatrixEvent({
+                    type: EventType.CallInvite,
+                    sender: "@user1:server",
+                    room_id: ROOM_ID,
+                    origin_server_ts: 1432735824652,
+                    content: { call_id: "call.1" },
+                    event_id: "$1:server",
+                }),
+                new MatrixEvent({
+                    content: {
+                        body: "This is an example text message",
+                        format: "org.matrix.custom.html",
+                        formatted_body: "<b>This is an example text message</b>",
+                        msgtype: "m.text",
+                    },
+                    event_id: "$144429830826TWwbB:localhost",
+                    origin_server_ts: 1432735824653,
+                    room_id: ROOM_ID,
+                    sender: "@example:example.org",
+                    type: "m.room.message",
+                    unsigned: {
+                        age: 1234,
+                    },
+                }),
+                new MatrixEvent({
+                    type: EventType.CallAnswer,
+                    sender: "@user2:server",
+                    room_id: ROOM_ID,
+                    origin_server_ts: 1432735824654,
+                    content: { call_id: "call.1" },
+                    event_id: "$2:server",
+                }),
+            ],
+        });
 
         const tiles = container.querySelectorAll<HTMLElement>(".mx_EventTile");
         expect(tiles.length).toEqual(2);
-        expect(tiles[0].dataset.eventId).toBe("$1:server");
-        expect(tiles[1].dataset.eventId).toBe("$144429830826TWwbB:localhost");
+        expect(tiles[0]!.dataset.eventId).toBe("$1:server");
+        expect(tiles[1]!.dataset.eventId).toBe("$144429830826TWwbB:localhost");
+    });
+
+    it("supports events with missing timestamps", () => {
+        const { container } = renderComponent({
+            timeline: [...Array(20)].map(
+                (_, i) =>
+                    new MatrixEvent({
+                        type: EventType.RoomMessage,
+                        sender: "@user1:server",
+                        room_id: ROOM_ID,
+                        content: { body: `Message #${i}` },
+                        event_id: `$${i}:server`,
+                        origin_server_ts: undefined,
+                    }),
+            ),
+        });
+
+        const separators = container.querySelectorAll(".mx_DateSeparator");
+        // One separator is always rendered at the top, we don't want any
+        // between messages.
+        expect(separators.length).toBe(1);
     });
 });
diff --git a/test/test-utils/test-utils.ts b/test/test-utils/test-utils.ts
index 5938f89cfe..59b07a4eb2 100644
--- a/test/test-utils/test-utils.ts
+++ b/test/test-utils/test-utils.ts
@@ -208,6 +208,10 @@ export function createTestClient(): MatrixClient {
         setPassword: jest.fn().mockRejectedValue({}),
         groupCallEventHandler: { groupCalls: new Map<string, GroupCall>() },
         redactEvent: jest.fn(),
+
+        createMessagesRequest: jest.fn().mockResolvedValue({
+            chunk: [],
+        }),
     } as unknown as MatrixClient;
 
     client.reEmitter = new ReEmitter(client);
diff --git a/test/utils/exportUtils/HTMLExport-test.ts b/test/utils/exportUtils/HTMLExport-test.ts
index 062988f7f2..607dfbdbdc 100644
--- a/test/utils/exportUtils/HTMLExport-test.ts
+++ b/test/utils/exportUtils/HTMLExport-test.ts
@@ -1,5 +1,5 @@
 /*
-Copyright 2022 The Matrix.org Foundation C.I.C.
+Copyright 2022 - 2023 The Matrix.org Foundation C.I.C.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -14,26 +14,101 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-import { mocked } from "jest-mock";
+import { EventType, IRoomEvent, MatrixClient, MatrixEvent, MsgType, Room, RoomMember } from "matrix-js-sdk/src/matrix";
+import fetchMock from "fetch-mock-jest";
 
-import { createTestClient, mkStubRoom, REPEATABLE_DATE } from "../../test-utils";
+import { filterConsole, mkStubRoom, REPEATABLE_DATE, stubClient } from "../../test-utils";
 import { ExportType, IExportOptions } from "../../../src/utils/exportUtils/exportUtils";
 import SdkConfig from "../../../src/SdkConfig";
 import HTMLExporter from "../../../src/utils/exportUtils/HtmlExport";
+import DMRoomMap from "../../../src/utils/DMRoomMap";
+import { mediaFromMxc } from "../../../src/customisations/Media";
+
+jest.mock("jszip");
+
+const EVENT_MESSAGE: IRoomEvent = {
+    event_id: "$1",
+    type: EventType.RoomMessage,
+    sender: "@bob:example.com",
+    origin_server_ts: 0,
+    content: {
+        msgtype: "m.text",
+        body: "Message",
+        avatar_url: "mxc://example.org/avatar.bmp",
+    },
+};
+
+const EVENT_ATTACHMENT: IRoomEvent = {
+    event_id: "$2",
+    type: EventType.RoomMessage,
+    sender: "@alice:example.com",
+    origin_server_ts: 1,
+    content: {
+        msgtype: MsgType.File,
+        body: "hello.txt",
+        filename: "hello.txt",
+        url: "mxc://example.org/test-id",
+    },
+};
 
 describe("HTMLExport", () => {
+    let client: jest.Mocked<MatrixClient>;
+    let room: Room;
+
+    filterConsole(
+        "Starting export",
+        "events in", // Fetched # events in # seconds
+        "events so far",
+        "Export successful!",
+        "does not have an m.room.create event",
+        "Creating HTML",
+        "Generating a ZIP",
+        "Cleaning up",
+    );
+
     beforeEach(() => {
         jest.useFakeTimers();
         jest.setSystemTime(REPEATABLE_DATE);
+
+        client = stubClient() as jest.Mocked<MatrixClient>;
+        DMRoomMap.makeShared();
+
+        room = new Room("!myroom:example.org", client, "@me:example.org");
+        client.getRoom.mockReturnValue(room);
     });
 
-    afterEach(() => {
-        mocked(SdkConfig.get).mockRestore();
-    });
+    function mockMessages(...events: IRoomEvent[]): void {
+        client.createMessagesRequest.mockImplementation((_roomId, fromStr, limit = 30) => {
+            const from = fromStr === null ? 0 : parseInt(fromStr);
+            const chunk = events.slice(from, limit);
+            return Promise.resolve({
+                chunk,
+                from: from.toString(),
+                to: (from + limit).toString(),
+            });
+        });
+    }
+
+    /** Retrieve a map of files within the zip. */
+    function getFiles(exporter: HTMLExporter): { [filename: string]: Blob } {
+        //@ts-ignore private access
+        const files = exporter.files;
+        return files.reduce((d, f) => ({ ...d, [f.name]: f.blob }), {});
+    }
+
+    function getMessageFile(exporter: HTMLExporter): Blob {
+        const files = getFiles(exporter);
+        return files["messages.html"]!;
+    }
+
+    /** set a mock fetch response for an MXC */
+    function mockMxc(mxc: string, body: string) {
+        const media = mediaFromMxc(mxc, client);
+        fetchMock.get(media.srcHttp, body);
+    }
 
     it("should have an SDK-branded destination file name", () => {
         const roomName = "My / Test / Room: Welcome";
-        const client = createTestClient();
         const stubOptions: IExportOptions = {
             attachmentsIncluded: false,
             maxSize: 50000000,
@@ -43,10 +118,201 @@ describe("HTMLExport", () => {
 
         expect(exporter.destinationFileName).toMatchSnapshot();
 
-        jest.spyOn(SdkConfig, "get").mockImplementation(() => {
-            return { brand: "BrandedChat/WithSlashes/ForFun" };
-        });
+        SdkConfig.put({ brand: "BrandedChat/WithSlashes/ForFun" });
 
         expect(exporter.destinationFileName).toMatchSnapshot();
     });
+
+    it("should export", async () => {
+        const events = [...Array(50)].map<IRoomEvent>((_, i) => ({
+            event_id: `${i}`,
+            type: EventType.RoomMessage,
+            sender: `@user${i}:example.com`,
+            origin_server_ts: 5_000 + i * 1000,
+            content: {
+                msgtype: "m.text",
+                body: `Message #${i}`,
+            },
+        }));
+        mockMessages(...events);
+
+        const exporter = new HTMLExporter(
+            room,
+            ExportType.LastNMessages,
+            {
+                attachmentsIncluded: false,
+                maxSize: 1_024 * 1_024,
+                numberOfMessages: events.length,
+            },
+            () => {},
+        );
+
+        await exporter.export();
+
+        const file = getMessageFile(exporter);
+        expect(await file.text()).toMatchSnapshot();
+    });
+
+    it("should include the room's avatar", async () => {
+        mockMessages(EVENT_MESSAGE);
+
+        const mxc = "mxc://www.example.com/avatars/nice-room.jpeg";
+        const avatar = "011011000110111101101100";
+        jest.spyOn(room, "getMxcAvatarUrl").mockReturnValue(mxc);
+        mockMxc(mxc, avatar);
+
+        const exporter = new HTMLExporter(
+            room,
+            ExportType.Timeline,
+            {
+                attachmentsIncluded: false,
+                maxSize: 1_024 * 1_024,
+            },
+            () => {},
+        );
+
+        await exporter.export();
+
+        const files = getFiles(exporter);
+        expect(await files["room.png"]!.text()).toBe(avatar);
+    });
+
+    it("should include the creation event", async () => {
+        const creator = "@bob:example.com";
+        mockMessages(EVENT_MESSAGE);
+        room.currentState.setStateEvents([
+            new MatrixEvent({
+                type: EventType.RoomCreate,
+                event_id: "$00001",
+                room_id: room.roomId,
+                sender: creator,
+                origin_server_ts: 0,
+                content: {},
+                state_key: "",
+            }),
+        ]);
+
+        const exporter = new HTMLExporter(
+            room,
+            ExportType.Timeline,
+            {
+                attachmentsIncluded: false,
+                maxSize: 1_024 * 1_024,
+            },
+            () => {},
+        );
+
+        await exporter.export();
+
+        expect(await getMessageFile(exporter).text()).toContain(`${creator} created this room.`);
+    });
+
+    it("should include the topic", async () => {
+        const topic = ":^-) (-^:";
+        mockMessages(EVENT_MESSAGE);
+        room.currentState.setStateEvents([
+            new MatrixEvent({
+                type: EventType.RoomTopic,
+                event_id: "$00001",
+                room_id: room.roomId,
+                sender: "@alice:example.com",
+                origin_server_ts: 0,
+                content: { topic },
+                state_key: "",
+            }),
+        ]);
+
+        const exporter = new HTMLExporter(
+            room,
+            ExportType.Timeline,
+            {
+                attachmentsIncluded: false,
+                maxSize: 1_024 * 1_024,
+            },
+            () => {},
+        );
+
+        await exporter.export();
+
+        expect(await getMessageFile(exporter).text()).toContain(`Topic: ${topic}`);
+    });
+
+    it("should include avatars", async () => {
+        mockMessages(EVENT_MESSAGE);
+
+        jest.spyOn(RoomMember.prototype, "getMxcAvatarUrl").mockReturnValue("mxc://example.org/avatar.bmp");
+
+        const avatarContent = "this is a bitmap all the pixels are red :^-)";
+        mockMxc("mxc://example.org/avatar.bmp", avatarContent);
+
+        const exporter = new HTMLExporter(
+            room,
+            ExportType.Timeline,
+            {
+                attachmentsIncluded: false,
+                maxSize: 1_024 * 1_024,
+            },
+            () => {},
+        );
+
+        await exporter.export();
+
+        // Ensure that the avatar is present
+        const files = getFiles(exporter);
+        const file = files["users/@bob-example.com.png"];
+        expect(file).not.toBeUndefined();
+
+        // Ensure it has the expected content
+        expect(await file.text()).toBe(avatarContent);
+    });
+
+    it("should include attachments", async () => {
+        mockMessages(EVENT_MESSAGE, EVENT_ATTACHMENT);
+        const attachmentBody = "Lorem ipsum dolor sit amet";
+
+        mockMxc("mxc://example.org/test-id", attachmentBody);
+
+        const exporter = new HTMLExporter(
+            room,
+            ExportType.Timeline,
+            {
+                attachmentsIncluded: true,
+                maxSize: 1_024 * 1_024,
+            },
+            () => {},
+        );
+
+        await exporter.export();
+
+        // Ensure that the attachment is present
+        const files = getFiles(exporter);
+        const file = files["files/hello-1-1-1970 at 12-00-00 AM.txt"];
+        expect(file).not.toBeUndefined();
+
+        // Ensure that the attachment has the expected content
+        const text = await file.text();
+        expect(text).toBe(attachmentBody);
+    });
+
+    it("should omit attachments", async () => {
+        mockMessages(EVENT_MESSAGE, EVENT_ATTACHMENT);
+
+        const exporter = new HTMLExporter(
+            room,
+            ExportType.Timeline,
+            {
+                attachmentsIncluded: false,
+                maxSize: 1_024 * 1_024,
+            },
+            () => {},
+        );
+
+        await exporter.export();
+
+        // Ensure that the attachment is present
+        const files = getFiles(exporter);
+        for (const fileName of Object.keys(files)) {
+            expect(fileName).not.toMatch(/^files\/hello/);
+        }
+    });
 });
diff --git a/test/utils/exportUtils/__snapshots__/HTMLExport-test.ts.snap b/test/utils/exportUtils/__snapshots__/HTMLExport-test.ts.snap
index 5cfed9ef90..c47170d3ed 100644
--- a/test/utils/exportUtils/__snapshots__/HTMLExport-test.ts.snap
+++ b/test/utils/exportUtils/__snapshots__/HTMLExport-test.ts.snap
@@ -1,5 +1,91 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`HTMLExport should export 1`] = `
+"
+          <!DOCTYPE html>
+            <html lang="en">
+            <head>
+                <meta charset="UTF-8" />
+                <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+                <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+                <link href="css/style.css" rel="stylesheet" />
+                <script src="js/script.js"></script>
+                <title>Exported Data</title>
+            </head>
+            <body style="height: 100vh;">
+                <section
+                id="matrixchat"
+                style="height: 100%; overflow: auto"
+                class="notranslate"
+                >
+                <div class="mx_MatrixChat_wrapper" aria-hidden="false">
+                    <div class="mx_MatrixChat">
+                    <main class="mx_RoomView">
+                        <div class="mx_RoomHeader light-panel">
+                        <div class="mx_RoomHeader_wrapper" aria-owns="mx_RightPanel">
+                            <div class="mx_RoomHeader_avatar">
+                            <div class="mx_DecoratedRoomAvatar">
+                               <span class="mx_BaseAvatar" role="presentation"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size:20.8px;width:32px;line-height:32px">!</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="!myroom:example.org" style="width:32px;height:32px" aria-hidden="true" data-testid="avatar-img"/></span>
+                            </div>
+                            </div>
+                            <div class="mx_RoomHeader_name">
+                            <div
+                                dir="auto"
+                                class="mx_RoomHeader_nametext"
+                                title="!myroom:example.org"
+                            >
+                                !myroom:example.org
+                            </div>
+                            </div>
+                            <div class="mx_RoomHeader_topic" dir="auto">  </div>
+                        </div>
+                        </div>
+                        <div class="mx_MainSplit">
+                        <div class="mx_RoomView_body">
+                            <div
+                            class="mx_RoomView_timeline mx_RoomView_timeline_rr_enabled"
+                            >
+                            <div
+                                class="
+                                mx_AutoHideScrollbar
+                                mx_ScrollPanel
+                                mx_RoomView_messagePanel
+                                "
+                            >
+                                <div class="mx_RoomView_messageListWrapper">
+                                <ol
+                                    class="mx_RoomView_MessageList"
+                                    aria-live="polite"
+                                    role="list"
+                                >
+                                <div class="mx_NewRoomIntro">
+                                    <span class="mx_BaseAvatar" role="presentation"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size:20.8px;width:32px;line-height:32px">!</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="!myroom:example.org" style="width:32px;height:32px" aria-hidden="true" data-testid="avatar-img"/></span>
+                                    <h2> !myroom:example.org </h2>
+                                    <p>  created this room. <br/><br/> <p><span>This is the start of export of <b>!myroom:example.org</b>. Exported by <a href="https://matrix.to/#/@userId:matrix.org" target="_blank" rel="noopener noreferrer"><b>@userId:matrix.org</b></a> at 2022/11/17.</span></p> </p>
+                                    <br/>
+                                    <p>  </p>
+                                </div>
+                                <li><div class="mx_DateSeparator" role="separator" tabindex="-1" aria-label="Thu, Jan 1 1970"><hr role="none"/><h2 aria-hidden="true">Thu, Jan 1 1970</h2><hr role="none"/></div></li><div class="mx_Export_EventWrapper" id="49"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="49" data-layout="group" data-self="false" data-event-id="49" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color6 mx_DisambiguatedProfile_displayName" dir="auto">@user49:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user49:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/49" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:54" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #49</span></div></div></li></div><div class="mx_Export_EventWrapper" id="48"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="48" data-layout="group" data-self="false" data-event-id="48" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color7 mx_DisambiguatedProfile_displayName" dir="auto">@user48:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user48:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/48" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:53" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #48</span></div></div></li></div><div class="mx_Export_EventWrapper" id="47"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="47" data-layout="group" data-self="false" data-event-id="47" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color8 mx_DisambiguatedProfile_displayName" dir="auto">@user47:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user47:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/47" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:52" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #47</span></div></div></li></div><div class="mx_Export_EventWrapper" id="46"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="46" data-layout="group" data-self="false" data-event-id="46" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color1 mx_DisambiguatedProfile_displayName" dir="auto">@user46:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user46:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/46" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:51" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #46</span></div></div></li></div><div class="mx_Export_EventWrapper" id="45"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="45" data-layout="group" data-self="false" data-event-id="45" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color8 mx_DisambiguatedProfile_displayName" dir="auto">@user45:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user45:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/45" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:50" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #45</span></div></div></li></div><div class="mx_Export_EventWrapper" id="44"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="44" data-layout="group" data-self="false" data-event-id="44" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color7 mx_DisambiguatedProfile_displayName" dir="auto">@user44:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user44:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/44" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:49" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #44</span></div></div></li></div><div class="mx_Export_EventWrapper" id="43"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="43" data-layout="group" data-self="false" data-event-id="43" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color6 mx_DisambiguatedProfile_displayName" dir="auto">@user43:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user43:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/43" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:48" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #43</span></div></div></li></div><div class="mx_Export_EventWrapper" id="42"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="42" data-layout="group" data-self="false" data-event-id="42" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color5 mx_DisambiguatedProfile_displayName" dir="auto">@user42:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user42:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/42" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:47" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #42</span></div></div></li></div><div class="mx_Export_EventWrapper" id="41"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="41" data-layout="group" data-self="false" data-event-id="41" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color4 mx_DisambiguatedProfile_displayName" dir="auto">@user41:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user41:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/41" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:46" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #41</span></div></div></li></div><div class="mx_Export_EventWrapper" id="40"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="40" data-layout="group" data-self="false" data-event-id="40" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color3 mx_DisambiguatedProfile_displayName" dir="auto">@user40:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user40:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/40" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:45" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #40</span></div></div></li></div><div class="mx_Export_EventWrapper" id="39"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="39" data-layout="group" data-self="false" data-event-id="39" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color5 mx_DisambiguatedProfile_displayName" dir="auto">@user39:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user39:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/39" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:44" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #39</span></div></div></li></div><div class="mx_Export_EventWrapper" id="38"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="38" data-layout="group" data-self="false" data-event-id="38" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color6 mx_DisambiguatedProfile_displayName" dir="auto">@user38:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user38:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/38" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:43" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #38</span></div></div></li></div><div class="mx_Export_EventWrapper" id="37"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="37" data-layout="group" data-self="false" data-event-id="37" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color3 mx_DisambiguatedProfile_displayName" dir="auto">@user37:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user37:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/37" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:42" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #37</span></div></div></li></div><div class="mx_Export_EventWrapper" id="36"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="36" data-layout="group" data-self="false" data-event-id="36" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color2 mx_DisambiguatedProfile_displayName" dir="auto">@user36:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user36:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/36" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:41" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #36</span></div></div></li></div><div class="mx_Export_EventWrapper" id="35"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="35" data-layout="group" data-self="false" data-event-id="35" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color1 mx_DisambiguatedProfile_displayName" dir="auto">@user35:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user35:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/35" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:40" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #35</span></div></div></li></div><div class="mx_Export_EventWrapper" id="34"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="34" data-layout="group" data-self="false" data-event-id="34" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color8 mx_DisambiguatedProfile_displayName" dir="auto">@user34:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user34:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/34" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:39" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #34</span></div></div></li></div><div class="mx_Export_EventWrapper" id="33"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="33" data-layout="group" data-self="false" data-event-id="33" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color7 mx_DisambiguatedProfile_displayName" dir="auto">@user33:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user33:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/33" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:38" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #33</span></div></div></li></div><div class="mx_Export_EventWrapper" id="32"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="32" data-layout="group" data-self="false" data-event-id="32" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color6 mx_DisambiguatedProfile_displayName" dir="auto">@user32:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user32:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/32" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:37" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #32</span></div></div></li></div><div class="mx_Export_EventWrapper" id="31"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="31" data-layout="group" data-self="false" data-event-id="31" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color5 mx_DisambiguatedProfile_displayName" dir="auto">@user31:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user31:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/31" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:36" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #31</span></div></div></li></div><div class="mx_Export_EventWrapper" id="30"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="30" data-layout="group" data-self="false" data-event-id="30" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color6 mx_DisambiguatedProfile_displayName" dir="auto">@user30:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user30:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/30" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:35" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #30</span></div></div></li></div><div class="mx_Export_EventWrapper" id="29"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="29" data-layout="group" data-self="false" data-event-id="29" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color6 mx_DisambiguatedProfile_displayName" dir="auto">@user29:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user29:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/29" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:34" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #29</span></div></div></li></div><div class="mx_Export_EventWrapper" id="28"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="28" data-layout="group" data-self="false" data-event-id="28" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color5 mx_DisambiguatedProfile_displayName" dir="auto">@user28:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user28:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/28" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:33" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #28</span></div></div></li></div><div class="mx_Export_EventWrapper" id="27"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="27" data-layout="group" data-self="false" data-event-id="27" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color4 mx_DisambiguatedProfile_displayName" dir="auto">@user27:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user27:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/27" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:32" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #27</span></div></div></li></div><div class="mx_Export_EventWrapper" id="26"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="26" data-layout="group" data-self="false" data-event-id="26" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color3 mx_DisambiguatedProfile_displayName" dir="auto">@user26:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user26:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/26" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:31" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #26</span></div></div></li></div><div class="mx_Export_EventWrapper" id="25"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="25" data-layout="group" data-self="false" data-event-id="25" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color2 mx_DisambiguatedProfile_displayName" dir="auto">@user25:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user25:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/25" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:30" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #25</span></div></div></li></div><div class="mx_Export_EventWrapper" id="24"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="24" data-layout="group" data-self="false" data-event-id="24" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color1 mx_DisambiguatedProfile_displayName" dir="auto">@user24:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user24:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/24" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:29" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #24</span></div></div></li></div><div class="mx_Export_EventWrapper" id="23"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="23" data-layout="group" data-self="false" data-event-id="23" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color8 mx_DisambiguatedProfile_displayName" dir="auto">@user23:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user23:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/23" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:28" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #23</span></div></div></li></div><div class="mx_Export_EventWrapper" id="22"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="22" data-layout="group" data-self="false" data-event-id="22" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color7 mx_DisambiguatedProfile_displayName" dir="auto">@user22:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user22:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/22" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:27" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #22</span></div></div></li></div><div class="mx_Export_EventWrapper" id="21"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="21" data-layout="group" data-self="false" data-event-id="21" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color4 mx_DisambiguatedProfile_displayName" dir="auto">@user21:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user21:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/21" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:26" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #21</span></div></div></li></div><div class="mx_Export_EventWrapper" id="20"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="20" data-layout="group" data-self="false" data-event-id="20" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color5 mx_DisambiguatedProfile_displayName" dir="auto">@user20:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user20:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/20" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:25" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #20</span></div></div></li></div><div class="mx_Export_EventWrapper" id="19"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="19" data-layout="group" data-self="false" data-event-id="19" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color7 mx_DisambiguatedProfile_displayName" dir="auto">@user19:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user19:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/19" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:24" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #19</span></div></div></li></div><div class="mx_Export_EventWrapper" id="18"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="18" data-layout="group" data-self="false" data-event-id="18" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color6 mx_DisambiguatedProfile_displayName" dir="auto">@user18:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user18:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/18" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:23" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #18</span></div></div></li></div><div class="mx_Export_EventWrapper" id="17"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="17" data-layout="group" data-self="false" data-event-id="17" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color5 mx_DisambiguatedProfile_displayName" dir="auto">@user17:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user17:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/17" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:22" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #17</span></div></div></li></div><div class="mx_Export_EventWrapper" id="16"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="16" data-layout="group" data-self="false" data-event-id="16" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color4 mx_DisambiguatedProfile_displayName" dir="auto">@user16:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user16:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/16" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:21" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #16</span></div></div></li></div><div class="mx_Export_EventWrapper" id="15"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="15" data-layout="group" data-self="false" data-event-id="15" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color3 mx_DisambiguatedProfile_displayName" dir="auto">@user15:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user15:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/15" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:20" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #15</span></div></div></li></div><div class="mx_Export_EventWrapper" id="14"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="14" data-layout="group" data-self="false" data-event-id="14" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color2 mx_DisambiguatedProfile_displayName" dir="auto">@user14:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user14:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/14" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:19" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #14</span></div></div></li></div><div class="mx_Export_EventWrapper" id="13"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="13" data-layout="group" data-self="false" data-event-id="13" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color1 mx_DisambiguatedProfile_displayName" dir="auto">@user13:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user13:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/13" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:18" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #13</span></div></div></li></div><div class="mx_Export_EventWrapper" id="12"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="12" data-layout="group" data-self="false" data-event-id="12" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color2 mx_DisambiguatedProfile_displayName" dir="auto">@user12:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user12:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/12" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:17" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #12</span></div></div></li></div><div class="mx_Export_EventWrapper" id="11"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="11" data-layout="group" data-self="false" data-event-id="11" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color3 mx_DisambiguatedProfile_displayName" dir="auto">@user11:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user11:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/11" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:16" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #11</span></div></div></li></div><div class="mx_Export_EventWrapper" id="10"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="10" data-layout="group" data-self="false" data-event-id="10" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color4 mx_DisambiguatedProfile_displayName" dir="auto">@user10:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user10:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/10" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:15" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #10</span></div></div></li></div><div class="mx_Export_EventWrapper" id="9"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="9" data-layout="group" data-self="false" data-event-id="9" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color8 mx_DisambiguatedProfile_displayName" dir="auto">@user9:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user9:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/9" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:14" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #9</span></div></div></li></div><div class="mx_Export_EventWrapper" id="8"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="8" data-layout="group" data-self="false" data-event-id="8" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color1 mx_DisambiguatedProfile_displayName" dir="auto">@user8:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user8:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/8" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:13" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #8</span></div></div></li></div><div class="mx_Export_EventWrapper" id="7"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="7" data-layout="group" data-self="false" data-event-id="7" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color2 mx_DisambiguatedProfile_displayName" dir="auto">@user7:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user7:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/7" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:12" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #7</span></div></div></li></div><div class="mx_Export_EventWrapper" id="6"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="6" data-layout="group" data-self="false" data-event-id="6" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color3 mx_DisambiguatedProfile_displayName" dir="auto">@user6:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user6:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/6" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:11" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #6</span></div></div></li></div><div class="mx_Export_EventWrapper" id="5"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="5" data-layout="group" data-self="false" data-event-id="5" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color6 mx_DisambiguatedProfile_displayName" dir="auto">@user5:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user5:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/5" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:10" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #5</span></div></div></li></div><div class="mx_Export_EventWrapper" id="4"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="4" data-layout="group" data-self="false" data-event-id="4" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color5 mx_DisambiguatedProfile_displayName" dir="auto">@user4:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user4:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/4" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:09" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #4</span></div></div></li></div><div class="mx_Export_EventWrapper" id="3"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="3" data-layout="group" data-self="false" data-event-id="3" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color4 mx_DisambiguatedProfile_displayName" dir="auto">@user3:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user3:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/3" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:08" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #3</span></div></div></li></div><div class="mx_Export_EventWrapper" id="2"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="2" data-layout="group" data-self="false" data-event-id="2" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color3 mx_DisambiguatedProfile_displayName" dir="auto">@user2:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user2:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/2" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:07" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #2</span></div></div></li></div><div class="mx_Export_EventWrapper" id="1"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="1" data-layout="group" data-self="false" data-event-id="1" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color2 mx_DisambiguatedProfile_displayName" dir="auto">@user1:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user1:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/1" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:06" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #1</span></div></div></li></div><div class="mx_Export_EventWrapper" id="0"><li class="mx_EventTile" tabindex="-1" aria-live="off" aria-atomic="true" data-scroll-tokens="0" data-layout="group" data-self="false" data-event-id="0" data-has-reply="false"><div class="mx_DisambiguatedProfile"><span class="mx_Username_color1 mx_DisambiguatedProfile_displayName" dir="auto">@user0:example.com</span></div><div class="mx_EventTile_avatar"><span aria-label="Avatar" aria-live="off" role="button" tabindex="0" class="mx_AccessibleButton mx_BaseAvatar"><span class="mx_BaseAvatar_initial" aria-hidden="true" style="font-size: 19.5px; width: 30px; line-height: 30px;">U</span><img class="mx_BaseAvatar_image" src="data:image/png;base64,00" alt="" title="@user0:example.com" style="width: 30px; height: 30px;" aria-hidden="true" data-testid="avatar-img"></span></div><div class="mx_EventTile_line"><a href="https://matrix.to/#/!myroom:example.org/0" aria-label="00:00"><span class="mx_MessageTimestamp" title="Thu, Jan 1 1970 00:00:05" aria-hidden="true">00:00</span></a><div class="mx_MTextBody mx_EventTile_content"><span class="mx_EventTile_body" dir="auto">Message #0</span></div></div></li></div>
+                                </ol>
+                                </div>
+                            </div>
+                            </div>
+                            <div class="mx_RoomView_statusArea">
+                            <div class="mx_RoomView_statusAreaBox">
+                                <div class="mx_RoomView_statusAreaBox_line"></div>
+                            </div>
+                            </div>
+                        </div>
+                        </div>
+                    </main>
+                    </div>
+                </div>
+                </section>
+                <div id="snackbar"/>
+            </body>
+        </html>"
+`;
+
 exports[`HTMLExport should have an SDK-branded destination file name 1`] = `"Element - My  Test  Room Welcome - Chat Export - 2022-11-17T16-58-32.517Z.zip"`;
 
 exports[`HTMLExport should have an SDK-branded destination file name 2`] = `"BrandedChatWithSlashesForFun - My  Test  Room Welcome - Chat Export - 2022-11-17T16-58-32.517Z.zip"`;
diff --git a/test/utils/exportUtils/exportCSS-test.ts b/test/utils/exportUtils/exportCSS-test.ts
new file mode 100644
index 0000000000..d2f3f14fbb
--- /dev/null
+++ b/test/utils/exportUtils/exportCSS-test.ts
@@ -0,0 +1,26 @@
+/*
+Copyright 2023 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import getExportCSS from "../../../src/utils/exportUtils/exportCSS";
+
+describe("exportCSS", () => {
+    describe("getExportCSS", () => {
+        it("supports documents missing stylesheets", async () => {
+            const css = await getExportCSS(new Set());
+            expect(css).not.toContain("color-scheme: light");
+        });
+    });
+});