diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml index cf3e1e2c88..592268d885 100644 --- a/.github/workflows/pull_request.yaml +++ b/.github/workflows/pull_request.yaml @@ -6,7 +6,5 @@ concurrency: ${{ github.workflow }}-${{ github.event.pull_request.head.ref }} jobs: action: uses: matrix-org/matrix-js-sdk/.github/workflows/pull_request.yaml@develop - with: - labels: "T-Defect,T-Enhancement,T-Task" secrets: ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} diff --git a/package.json b/package.json index f1ccc355a7..c05a52f285 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,7 @@ "react-transition-group": "^4.4.1", "rfc4648": "^1.4.0", "sanitize-filename": "^1.6.3", - "sanitize-html": "^2.3.2", + "sanitize-html": "2.8.0", "tar-js": "^0.3.0", "ua-parser-js": "^1.0.2", "url": "^0.11.0", @@ -167,9 +167,8 @@ "@types/react": "17.0.49", "@types/react-beautiful-dnd": "^13.0.0", "@types/react-dom": "17.0.17", - "@types/react-test-renderer": "^17.0.1", "@types/react-transition-group": "^4.4.0", - "@types/sanitize-html": "^2.3.1", + "@types/sanitize-html": "2.8.0", "@types/tar-js": "^0.3.2", "@types/ua-parser-js": "^0.7.36", "@types/zxcvbn": "^4.4.0", @@ -212,7 +211,6 @@ "postcss-scss": "^4.0.4", "prettier": "2.8.0", "raw-loader": "^4.0.2", - "react-test-renderer": "^17.0.2", "rimraf": "^3.0.2", "stylelint": "^14.9.1", "stylelint-config-prettier": "^9.0.4", diff --git a/res/css/views/elements/_CopyableText.pcss b/res/css/views/elements/_CopyableText.pcss index e6b3b1ebf9..8e1d3f3cfd 100644 --- a/res/css/views/elements/_CopyableText.pcss +++ b/res/css/views/elements/_CopyableText.pcss @@ -38,9 +38,12 @@ limitations under the License. cursor: pointer; margin-left: 20px; display: block; + /* If the copy button is used within a scrollable div, make it stick to the right while scrolling */ + position: sticky; + right: 0; /* center to first line */ - position: relative; top: 0.15em; + background-color: $background; &::before { content: ""; diff --git a/res/css/views/settings/tabs/user/_HelpUserSettingsTab.pcss b/res/css/views/settings/tabs/user/_HelpUserSettingsTab.pcss index c03de9f36c..5a61bcd2da 100644 --- a/res/css/views/settings/tabs/user/_HelpUserSettingsTab.pcss +++ b/res/css/views/settings/tabs/user/_HelpUserSettingsTab.pcss @@ -28,4 +28,9 @@ limitations under the License. margin-bottom: $spacing-16; } } + + /* prevent the access token from overflowing the text box */ + div .mx_CopyableText { + overflow: scroll; + } } diff --git a/src/components/views/beacon/BeaconViewDialog.tsx b/src/components/views/beacon/BeaconViewDialog.tsx index a2fa704e8e..6c91cd1f39 100644 --- a/src/components/views/beacon/BeaconViewDialog.tsx +++ b/src/components/views/beacon/BeaconViewDialog.tsx @@ -158,12 +158,12 @@ const BeaconViewDialog: React.FC<IProps> = ({ initialFocusedBeacon, roomId, matr )} {mapDisplayError && <MapError error={mapDisplayError.message as LocationShareError} isMinimised />} {!centerGeoUri && !mapDisplayError && ( - <MapFallback data-test-id="beacon-view-dialog-map-fallback" className="mx_BeaconViewDialog_map"> + <MapFallback data-testid="beacon-view-dialog-map-fallback" className="mx_BeaconViewDialog_map"> <span className="mx_BeaconViewDialog_mapFallbackMessage">{_t("No live locations")}</span> <AccessibleButton kind="primary" onClick={onFinished} - data-test-id="beacon-view-dialog-fallback-close" + data-testid="beacon-view-dialog-fallback-close" > {_t("Close")} </AccessibleButton> @@ -179,7 +179,7 @@ const BeaconViewDialog: React.FC<IProps> = ({ initialFocusedBeacon, roomId, matr <AccessibleButton kind="primary" onClick={() => setSidebarOpen(true)} - data-test-id="beacon-view-dialog-open-sidebar" + data-testid="beacon-view-dialog-open-sidebar" className="mx_BeaconViewDialog_viewListButton" > <LiveLocationIcon height={12} /> diff --git a/src/components/views/messages/DecryptionFailureBody.tsx b/src/components/views/messages/DecryptionFailureBody.tsx index 4c1d6e08b8..0120b960a8 100644 --- a/src/components/views/messages/DecryptionFailureBody.tsx +++ b/src/components/views/messages/DecryptionFailureBody.tsx @@ -14,14 +14,19 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { ReactNode } from "react"; +import React from "react"; +import { MatrixEvent } from "matrix-js-sdk/src/matrix"; import { _t } from "../../../languageHandler"; import { IBodyProps } from "./IBodyProps"; -// A placeholder element for messages that could not be decrypted -export default class DecryptionFailureBody extends React.Component<Partial<IBodyProps>> { - public render(): ReactNode { - return <div className="mx_DecryptionFailureBody mx_EventTile_content">{_t("Unable to decrypt message")}</div>; - } +function getErrorMessage(mxEvent?: MatrixEvent): string { + return mxEvent?.isEncryptedDisabledForUnverifiedDevices + ? _t("The sender has blocked you from receiving this message") + : _t("Unable to decrypt message"); +} + +// A placeholder element for messages that could not be decrypted +export function DecryptionFailureBody({ mxEvent }: Partial<IBodyProps>): JSX.Element { + return <div className="mx_DecryptionFailureBody mx_EventTile_content">{getErrorMessage(mxEvent)}</div>; } diff --git a/src/components/views/messages/MPollBody.tsx b/src/components/views/messages/MPollBody.tsx index 6a92185e61..62c53af518 100644 --- a/src/components/views/messages/MPollBody.tsx +++ b/src/components/views/messages/MPollBody.tsx @@ -182,12 +182,14 @@ export default class MPollBody extends React.Component<IBodyProps, IState> { private addListeners(): void { this.state.poll?.on(PollEvent.Responses, this.onResponsesChange); this.state.poll?.on(PollEvent.End, this.onRelationsChange); + this.state.poll?.on(PollEvent.UndecryptableRelations, this.render.bind(this)); } private removeListeners(): void { if (this.state.poll) { this.state.poll.off(PollEvent.Responses, this.onResponsesChange); this.state.poll.off(PollEvent.End, this.onRelationsChange); + this.state.poll.off(PollEvent.UndecryptableRelations, this.render.bind(this)); } } @@ -297,7 +299,9 @@ export default class MPollBody extends React.Component<IBodyProps, IState> { const showResults = poll.isEnded || (disclosed && myVote !== undefined); let totalText: string; - if (poll.isEnded) { + if (showResults && poll.undecryptableRelationsCount) { + totalText = _t("Due to decryption errors, some votes may not be counted"); + } else if (poll.isEnded) { totalText = _t("Final result based on %(count)s votes", { count: totalVotes }); } else if (!disclosed) { totalText = _t("Results will be visible when the poll is ended"); diff --git a/src/components/views/messages/MPollEndBody.tsx b/src/components/views/messages/MPollEndBody.tsx index 2ae6a73e86..bf8272a58f 100644 --- a/src/components/views/messages/MPollEndBody.tsx +++ b/src/components/views/messages/MPollEndBody.tsx @@ -21,7 +21,9 @@ import { logger } from "matrix-js-sdk/src/logger"; import { Icon as PollIcon } from "../../../../res/img/element-icons/room/composer/poll.svg"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; +import { _t } from "../../../languageHandler"; import { textForEvent } from "../../../TextForEvent"; +import { Caption } from "../typography/Caption"; import { IBodyProps } from "./IBodyProps"; import MPollBody from "./MPollBody"; @@ -105,5 +107,10 @@ export const MPollEndBody = React.forwardRef<any, IBodyProps>(({ mxEvent, ...pro ); } - return <MPollBody mxEvent={pollStartEvent} {...props} />; + return ( + <div> + <Caption>{_t("Ended a poll")}</Caption> + <MPollBody mxEvent={pollStartEvent} {...props} /> + </div> + ); }); diff --git a/src/components/views/messages/MessageEvent.tsx b/src/components/views/messages/MessageEvent.tsx index b7a8f60831..a32747cd2e 100644 --- a/src/components/views/messages/MessageEvent.tsx +++ b/src/components/views/messages/MessageEvent.tsx @@ -41,7 +41,7 @@ import { MPollEndBody } from "./MPollEndBody"; import MLocationBody from "./MLocationBody"; import MjolnirBody from "./MjolnirBody"; import MBeaconBody from "./MBeaconBody"; -import DecryptionFailureBody from "./DecryptionFailureBody"; +import { DecryptionFailureBody } from "./DecryptionFailureBody"; import { GetRelationsForEvent, IEventTileOps } from "../rooms/EventTile"; import { VoiceBroadcastBody, VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "../../../voice-broadcast"; diff --git a/src/components/views/room_settings/AliasSettings.tsx b/src/components/views/room_settings/AliasSettings.tsx index 3dd13ba40f..8c219023bd 100644 --- a/src/components/views/room_settings/AliasSettings.tsx +++ b/src/components/views/room_settings/AliasSettings.tsx @@ -399,7 +399,7 @@ export default class AliasSettings extends React.Component<IProps, IState> { return ( <div className="mx_AliasSettings"> <SettingsFieldset - data-test-id="published-address-fieldset" + data-testid="published-address-fieldset" legend={_t("Published Addresses")} description={ <> @@ -450,7 +450,7 @@ export default class AliasSettings extends React.Component<IProps, IState> { /> </SettingsFieldset> <SettingsFieldset - data-test-id="local-address-fieldset" + data-testid="local-address-fieldset" legend={_t("Local Addresses")} description={ isSpaceRoom diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index d1198e60d3..fa32df68a6 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -35,7 +35,7 @@ import { Layout } from "../../../settings/enums/Layout"; import { formatTime } from "../../../DateUtils"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; -import DecryptionFailureBody from "../messages/DecryptionFailureBody"; +import { DecryptionFailureBody } from "../messages/DecryptionFailureBody"; import { E2EState } from "./E2EIcon"; import RoomAvatar from "../avatars/RoomAvatar"; import MessageContextMenu from "../context_menus/MessageContextMenu"; @@ -1270,7 +1270,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState> {this.props.mxEvent.isRedacted() ? ( <RedactedBody mxEvent={this.props.mxEvent} /> ) : this.props.mxEvent.isDecryptionFailure() ? ( - <DecryptionFailureBody /> + <DecryptionFailureBody mxEvent={this.props.mxEvent} /> ) : ( MessagePreviewStore.instance.generatePreviewForEvent(this.props.mxEvent) )} diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx index 2b0094dd3d..83c8c9fc11 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx @@ -89,6 +89,8 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps, "useOnlyCurrentProfiles", ]; + private static ROOM_DIRECTORY_SETTINGS = ["SpotlightSearch.showNsfwPublicRooms"]; + private static GENERAL_SETTINGS = [ "promptBeforeInviteUnknownUsers", // Start automatically after startup (electron-only) @@ -234,6 +236,11 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps, {this.renderGroup(PreferencesUserSettingsTab.TIMELINE_SETTINGS)} </div> + <div className="mx_SettingsTab_section"> + <span className="mx_SettingsTab_subheading">{_t("Room directory")}</span> + {this.renderGroup(PreferencesUserSettingsTab.ROOM_DIRECTORY_SETTINGS)} + </div> + <div className="mx_SettingsTab_section"> <span className="mx_SettingsTab_subheading">{_t("General")}</span> {this.renderGroup(PreferencesUserSettingsTab.GENERAL_SETTINGS)} diff --git a/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx b/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx index 35f310dbdd..7993a2af64 100644 --- a/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx +++ b/src/components/views/spaces/SpaceSettingsVisibilityTab.tsx @@ -93,7 +93,7 @@ const SpaceSettingsVisibilityTab: React.FC<IProps> = ({ matrixClient: cli, space advancedSection = ( <div> <AccessibleButton - data-test-id="toggle-guest-access-btn" + data-testid="toggle-guest-access-btn" onClick={toggleAdvancedSection} kind="link" className="mx_SettingsTab_showAdvanced" @@ -141,13 +141,13 @@ const SpaceSettingsVisibilityTab: React.FC<IProps> = ({ matrixClient: cli, space <div className="mx_SettingsTab_heading">{_t("Visibility")}</div> {error && ( - <div data-test-id="space-settings-error" className="mx_SpaceRoomView_errorText"> + <div data-testid="space-settings-error" className="mx_SpaceRoomView_errorText"> {error} </div> )} <SettingsFieldset - data-test-id="access-fieldset" + data-testid="access-fieldset" legend={_t("Access")} description={_t("Decide who can view and join %(spaceName)s.", { spaceName: space.name })} > diff --git a/src/hooks/usePublicRoomDirectory.ts b/src/hooks/usePublicRoomDirectory.ts index e1bf806c0b..d4862b6e26 100644 --- a/src/hooks/usePublicRoomDirectory.ts +++ b/src/hooks/usePublicRoomDirectory.ts @@ -25,6 +25,7 @@ import SdkConfig from "../SdkConfig"; import SettingsStore from "../settings/SettingsStore"; import { Protocols } from "../utils/DirectoryUtils"; import { useLatestResult } from "./useLatestResult"; +import { useSettingValue } from "./useSettings"; export const ALL_ROOMS = "ALL_ROOMS"; const LAST_SERVER_KEY = "mx_last_room_directory_server"; @@ -38,6 +39,10 @@ export interface IPublicRoomsOpts { let thirdParty: Protocols; +const NSFW_KEYWORD = "nsfw"; +const cheapNsfwFilter = (room: IPublicRoomsChunkRoom): boolean => + !room.name?.toLocaleLowerCase().includes(NSFW_KEYWORD) && !room.topic?.toLocaleLowerCase().includes(NSFW_KEYWORD); + export const usePublicRoomDirectory = (): { ready: boolean; loading: boolean; @@ -58,6 +63,8 @@ export const usePublicRoomDirectory = (): { const [updateQuery, updateResult] = useLatestResult<IRoomDirectoryOptions, IPublicRoomsChunkRoom[]>(setPublicRooms); + const showNsfwPublicRooms = useSettingValue<boolean>("SpotlightSearch.showNsfwPublicRooms"); + async function initProtocols(): Promise<void> { if (!MatrixClientPeg.get()) { // We may not have a client yet when invoked from welcome page @@ -108,7 +115,7 @@ export const usePublicRoomDirectory = (): { try { setLoading(true); const { chunk } = await MatrixClientPeg.get().publicRooms(opts); - updateResult(opts, chunk); + updateResult(opts, showNsfwPublicRooms ? chunk : chunk.filter(cheapNsfwFilter)); return true; } catch (e) { console.error("Could not fetch public rooms for params", opts, e); @@ -118,7 +125,7 @@ export const usePublicRoomDirectory = (): { setLoading(false); } }, - [config, updateQuery, updateResult], + [config, updateQuery, updateResult, showNsfwPublicRooms], ); useEffect(() => { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 632f50d4c1..3c3ff6c20a 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1018,6 +1018,7 @@ "Automatic gain control": "Automatic gain control", "Echo cancellation": "Echo cancellation", "Noise suppression": "Noise suppression", + "Show NSFW content": "Show NSFW content", "Send analytics data": "Send analytics data", "Record the client name, version, and url to recognise sessions more easily in session manager": "Record the client name, version, and url to recognise sessions more easily in session manager", "Never send encrypted messages to unverified sessions from this session": "Never send encrypted messages to unverified sessions from this session", @@ -1619,6 +1620,7 @@ "Code blocks": "Code blocks", "Images, GIFs and videos": "Images, GIFs and videos", "Timeline": "Timeline", + "Room directory": "Room directory", "Enable hardware acceleration (restart %(appName)s to take effect)": "Enable hardware acceleration (restart %(appName)s to take effect)", "Autocomplete delay (ms)": "Autocomplete delay (ms)", "Read Marker lifetime (ms)": "Read Marker lifetime (ms)", @@ -2328,6 +2330,7 @@ "Last month": "Last month", "The beginning of the room": "The beginning of the room", "Jump to date": "Jump to date", + "The sender has blocked you from receiving this message": "The sender has blocked you from receiving this message", "%(displayName)s (%(matrixId)s)": "%(displayName)s (%(matrixId)s)", "Downloading": "Downloading", "Decrypting": "Decrypting", @@ -2402,6 +2405,7 @@ "Sorry, you can't edit a poll after votes have been cast.": "Sorry, you can't edit a poll after votes have been cast.", "Vote not registered": "Vote not registered", "Sorry, your vote was not registered. Please try again.": "Sorry, your vote was not registered. Please try again.", + "Due to decryption errors, some votes may not be counted": "Due to decryption errors, some votes may not be counted", "Final result based on %(count)s votes|other": "Final result based on %(count)s votes", "Final result based on %(count)s votes|one": "Final result based on %(count)s vote", "Results will be visible when the poll is ended": "Results will be visible when the poll is ended", @@ -2411,6 +2415,7 @@ "Based on %(count)s votes|other": "Based on %(count)s votes", "Based on %(count)s votes|one": "Based on %(count)s vote", "edited": "edited", + "Ended a poll": "Ended a poll", "Error decrypting video": "Error decrypting video", "Error processing voice message": "Error processing voice message", "Add reaction": "Add reaction", diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index bc0663e640..9e9b920a8d 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -743,6 +743,11 @@ export const SETTINGS: { [setting: string]: ISetting } = { supportedLevels: [SettingLevel.ACCOUNT], default: [], // list of room IDs, most recent first }, + "SpotlightSearch.showNsfwPublicRooms": { + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + displayName: _td("Show NSFW content"), + default: false, + }, "room_directory_servers": { supportedLevels: [SettingLevel.ACCOUNT], default: [], diff --git a/src/utils/exportUtils/HtmlExport.tsx b/src/utils/exportUtils/HtmlExport.tsx index 2896fdb1c2..3ec04a460b 100644 --- a/src/utils/exportUtils/HtmlExport.tsx +++ b/src/utils/exportUtils/HtmlExport.tsx @@ -89,7 +89,7 @@ export default class HTMLExporter extends Exporter { return renderToStaticMarkup(avatar); } - protected async wrapHTML(content: string): Promise<string> { + protected async wrapHTML(content: string, currentPage: number, nbPages: number): Promise<string> { const roomAvatar = await this.getRoomAvatar(); const exportDate = formatFullDateNoDayNoTime(new Date()); const creator = this.room.currentState.getStateEvents(EventType.RoomCreate, "")?.getSender(); @@ -128,6 +128,29 @@ export default class HTMLExporter extends Exporter { ); const topicText = topic ? _t("Topic: %(topic)s", { topic }) : ""; + const previousMessagesLink = renderToStaticMarkup( + currentPage !== 0 ? ( + <div style={{ textAlign: "center" }}> + <a href={`./messages${currentPage === 1 ? "" : currentPage}.html`} style={{ fontWeight: "bold" }}> + Previous group of messages + </a> + </div> + ) : ( + <></> + ), + ); + + const nextMessagesLink = renderToStaticMarkup( + currentPage < nbPages - 1 ? ( + <div style={{ textAlign: "center", margin: "10px" }}> + <a href={"./messages" + (currentPage + 2) + ".html"} style={{ fontWeight: "bold" }}> + Next group of messages + </a> + </div> + ) : ( + <></> + ), + ); return ` <!DOCTYPE html> @@ -168,6 +191,7 @@ export default class HTMLExporter extends Exporter { <div class="mx_RoomHeader_topic" dir="auto"> ${topic} </div> </div> </div> + ${previousMessagesLink} <div class="mx_MainSplit"> <div class="mx_RoomView_body"> <div @@ -186,13 +210,17 @@ export default class HTMLExporter extends Exporter { aria-live="polite" role="list" > - <div class="mx_NewRoomIntro"> - ${roomAvatar} - <h2> ${this.room.name} </h2> - <p> ${createdText} <br/><br/> ${exportedText} </p> - <br/> - <p> ${topicText} </p> - </div> + ${ + currentPage == 0 + ? `<div class="mx_NewRoomIntro"> + ${roomAvatar} + <h2> ${this.room.name} </h2> + <p> ${createdText} <br/><br/> ${exportedText} </p> + <br/> + <p> ${topicText} </p> + </div>` + : "" + } ${content} </ol> </div> @@ -205,6 +233,7 @@ export default class HTMLExporter extends Exporter { </div> </div> </div> + ${nextMessagesLink} </main> </div> </div> @@ -381,7 +410,12 @@ export default class HTMLExporter extends Exporter { return eventTile; } - protected async createHTML(events: MatrixEvent[], start: number): Promise<string> { + protected async createHTML( + events: MatrixEvent[], + start: number, + currentPage: number, + nbPages: number, + ): Promise<string> { let content = ""; let prevEvent: MatrixEvent | null = null; for (let i = start; i < Math.min(start + 1000, events.length); i++) { @@ -405,7 +439,7 @@ export default class HTMLExporter extends Exporter { content += body; prevEvent = event; } - return this.wrapHTML(content); + return this.wrapHTML(content, currentPage, nbPages); } public async export(): Promise<void> { @@ -428,7 +462,7 @@ export default class HTMLExporter extends Exporter { const usedClasses = new Set<string>(); for (let page = 0; page < res.length / 1000; page++) { - const html = await this.createHTML(res, page * 1000); + const html = await this.createHTML(res, page * 1000, page, res.length / 1000); const document = new DOMParser().parseFromString(html, "text/html"); document.querySelectorAll("*").forEach((element) => { element.classList.forEach((c) => usedClasses.add(c)); diff --git a/test/TextForEvent-test.ts b/test/TextForEvent-test.ts index f129ceb01a..97a093efee 100644 --- a/test/TextForEvent-test.ts +++ b/test/TextForEvent-test.ts @@ -15,7 +15,7 @@ limitations under the License. */ import { EventType, MatrixClient, MatrixEvent, Room, RoomMember } from "matrix-js-sdk/src/matrix"; -import TestRenderer from "react-test-renderer"; +import { render } from "@testing-library/react"; import { ReactElement } from "react"; import { Mocked, mocked } from "jest-mock"; @@ -46,43 +46,6 @@ function mockPinnedEvent(pinnedMessageIds?: string[], prevPinnedMessageIds?: str }); } -// Helper function that renders a component to a plain text string. -// Once snapshots are introduced in tests, this function will no longer be necessary, -// and should be replaced with snapshots. -function renderComponent(component: TestRenderer.ReactTestRenderer): string { - const serializeObject = ( - object: - | TestRenderer.ReactTestRendererJSON - | TestRenderer.ReactTestRendererJSON[] - | TestRenderer.ReactTestRendererNode - | TestRenderer.ReactTestRendererNode[], - ): string => { - if (typeof object === "string") { - return object === " " ? "" : object; - } - - if (Array.isArray(object) && object.length === 1 && typeof object[0] === "string") { - return object[0]; - } - - if (!Array.isArray(object) && object["type"] !== undefined && typeof object["children"] !== undefined) { - return serializeObject(object.children!); - } - - if (!Array.isArray(object)) { - return ""; - } - - return object - .map((child) => { - return serializeObject(child); - }) - .join(""); - }; - - return serializeObject(component.toJSON()!); -} - describe("TextForEvent", () => { describe("getSenderName()", () => { it("Prefers sender.name", () => { @@ -105,71 +68,71 @@ describe("TextForEvent", () => { it("mentions message when a single message was pinned, with no previously pinned messages", () => { const event = mockPinnedEvent(["message-1"]); const plainText = textForEvent(event); - const component = TestRenderer.create(textForEvent(event, true) as ReactElement); + const component = render(textForEvent(event, true) as ReactElement); const expectedText = "@foo:example.com pinned a message to this room. See all pinned messages."; expect(plainText).toBe(expectedText); - expect(renderComponent(component)).toBe(expectedText); + expect(component.container).toHaveTextContent(expectedText); }); it("mentions message when a single message was pinned, with multiple previously pinned messages", () => { const event = mockPinnedEvent(["message-1", "message-2", "message-3"], ["message-1", "message-2"]); const plainText = textForEvent(event); - const component = TestRenderer.create(textForEvent(event, true) as ReactElement); + const component = render(textForEvent(event, true) as ReactElement); const expectedText = "@foo:example.com pinned a message to this room. See all pinned messages."; expect(plainText).toBe(expectedText); - expect(renderComponent(component)).toBe(expectedText); + expect(component.container).toHaveTextContent(expectedText); }); it("mentions message when a single message was unpinned, with a single message previously pinned", () => { const event = mockPinnedEvent([], ["message-1"]); const plainText = textForEvent(event); - const component = TestRenderer.create(textForEvent(event, true) as ReactElement); + const component = render(textForEvent(event, true) as ReactElement); const expectedText = "@foo:example.com unpinned a message from this room. See all pinned messages."; expect(plainText).toBe(expectedText); - expect(renderComponent(component)).toBe(expectedText); + expect(component.container).toHaveTextContent(expectedText); }); it("mentions message when a single message was unpinned, with multiple previously pinned messages", () => { const event = mockPinnedEvent(["message-2"], ["message-1", "message-2"]); const plainText = textForEvent(event); - const component = TestRenderer.create(textForEvent(event, true) as ReactElement); + const component = render(textForEvent(event, true) as ReactElement); const expectedText = "@foo:example.com unpinned a message from this room. See all pinned messages."; expect(plainText).toBe(expectedText); - expect(renderComponent(component)).toBe(expectedText); + expect(component.container).toHaveTextContent(expectedText); }); it("shows generic text when multiple messages were pinned", () => { const event = mockPinnedEvent(["message-1", "message-2", "message-3"], ["message-1"]); const plainText = textForEvent(event); - const component = TestRenderer.create(textForEvent(event, true) as ReactElement); + const component = render(textForEvent(event, true) as ReactElement); const expectedText = "@foo:example.com changed the pinned messages for the room."; expect(plainText).toBe(expectedText); - expect(renderComponent(component)).toBe(expectedText); + expect(component.container).toHaveTextContent(expectedText); }); it("shows generic text when multiple messages were unpinned", () => { const event = mockPinnedEvent(["message-3"], ["message-1", "message-2", "message-3"]); const plainText = textForEvent(event); - const component = TestRenderer.create(textForEvent(event, true) as ReactElement); + const component = render(textForEvent(event, true) as ReactElement); const expectedText = "@foo:example.com changed the pinned messages for the room."; expect(plainText).toBe(expectedText); - expect(renderComponent(component)).toBe(expectedText); + expect(component.container).toHaveTextContent(expectedText); }); it("shows generic text when one message was pinned, and another unpinned", () => { const event = mockPinnedEvent(["message-2"], ["message-1"]); const plainText = textForEvent(event); - const component = TestRenderer.create(textForEvent(event, true) as ReactElement); + const component = render(textForEvent(event, true) as ReactElement); const expectedText = "@foo:example.com changed the pinned messages for the room."; expect(plainText).toBe(expectedText); - expect(renderComponent(component)).toBe(expectedText); + expect(component.container).toHaveTextContent(expectedText); }); }); diff --git a/test/components/structures/MessagePanel-test.tsx b/test/components/structures/MessagePanel-test.tsx index afa9a1cdf5..e02e96b616 100644 --- a/test/components/structures/MessagePanel-test.tsx +++ b/test/components/structures/MessagePanel-test.tsx @@ -16,7 +16,6 @@ limitations under the License. */ import React from "react"; -import ReactDOM from "react-dom"; import { EventEmitter } from "events"; import { MatrixEvent, Room, RoomMember } from "matrix-js-sdk/src/matrix"; import FakeTimers from "@sinonjs/fake-timers"; @@ -358,7 +357,7 @@ describe("MessagePanel", function () { const [rm] = container.getElementsByClassName("mx_RoomView_myReadMarker_container"); // it should follow the <li> which wraps the event tile for event 4 - const eventContainer = ReactDOM.findDOMNode(tiles[4]); + const eventContainer = tiles[4]; expect(rm.previousSibling).toEqual(eventContainer); }); diff --git a/test/components/structures/TabbedView-test.tsx b/test/components/structures/TabbedView-test.tsx index 3da67d8540..520d406e48 100644 --- a/test/components/structures/TabbedView-test.tsx +++ b/test/components/structures/TabbedView-test.tsx @@ -15,8 +15,7 @@ limitations under the License. */ import React from "react"; -import { fireEvent, render } from "@testing-library/react"; -import { act } from "react-dom/test-utils"; +import { act, fireEvent, render } from "@testing-library/react"; import TabbedView, { Tab, TabLocation } from "../../../src/components/structures/TabbedView"; diff --git a/test/components/structures/ThreadView-test.tsx b/test/components/structures/ThreadView-test.tsx index 2993a3b6d6..5b1526843b 100644 --- a/test/components/structures/ThreadView-test.tsx +++ b/test/components/structures/ThreadView-test.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { getByTestId, render, RenderResult, waitFor } from "@testing-library/react"; +import { act, getByTestId, render, RenderResult, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { mocked } from "jest-mock"; import { MsgType, RelationType } from "matrix-js-sdk/src/@types/event"; @@ -23,7 +23,6 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { Room } from "matrix-js-sdk/src/models/room"; import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread"; import React, { useState } from "react"; -import { act } from "react-dom/test-utils"; import ThreadView from "../../../src/components/structures/ThreadView"; import MatrixClientContext from "../../../src/contexts/MatrixClientContext"; diff --git a/test/components/views/beacon/BeaconListItem-test.tsx b/test/components/views/beacon/BeaconListItem-test.tsx index 580c247b69..cab8da30a0 100644 --- a/test/components/views/beacon/BeaconListItem-test.tsx +++ b/test/components/views/beacon/BeaconListItem-test.tsx @@ -15,10 +15,9 @@ limitations under the License. */ import React from "react"; -import { fireEvent, render } from "@testing-library/react"; +import { act, fireEvent, render } from "@testing-library/react"; import { Beacon, RoomMember, MatrixEvent } from "matrix-js-sdk/src/matrix"; import { LocationAssetType } from "matrix-js-sdk/src/@types/location"; -import { act } from "react-dom/test-utils"; import BeaconListItem from "../../../../src/components/views/beacon/BeaconListItem"; import MatrixClientContext from "../../../../src/contexts/MatrixClientContext"; diff --git a/test/components/views/beacon/BeaconMarker-test.tsx b/test/components/views/beacon/BeaconMarker-test.tsx index 90b578e89b..15b1335d90 100644 --- a/test/components/views/beacon/BeaconMarker-test.tsx +++ b/test/components/views/beacon/BeaconMarker-test.tsx @@ -15,9 +15,8 @@ limitations under the License. */ import React from "react"; -import { render, screen } from "@testing-library/react"; +import { act, render, screen } from "@testing-library/react"; import * as maplibregl from "maplibre-gl"; -import { act } from "react-dom/test-utils"; import { Beacon, Room, RoomMember, MatrixEvent, getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix"; import BeaconMarker from "../../../../src/components/views/beacon/BeaconMarker"; diff --git a/test/components/views/beacon/BeaconViewDialog-test.tsx b/test/components/views/beacon/BeaconViewDialog-test.tsx index d6dfe403f7..11c1863a57 100644 --- a/test/components/views/beacon/BeaconViewDialog-test.tsx +++ b/test/components/views/beacon/BeaconViewDialog-test.tsx @@ -15,17 +15,14 @@ limitations under the License. */ import React from "react"; -// eslint-disable-next-line deprecate/import -import { mount, ReactWrapper } from "enzyme"; import { act } from "react-dom/test-utils"; +import { fireEvent, render, RenderResult } from "@testing-library/react"; import { MatrixClient, MatrixEvent, Room, RoomMember, getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix"; import * as maplibregl from "maplibre-gl"; import { mocked } from "jest-mock"; import BeaconViewDialog from "../../../../src/components/views/beacon/BeaconViewDialog"; import { - findByAttr, - findByTestId, getMockClientWithEventEmitter, makeBeaconEvent, makeBeaconInfoEvent, @@ -34,8 +31,6 @@ import { } from "../../../test-utils"; import { TILE_SERVER_WK_KEY } from "../../../../src/utils/WellKnownUtils"; import { OwnBeaconStore } from "../../../../src/stores/OwnBeaconStore"; -import { BeaconDisplayStatus } from "../../../../src/components/views/beacon/displayStatus"; -import BeaconListItem from "../../../../src/components/views/beacon/BeaconListItem"; describe("<BeaconViewDialog />", () => { // 14.03.2022 16:15 @@ -60,6 +55,7 @@ describe("<BeaconViewDialog />", () => { const mapOptions = { container: {} as unknown as HTMLElement, style: "" }; const mockMap = new maplibregl.Map(mapOptions); + const mockMarker = new maplibregl.Marker(); // make fresh rooms every time // as we update room state @@ -84,13 +80,11 @@ describe("<BeaconViewDialog />", () => { matrixClient: mockClient as MatrixClient, }; - const getComponent = (props = {}) => mount(<BeaconViewDialog {...defaultProps} {...props} />); + const getComponent = (props = {}): RenderResult => render(<BeaconViewDialog {...defaultProps} {...props} />); - const openSidebar = (component: ReactWrapper) => - act(() => { - findByTestId(component, "beacon-view-dialog-open-sidebar").at(0).simulate("click"); - component.setProps({}); - }); + const openSidebar = (getByTestId: RenderResult["getByTestId"]) => { + fireEvent.click(getByTestId("beacon-view-dialog-open-sidebar")); + }; beforeEach(() => { jest.spyOn(OwnBeaconStore.instance, "getLiveBeaconIds").mockRestore(); @@ -103,14 +97,14 @@ describe("<BeaconViewDialog />", () => { const room = setupRoom([defaultEvent]); const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!; beacon.addLocations([location1]); - const component = getComponent(); - expect(component.find("Map").props()).toEqual( - expect.objectContaining({ - centerGeoUri: "geo:51,41", - interactive: true, - }), - ); - expect(component.find("SmartMarker").length).toEqual(1); + getComponent(); + // centered on default event + expect(mockMap.setCenter).toHaveBeenCalledWith({ + lon: 41, + lat: 51, + }); + // marker added + expect(mockMarker.addTo).toHaveBeenCalledWith(mockMap); }); it("does not render any own beacon status when user is not live sharing", () => { @@ -118,8 +112,8 @@ describe("<BeaconViewDialog />", () => { const room = setupRoom([defaultEvent]); const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!; beacon.addLocations([location1]); - const component = getComponent(); - expect(component.find("DialogOwnBeaconStatus").html()).toBeNull(); + const { queryByText } = getComponent(); + expect(queryByText("Live location enabled")).not.toBeInTheDocument(); }); it("renders own beacon status when user is live sharing", () => { @@ -130,52 +124,47 @@ describe("<BeaconViewDialog />", () => { // mock own beacon store to show default event as alice's live beacon jest.spyOn(OwnBeaconStore.instance, "getLiveBeaconIds").mockReturnValue([beacon.identifier]); jest.spyOn(OwnBeaconStore.instance, "getBeaconById").mockReturnValue(beacon); - const component = getComponent(); - expect(component.find("MemberAvatar").length).toBeTruthy(); - expect(component.find("OwnBeaconStatus").props()).toEqual({ - beacon, - displayStatus: BeaconDisplayStatus.Active, - className: "mx_DialogOwnBeaconStatus_status", - }); + const { container } = getComponent(); + expect(container.querySelector(".mx_DialogOwnBeaconStatus")).toMatchSnapshot(); }); - it("updates markers on changes to beacons", () => { + it("updates markers on changes to beacons", async () => { const room = setupRoom([defaultEvent]); const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!; beacon.addLocations([location1]); - const component = getComponent(); - expect(component.find("BeaconMarker").length).toEqual(1); + const { container } = getComponent(); + + // one marker + expect(mockMarker.addTo).toHaveBeenCalledTimes(1); + expect(container.getElementsByClassName("mx_Marker").length).toEqual(1); const anotherBeaconEvent = makeBeaconInfoEvent(bobId, roomId, { isLive: true }, "$bob-room1-1"); - act(() => { // emits RoomStateEvent.BeaconLiveness room.currentState.setStateEvents([anotherBeaconEvent]); + const beacon2 = room.currentState.beacons.get(getBeaconInfoIdentifier(anotherBeaconEvent))!; + beacon2.addLocations([location1]); }); - component.setProps({}); - // two markers now! - expect(component.find("BeaconMarker").length).toEqual(2); + expect(container.getElementsByClassName("mx_Marker").length).toEqual(2); }); it("does not update bounds or center on changing beacons", () => { const room = setupRoom([defaultEvent]); const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!; beacon.addLocations([location1]); - const component = getComponent(); - expect(component.find("BeaconMarker").length).toEqual(1); + const { container } = getComponent(); + expect(container.getElementsByClassName("mx_Marker").length).toEqual(1); const anotherBeaconEvent = makeBeaconInfoEvent(bobId, roomId, { isLive: true }, "$bob-room1-1"); - act(() => { // emits RoomStateEvent.BeaconLiveness room.currentState.setStateEvents([anotherBeaconEvent]); + const beacon2 = room.currentState.beacons.get(getBeaconInfoIdentifier(anotherBeaconEvent))!; + beacon2.addLocations([location1]); }); - - component.setProps({}); - - // two markers now! + // called once on init expect(mockMap.setCenter).toHaveBeenCalledTimes(1); expect(mockMap.fitBounds).toHaveBeenCalledTimes(1); }); @@ -185,14 +174,12 @@ describe("<BeaconViewDialog />", () => { const onFinished = jest.fn(); const room = setupRoom([defaultEvent]); room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent)); - const component = getComponent({ onFinished }); + const { getByTestId } = getComponent({ onFinished }); // map placeholder - expect(findByTestId(component, "beacon-view-dialog-map-fallback")).toMatchSnapshot(); + expect(getByTestId("beacon-view-dialog-map-fallback")).toMatchSnapshot(); - act(() => { - findByTestId(component, "beacon-view-dialog-fallback-close").at(0).simulate("click"); - }); + fireEvent.click(getByTestId("beacon-view-dialog-fallback-close")); expect(onFinished).toHaveBeenCalled(); }); @@ -202,8 +189,8 @@ describe("<BeaconViewDialog />", () => { const room = setupRoom([defaultEvent]); const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!; beacon.addLocations([location1]); - const component = getComponent({ onFinished }); - expect(component.find("BeaconMarker").length).toEqual(1); + const { container } = getComponent({ onFinished }); + expect(container.getElementsByClassName("mx_Marker").length).toEqual(1); // this will replace the defaultEvent // leading to no more live beacons @@ -219,12 +206,10 @@ describe("<BeaconViewDialog />", () => { room.currentState.setStateEvents([anotherBeaconEvent]); }); - component.setProps({}); - // no more avatars - expect(component.find("MemberAvatar").length).toBeFalsy(); + expect(container.getElementsByClassName("mx_Marker").length).toEqual(0); // map still rendered - expect(component.find("Map").length).toBeTruthy(); + expect(container.querySelector("#mx_Map_mx_BeaconViewDialog")).toBeInTheDocument(); // map location unchanged expect(mockMap.setCenter).not.toHaveBeenCalled(); expect(mockMap.fitBounds).not.toHaveBeenCalled(); @@ -235,31 +220,28 @@ describe("<BeaconViewDialog />", () => { const room = setupRoom([defaultEvent]); const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!; beacon.addLocations([location1]); - const component = getComponent(); + const { container, getByTestId } = getComponent(); - openSidebar(component); + openSidebar(getByTestId); - expect(component.find("DialogSidebar").length).toBeTruthy(); + expect(container.querySelector(".mx_DialogSidebar")).toBeInTheDocument(); }); it("closes sidebar on close button click", () => { const room = setupRoom([defaultEvent]); const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!; beacon.addLocations([location1]); - const component = getComponent(); + const { container, getByTestId } = getComponent(); // open the sidebar - openSidebar(component); + openSidebar(getByTestId); - expect(component.find("DialogSidebar").length).toBeTruthy(); + expect(container.querySelector(".mx_DialogSidebar")).toBeInTheDocument(); // now close it - act(() => { - findByAttr("data-testid")(component, "dialog-sidebar-close").at(0).simulate("click"); - component.setProps({}); - }); + fireEvent.click(getByTestId("dialog-sidebar-close")); - expect(component.find("DialogSidebar").length).toBeFalsy(); + expect(container.querySelector(".mx_DialogSidebar")).not.toBeInTheDocument(); }); }); @@ -326,16 +308,17 @@ describe("<BeaconViewDialog />", () => { [location1, location2], ); - const component = getComponent({ beacons: [beacon1, beacon2] }); + const { container, getByTestId } = getComponent({ beacons: [beacon1, beacon2] }); // reset call counts on map mocks after initial render jest.clearAllMocks(); - openSidebar(component); + openSidebar(getByTestId); act(() => { + const listItems = container.querySelectorAll(".mx_BeaconListItem"); // click on the first beacon in the list - component.find(BeaconListItem).at(0).simulate("click"); + fireEvent.click(listItems[0]!); }); // centered on clicked beacon @@ -359,16 +342,17 @@ describe("<BeaconViewDialog />", () => { [location1, location2], ); - const component = getComponent({ beacons: [beacon1, beacon2] }); + const { container, getByTestId } = getComponent({ beacons: [beacon1, beacon2] }); // reset call counts on map mocks after initial render jest.clearAllMocks(); - openSidebar(component); + openSidebar(getByTestId); act(() => { // click on the second beacon in the list - component.find(BeaconListItem).at(1).simulate("click"); + const listItems = container.querySelectorAll(".mx_BeaconListItem"); + fireEvent.click(listItems[1]!); }); const expectedBounds = new maplibregl.LngLatBounds([22, 33], [22, 33]); @@ -378,7 +362,8 @@ describe("<BeaconViewDialog />", () => { act(() => { // click on the second beacon in the list - component.find(BeaconListItem).at(1).simulate("click"); + const listItems = container.querySelectorAll(".mx_BeaconListItem"); + fireEvent.click(listItems[1]!); }); // centered on clicked beacon diff --git a/test/components/views/beacon/DialogSidebar-test.tsx b/test/components/views/beacon/DialogSidebar-test.tsx index 3cb81c0c49..a0def1f445 100644 --- a/test/components/views/beacon/DialogSidebar-test.tsx +++ b/test/components/views/beacon/DialogSidebar-test.tsx @@ -15,8 +15,7 @@ limitations under the License. */ import React, { ComponentProps } from "react"; -import { fireEvent, render } from "@testing-library/react"; -import { act } from "react-dom/test-utils"; +import { act, fireEvent, render } from "@testing-library/react"; import DialogSidebar from "../../../../src/components/views/beacon/DialogSidebar"; import MatrixClientContext from "../../../../src/contexts/MatrixClientContext"; diff --git a/test/components/views/beacon/LeftPanelLiveShareWarning-test.tsx b/test/components/views/beacon/LeftPanelLiveShareWarning-test.tsx index 93fbc7c2b6..5cd3fad032 100644 --- a/test/components/views/beacon/LeftPanelLiveShareWarning-test.tsx +++ b/test/components/views/beacon/LeftPanelLiveShareWarning-test.tsx @@ -16,8 +16,7 @@ limitations under the License. import React from "react"; import { mocked } from "jest-mock"; -import { fireEvent, render } from "@testing-library/react"; -import { act } from "react-dom/test-utils"; +import { act, fireEvent, render } from "@testing-library/react"; import { Beacon, BeaconIdentifier } from "matrix-js-sdk/src/matrix"; import LeftPanelLiveShareWarning from "../../../../src/components/views/beacon/LeftPanelLiveShareWarning"; diff --git a/test/components/views/beacon/RoomCallBanner-test.tsx b/test/components/views/beacon/RoomCallBanner-test.tsx index 0f19aa471a..59d4ae615c 100644 --- a/test/components/views/beacon/RoomCallBanner-test.tsx +++ b/test/components/views/beacon/RoomCallBanner-test.tsx @@ -15,10 +15,9 @@ limitations under the License. */ import React from "react"; -import { act } from "react-dom/test-utils"; import { Room, PendingEventOrdering, MatrixClient, RoomMember, RoomStateEvent } from "matrix-js-sdk/src/matrix"; import { ClientWidgetApi, Widget } from "matrix-widget-api"; -import { cleanup, render, screen } from "@testing-library/react"; +import { act, cleanup, render, screen } from "@testing-library/react"; import { mocked, Mocked } from "jest-mock"; import { mkRoomMember, MockedCall, setupAsyncStoreWithClient, stubClient, useMockedCalls } from "../../../test-utils"; diff --git a/test/components/views/beacon/RoomLiveShareWarning-test.tsx b/test/components/views/beacon/RoomLiveShareWarning-test.tsx index 032d7e0783..fef2bcbb32 100644 --- a/test/components/views/beacon/RoomLiveShareWarning-test.tsx +++ b/test/components/views/beacon/RoomLiveShareWarning-test.tsx @@ -15,10 +15,9 @@ limitations under the License. */ import React from "react"; -import { act } from "react-dom/test-utils"; import { Room, Beacon, BeaconEvent, getBeaconInfoIdentifier, MatrixEvent } from "matrix-js-sdk/src/matrix"; import { logger } from "matrix-js-sdk/src/logger"; -import { fireEvent, getByTestId, render, screen, waitFor } from "@testing-library/react"; +import { act, fireEvent, getByTestId, render, screen, waitFor } from "@testing-library/react"; import RoomLiveShareWarning from "../../../../src/components/views/beacon/RoomLiveShareWarning"; import { OwnBeaconStore, OwnBeaconStoreEvent } from "../../../../src/stores/OwnBeaconStore"; diff --git a/test/components/views/beacon/__snapshots__/BeaconViewDialog-test.tsx.snap b/test/components/views/beacon/__snapshots__/BeaconViewDialog-test.tsx.snap index 3eda1f9292..e2f61aaca5 100644 --- a/test/components/views/beacon/__snapshots__/BeaconViewDialog-test.tsx.snap +++ b/test/components/views/beacon/__snapshots__/BeaconViewDialog-test.tsx.snap @@ -1,83 +1,83 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`<BeaconViewDialog /> renders a fallback when there are no locations 1`] = ` -[ - <MapFallback - className="mx_BeaconViewDialog_map" - data-test-id="beacon-view-dialog-map-fallback" - > - <div - className="mx_MapFallback mx_BeaconViewDialog_map" - data-test-id="beacon-view-dialog-map-fallback" - > - <div - className="mx_MapFallback_bg" - /> - <div - className="mx_MapFallback_icon" - /> - <span - className="mx_BeaconViewDialog_mapFallbackMessage" - > - No live locations - </span> - <AccessibleButton - data-test-id="beacon-view-dialog-fallback-close" - element="div" - kind="primary" - onClick={[MockFunction]} - role="button" - tabIndex={0} - > - <div - className="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary" - data-test-id="beacon-view-dialog-fallback-close" - onClick={[MockFunction]} - onKeyDown={[Function]} - onKeyUp={[Function]} - role="button" - tabIndex={0} - > - Close - </div> - </AccessibleButton> - </div> - </MapFallback>, +<div + class="mx_MapFallback mx_BeaconViewDialog_map" + data-testid="beacon-view-dialog-map-fallback" +> <div - className="mx_MapFallback mx_BeaconViewDialog_map" - data-test-id="beacon-view-dialog-map-fallback" + class="mx_MapFallback_bg" + /> + <div + class="mx_MapFallback_icon" + /> + <span + class="mx_BeaconViewDialog_mapFallbackMessage" + > + No live locations + </span> + <div + class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary" + data-testid="beacon-view-dialog-fallback-close" + role="button" + tabindex="0" + > + Close + </div> +</div> +`; + +exports[`<BeaconViewDialog /> renders own beacon status when user is live sharing 1`] = ` +<div + class="mx_DialogOwnBeaconStatus" +> + <span + class="mx_BaseAvatar mx_DialogOwnBeaconStatus_avatar" + role="presentation" + > + <span + aria-hidden="true" + class="mx_BaseAvatar_initial" + style="font-size: 20.8px; width: 32px; line-height: 32px;" + > + A + </span> + <img + alt="" + aria-hidden="true" + class="mx_BaseAvatar_image" + data-testid="avatar-img" + src="data:image/png;base64,00" + style="width: 32px; height: 32px;" + title="@alice:server" + /> + </span> + <div + class="mx_BeaconStatus mx_BeaconStatus_Active mx_DialogOwnBeaconStatus_status" > <div - className="mx_MapFallback_bg" - /> - <div - className="mx_MapFallback_icon" - /> - <span - className="mx_BeaconViewDialog_mapFallbackMessage" + class="mx_BeaconStatus_description" > - No live locations - </span> - <AccessibleButton - data-test-id="beacon-view-dialog-fallback-close" - element="div" - kind="primary" - onClick={[MockFunction]} - role="button" - tabIndex={0} - > - <div - className="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary" - data-test-id="beacon-view-dialog-fallback-close" - onClick={[MockFunction]} - onKeyDown={[Function]} - onKeyUp={[Function]} - role="button" - tabIndex={0} + <span + class="mx_BeaconStatus_label" > - Close - </div> - </AccessibleButton> - </div>, -] + Live location enabled + </span> + <span + class="mx_LiveTimeRemaining" + data-test-id="room-live-share-expiry" + > + 1h left + </span> + </div> + <div + class="mx_AccessibleButton mx_OwnBeaconStatus_button mx_OwnBeaconStatus_destructiveButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link" + data-test-id="beacon-status-stop-beacon" + role="button" + tabindex="0" + > + Stop + </div> + </div> +</div> `; diff --git a/test/components/views/dialogs/ForwardDialog-test.tsx b/test/components/views/dialogs/ForwardDialog-test.tsx index 7d0354f352..077776499c 100644 --- a/test/components/views/dialogs/ForwardDialog-test.tsx +++ b/test/components/views/dialogs/ForwardDialog-test.tsx @@ -15,11 +15,10 @@ limitations under the License. */ import React from "react"; -import { act } from "react-dom/test-utils"; import { MatrixEvent, EventType } from "matrix-js-sdk/src/matrix"; import { LocationAssetType, M_ASSET, M_LOCATION, M_TIMESTAMP } from "matrix-js-sdk/src/@types/location"; import { M_TEXT } from "matrix-js-sdk/src/@types/extensible_events"; -import { fireEvent, getByTestId, render, RenderResult, screen } from "@testing-library/react"; +import { act, fireEvent, getByTestId, render, RenderResult, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; diff --git a/test/components/views/dialogs/SpotlightDialog-test.tsx b/test/components/views/dialogs/SpotlightDialog-test.tsx index d3ab12a982..08e746bb1d 100644 --- a/test/components/views/dialogs/SpotlightDialog-test.tsx +++ b/test/components/views/dialogs/SpotlightDialog-test.tsx @@ -367,4 +367,67 @@ describe("Spotlight Dialog", () => { expect(screen.queryByText("give feedback")).not.toBeInTheDocument(); }); }); + + describe("nsfw public rooms filter", () => { + const nsfwNameRoom: IPublicRoomsChunkRoom = { + room_id: "@room1:matrix.org", + name: "Room 1 [NSFW]", + topic: undefined, + world_readable: false, + num_joined_members: 1, + guest_can_join: false, + }; + + const nsfwTopicRoom: IPublicRoomsChunkRoom = { + room_id: "@room2:matrix.org", + name: "Room 2", + topic: "A room with a topic that includes nsfw", + world_readable: false, + num_joined_members: 1, + guest_can_join: false, + }; + + const potatoRoom: IPublicRoomsChunkRoom = { + room_id: "@room3:matrix.org", + name: "Potato Room 3", + topic: "Room where we discuss potatoes", + world_readable: false, + num_joined_members: 1, + guest_can_join: false, + }; + + beforeEach(() => { + mockedClient = mockClient({ rooms: [nsfwNameRoom, nsfwTopicRoom, potatoRoom], users: [testPerson] }); + SettingsStore.setValue("SpotlightSearch.showNsfwPublicRooms", null, SettingLevel.DEVICE, false); + }); + + afterAll(() => { + SettingsStore.setValue("SpotlightSearch.showNsfwPublicRooms", null, SettingLevel.DEVICE, false); + }); + + it("does not display rooms with nsfw keywords in results when showNsfwPublicRooms is falsy", async () => { + render(<SpotlightDialog initialFilter={Filter.PublicRooms} onFinished={() => null} />); + + // search is debounced + jest.advanceTimersByTime(200); + await flushPromisesWithFakeTimers(); + + expect(screen.getByText(potatoRoom.name)).toBeInTheDocument(); + expect(screen.queryByText(nsfwTopicRoom.name)).not.toBeInTheDocument(); + expect(screen.queryByText(nsfwTopicRoom.name)).not.toBeInTheDocument(); + }); + + it("displays rooms with nsfw keywords in results when showNsfwPublicRooms is truthy", async () => { + SettingsStore.setValue("SpotlightSearch.showNsfwPublicRooms", null, SettingLevel.DEVICE, true); + render(<SpotlightDialog initialFilter={Filter.PublicRooms} onFinished={() => null} />); + + // search is debounced + jest.advanceTimersByTime(200); + await flushPromisesWithFakeTimers(); + + expect(screen.getByText(nsfwTopicRoom.name)).toBeInTheDocument(); + expect(screen.getByText(nsfwNameRoom.name)).toBeInTheDocument(); + expect(screen.getByText(potatoRoom.name)).toBeInTheDocument(); + }); + }); }); diff --git a/test/components/views/elements/ExternalLink-test.tsx b/test/components/views/elements/ExternalLink-test.tsx index 1333d341a2..83ee4f93a8 100644 --- a/test/components/views/elements/ExternalLink-test.tsx +++ b/test/components/views/elements/ExternalLink-test.tsx @@ -12,8 +12,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { render } from "@testing-library/react"; import React from "react"; -import { renderIntoDocument } from "react-dom/test-utils"; import ExternalLink from "../../../../src/components/views/elements/ExternalLink"; @@ -22,15 +22,10 @@ describe("<ExternalLink />", () => { "href": "test.com", "onClick": jest.fn(), "className": "myCustomClass", - "data-test-id": "test", + "data-testid": "test", }; const getComponent = (props = {}) => { - const wrapper = renderIntoDocument<HTMLDivElement>( - <div> - <ExternalLink {...defaultProps} {...props} /> - </div>, - ) as HTMLDivElement; - return wrapper.children[0]; + return render(<ExternalLink {...defaultProps} {...props} />); }; it("renders link correctly", () => { @@ -39,18 +34,19 @@ describe("<ExternalLink />", () => { react element <b>children</b> </span> ); - expect(getComponent({ children, target: "_self", rel: "noopener" })).toMatchSnapshot(); + expect(getComponent({ children, target: "_self", rel: "noopener" }).asFragment()).toMatchSnapshot(); }); it("defaults target and rel", () => { const children = "test"; - const component = getComponent({ children }); - expect(component.getAttribute("rel")).toEqual("noreferrer noopener"); - expect(component.getAttribute("target")).toEqual("_blank"); + const { getByTestId } = getComponent({ children }); + const container = getByTestId("test"); + expect(container.getAttribute("rel")).toEqual("noreferrer noopener"); + expect(container.getAttribute("target")).toEqual("_blank"); }); it("renders plain text link correctly", () => { const children = "test"; - expect(getComponent({ children })).toMatchSnapshot(); + expect(getComponent({ children }).asFragment()).toMatchSnapshot(); }); }); diff --git a/test/components/views/elements/TooltipTarget-test.tsx b/test/components/views/elements/TooltipTarget-test.tsx index 82b620c262..c6fa4d47a0 100644 --- a/test/components/views/elements/TooltipTarget-test.tsx +++ b/test/components/views/elements/TooltipTarget-test.tsx @@ -15,8 +15,7 @@ limitations under the License. */ import React from "react"; -import { renderIntoDocument, Simulate } from "react-dom/test-utils"; -import { act } from "react-dom/test-utils"; +import { act, renderIntoDocument, Simulate } from "react-dom/test-utils"; import { Alignment } from "../../../../src/components/views/elements/Tooltip"; import TooltipTarget from "../../../../src/components/views/elements/TooltipTarget"; diff --git a/test/components/views/elements/__snapshots__/ExternalLink-test.tsx.snap b/test/components/views/elements/__snapshots__/ExternalLink-test.tsx.snap index fae5cfb9e7..1cc44a5450 100644 --- a/test/components/views/elements/__snapshots__/ExternalLink-test.tsx.snap +++ b/test/components/views/elements/__snapshots__/ExternalLink-test.tsx.snap @@ -1,36 +1,40 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`<ExternalLink /> renders link correctly 1`] = ` -<a - class="mx_ExternalLink myCustomClass" - data-test-id="test" - href="test.com" - rel="noopener" - target="_self" -> - <span> - react element - <b> - children - </b> - </span> - <i - class="mx_ExternalLink_icon" - /> -</a> +<DocumentFragment> + <a + class="mx_ExternalLink myCustomClass" + data-testid="test" + href="test.com" + rel="noopener" + target="_self" + > + <span> + react element + <b> + children + </b> + </span> + <i + class="mx_ExternalLink_icon" + /> + </a> +</DocumentFragment> `; exports[`<ExternalLink /> renders plain text link correctly 1`] = ` -<a - class="mx_ExternalLink myCustomClass" - data-test-id="test" - href="test.com" - rel="noreferrer noopener" - target="_blank" -> - test - <i - class="mx_ExternalLink_icon" - /> -</a> +<DocumentFragment> + <a + class="mx_ExternalLink myCustomClass" + data-testid="test" + href="test.com" + rel="noreferrer noopener" + target="_blank" + > + test + <i + class="mx_ExternalLink_icon" + /> + </a> +</DocumentFragment> `; diff --git a/test/components/views/location/LocationPicker-test.tsx b/test/components/views/location/LocationPicker-test.tsx index 97f21ffe7a..50b5af248f 100644 --- a/test/components/views/location/LocationPicker-test.tsx +++ b/test/components/views/location/LocationPicker-test.tsx @@ -15,9 +15,8 @@ limitations under the License. */ import React from "react"; -import { fireEvent, render, RenderResult } from "@testing-library/react"; +import { act, fireEvent, render, RenderResult } from "@testing-library/react"; import * as maplibregl from "maplibre-gl"; -import { act } from "react-dom/test-utils"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { MatrixClient } from "matrix-js-sdk/src/client"; import { mocked } from "jest-mock"; diff --git a/test/components/views/location/LocationShareMenu-test.tsx b/test/components/views/location/LocationShareMenu-test.tsx index 00df011f35..5aeb79b922 100644 --- a/test/components/views/location/LocationShareMenu-test.tsx +++ b/test/components/views/location/LocationShareMenu-test.tsx @@ -21,8 +21,7 @@ import { MatrixClient } from "matrix-js-sdk/src/client"; import { RelationType } from "matrix-js-sdk/src/matrix"; import { logger } from "matrix-js-sdk/src/logger"; import { M_ASSET, LocationAssetType } from "matrix-js-sdk/src/@types/location"; -import { act } from "react-dom/test-utils"; -import { fireEvent, render, RenderResult } from "@testing-library/react"; +import { act, fireEvent, render, RenderResult } from "@testing-library/react"; import * as maplibregl from "maplibre-gl"; import LocationShareMenu from "../../../../src/components/views/location/LocationShareMenu"; diff --git a/test/components/views/location/Map-test.tsx b/test/components/views/location/Map-test.tsx index ab9f86cf98..3cde4a12d6 100644 --- a/test/components/views/location/Map-test.tsx +++ b/test/components/views/location/Map-test.tsx @@ -15,8 +15,7 @@ limitations under the License. */ import React from "react"; -import { act } from "react-dom/test-utils"; -import { fireEvent, getByTestId, render } from "@testing-library/react"; +import { act, fireEvent, getByTestId, render } from "@testing-library/react"; import * as maplibregl from "maplibre-gl"; import { ClientEvent } from "matrix-js-sdk/src/matrix"; import { logger } from "matrix-js-sdk/src/logger"; diff --git a/test/components/views/messages/DecryptionFailureBody-test.tsx b/test/components/views/messages/DecryptionFailureBody-test.tsx new file mode 100644 index 0000000000..e8d4fce56e --- /dev/null +++ b/test/components/views/messages/DecryptionFailureBody-test.tsx @@ -0,0 +1,63 @@ +/* + * 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 } from "@testing-library/react"; +import { MatrixEvent } from "matrix-js-sdk/src/matrix"; + +import { mkEvent } from "../../../test-utils"; +import { DecryptionFailureBody } from "../../../../src/components/views/messages/DecryptionFailureBody"; + +describe("DecryptionFailureBody", () => { + function customRender(event: MatrixEvent) { + return render(<DecryptionFailureBody mxEvent={event} />); + } + + it(`Should display "Unable to decrypt message"`, () => { + // When + const event = mkEvent({ + type: "m.room.message", + room: "myfakeroom", + user: "myfakeuser", + content: { + msgtype: "m.bad.encrypted", + }, + event: true, + }); + const { container } = customRender(event); + + // Then + expect(container).toMatchSnapshot(); + }); + + it(`Should display "The sender has blocked you from receiving this message"`, () => { + // When + const event = mkEvent({ + type: "m.room.message", + room: "myfakeroom", + user: "myfakeuser", + content: { + msgtype: "m.bad.encrypted", + }, + event: true, + }); + jest.spyOn(event, "isEncryptedDisabledForUnverifiedDevices", "get").mockReturnValue(true); + const { container } = customRender(event); + + // Then + expect(container).toMatchSnapshot(); + }); +}); diff --git a/test/components/views/messages/MKeyVerificationConclusion-test.tsx b/test/components/views/messages/MKeyVerificationConclusion-test.tsx index c19166ef57..c5d023c775 100644 --- a/test/components/views/messages/MKeyVerificationConclusion-test.tsx +++ b/test/components/views/messages/MKeyVerificationConclusion-test.tsx @@ -15,7 +15,7 @@ limitations under the License. */ import React from "react"; -import TestRenderer from "react-test-renderer"; +import { render } from "@testing-library/react"; import { EventEmitter } from "events"; import { MatrixEvent, EventType } from "matrix-js-sdk/src/matrix"; import { CryptoEvent } from "matrix-js-sdk/src/crypto"; @@ -72,29 +72,29 @@ describe("MKeyVerificationConclusion", () => { it("shouldn't render if there's no verificationRequest", () => { const event = new MatrixEvent({}); - const renderer = TestRenderer.create(<MKeyVerificationConclusion mxEvent={event} />); - expect(renderer.toJSON()).toBeNull(); + const { container } = render(<MKeyVerificationConclusion mxEvent={event} />); + expect(container).toBeEmpty(); }); it("shouldn't render if the verificationRequest is pending", () => { const event = new MatrixEvent({}); event.verificationRequest = getMockVerificationRequest({ pending: true }); - const renderer = TestRenderer.create(<MKeyVerificationConclusion mxEvent={event} />); - expect(renderer.toJSON()).toBeNull(); + const { container } = render(<MKeyVerificationConclusion mxEvent={event} />); + expect(container).toBeEmpty(); }); it("shouldn't render if the event type is cancel but the request type isn't", () => { const event = new MatrixEvent({ type: EventType.KeyVerificationCancel }); event.verificationRequest = getMockVerificationRequest({ cancelled: false }); - const renderer = TestRenderer.create(<MKeyVerificationConclusion mxEvent={event} />); - expect(renderer.toJSON()).toBeNull(); + const { container } = render(<MKeyVerificationConclusion mxEvent={event} />); + expect(container).toBeEmpty(); }); it("shouldn't render if the event type is done but the request type isn't", () => { const event = new MatrixEvent({ type: "m.key.verification.done" }); event.verificationRequest = getMockVerificationRequest({ done: false }); - const renderer = TestRenderer.create(<MKeyVerificationConclusion mxEvent={event} />); - expect(renderer.toJSON()).toBeNull(); + const { container } = render(<MKeyVerificationConclusion mxEvent={event} />); + expect(container).toBeEmpty(); }); it("shouldn't render if the user isn't actually trusted", () => { @@ -102,8 +102,8 @@ describe("MKeyVerificationConclusion", () => { const event = new MatrixEvent({ type: "m.key.verification.done" }); event.verificationRequest = getMockVerificationRequest({ done: true }); - const renderer = TestRenderer.create(<MKeyVerificationConclusion mxEvent={event} />); - expect(renderer.toJSON()).toBeNull(); + const { container } = render(<MKeyVerificationConclusion mxEvent={event} />); + expect(container).toBeEmpty(); }); it("should rerender appropriately if user trust status changes", () => { @@ -111,8 +111,8 @@ describe("MKeyVerificationConclusion", () => { const event = new MatrixEvent({ type: "m.key.verification.done" }); event.verificationRequest = getMockVerificationRequest({ done: true, otherUserId: "@someuser:domain" }); - const renderer = TestRenderer.create(<MKeyVerificationConclusion mxEvent={event} />); - expect(renderer.toJSON()).toBeNull(); + const { container } = render(<MKeyVerificationConclusion mxEvent={event} />); + expect(container).toBeEmpty(); mockClient.checkUserTrust.mockReturnValue(trustworthy); @@ -122,7 +122,7 @@ describe("MKeyVerificationConclusion", () => { "@anotheruser:domain", new UserTrustLevel(true, true, true), ); - expect(renderer.toJSON()).toBeNull(); + expect(container).toBeEmpty(); /* But when our user changes, we do rerender */ mockClient.emit( @@ -130,6 +130,6 @@ describe("MKeyVerificationConclusion", () => { event.verificationRequest.otherUserId, new UserTrustLevel(true, true, true), ); - expect(renderer.toJSON()).not.toBeNull(); + expect(container).not.toBeEmpty(); }); }); diff --git a/test/components/views/messages/MPollBody-test.tsx b/test/components/views/messages/MPollBody-test.tsx index 2e411f2b7d..6cb3187603 100644 --- a/test/components/views/messages/MPollBody-test.tsx +++ b/test/components/views/messages/MPollBody-test.tsx @@ -765,6 +765,20 @@ describe("MPollBody", () => { expect(container).toMatchSnapshot(); }); + it("renders a warning message when poll has undecryptable relations", async () => { + const votes = [ + responseEvent("@op:example.com", "pizza", 12), + responseEvent("@op:example.com", [], 13), + responseEvent("@op:example.com", "italian", 14), + responseEvent("@me:example.com", "wings", 15), + responseEvent("@qr:example.com", "italian", 16), + ]; + + jest.spyOn(votes[1], "isDecryptionFailure").mockReturnValue(true); + const { getByText } = await newMPollBody(votes); + expect(getByText("Due to decryption errors, some votes may not be counted")).toBeInTheDocument(); + }); + it("renders a poll with local, non-local and invalid votes", async () => { const votes = [ responseEvent("@a:example.com", "pizza", 12), diff --git a/test/components/views/messages/__snapshots__/DecryptionFailureBody-test.tsx.snap b/test/components/views/messages/__snapshots__/DecryptionFailureBody-test.tsx.snap new file mode 100644 index 0000000000..c0096b6467 --- /dev/null +++ b/test/components/views/messages/__snapshots__/DecryptionFailureBody-test.tsx.snap @@ -0,0 +1,21 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DecryptionFailureBody Should display "The sender has blocked you from receiving this message" 1`] = ` +<div> + <div + class="mx_DecryptionFailureBody mx_EventTile_content" + > + The sender has blocked you from receiving this message + </div> +</div> +`; + +exports[`DecryptionFailureBody Should display "Unable to decrypt message" 1`] = ` +<div> + <div + class="mx_DecryptionFailureBody mx_EventTile_content" + > + Unable to decrypt message + </div> +</div> +`; diff --git a/test/components/views/messages/__snapshots__/MPollEndBody-test.tsx.snap b/test/components/views/messages/__snapshots__/MPollEndBody-test.tsx.snap index e98ec1f4fc..bad7c2d129 100644 --- a/test/components/views/messages/__snapshots__/MPollEndBody-test.tsx.snap +++ b/test/components/views/messages/__snapshots__/MPollEndBody-test.tsx.snap @@ -10,97 +10,104 @@ exports[`<MPollEndBody /> when poll start event does not exist in current timeli exports[`<MPollEndBody /> when poll start event exists in current timeline renders an ended poll 1`] = ` <div> - <div - class="mx_MPollBody" - > - <h2 - data-testid="pollQuestion" + <div> + <span + class="mx_Caption" > - Question? - </h2> + Ended a poll + </span> <div - class="mx_MPollBody_allOptions" + class="mx_MPollBody" > + <h2 + data-testid="pollQuestion" + > + Question? + </h2> <div - class="mx_PollOption mx_PollOption_ended" - data-testid="pollOption-socks" + class="mx_MPollBody_allOptions" > <div - class="mx_PollOption_endedOption" - data-value="socks" + class="mx_PollOption mx_PollOption_ended" + data-testid="pollOption-socks" > <div - class="mx_PollOption_content" + class="mx_PollOption_endedOption" + data-value="socks" > <div - class="mx_PollOption_optionText" + class="mx_PollOption_content" > - Socks + <div + class="mx_PollOption_optionText" + > + Socks + </div> + <div + class="mx_PollOption_optionVoteCount" + > + 0 votes + </div> </div> + </div> + <div + class="mx_PollOption_popularityBackground" + > <div - class="mx_PollOption_optionVoteCount" - > - 0 votes - </div> + class="mx_PollOption_popularityAmount" + style="width: 0%;" + /> </div> </div> <div - class="mx_PollOption_popularityBackground" + class="mx_PollOption mx_PollOption_ended" + data-testid="pollOption-shoes" > <div - class="mx_PollOption_popularityAmount" - style="width: 0%;" - /> - </div> - </div> - <div - class="mx_PollOption mx_PollOption_ended" - data-testid="pollOption-shoes" - > - <div - class="mx_PollOption_endedOption" - data-value="shoes" - > - <div - class="mx_PollOption_content" + class="mx_PollOption_endedOption" + data-value="shoes" > <div - class="mx_PollOption_optionText" + class="mx_PollOption_content" > - Shoes - </div> - <div - class="mx_PollOption_optionVoteCount" - > - 0 votes + <div + class="mx_PollOption_optionText" + > + Shoes + </div> + <div + class="mx_PollOption_optionVoteCount" + > + 0 votes + </div> </div> </div> - </div> - <div - class="mx_PollOption_popularityBackground" - > <div - class="mx_PollOption_popularityAmount" - style="width: 0%;" - /> + class="mx_PollOption_popularityBackground" + > + <div + class="mx_PollOption_popularityAmount" + style="width: 0%;" + /> + </div> </div> </div> - </div> - <div - class="mx_MPollBody_totalVotes" - data-testid="totalVotes" - > - Final result based on 0 votes <div - class="mx_Spinner" + class="mx_MPollBody_totalVotes" + data-testid="totalVotes" > + Final result based on 0 votes <div - aria-label="Loading…" - class="mx_Spinner_icon" - data-testid="spinner" - role="progressbar" - style="width: 16px; height: 16px;" - /> + class="mx_Spinner" + > + <div + aria-label="Loading…" + class="mx_Spinner_icon" + data-testid="spinner" + role="progressbar" + style="width: 16px; height: 16px;" + /> + </div> </div> </div> </div> diff --git a/test/components/views/rooms/RoomListHeader-test.tsx b/test/components/views/rooms/RoomListHeader-test.tsx index f67d2083d1..03a4b2fd20 100644 --- a/test/components/views/rooms/RoomListHeader-test.tsx +++ b/test/components/views/rooms/RoomListHeader-test.tsx @@ -18,9 +18,8 @@ import React from "react"; import { MatrixClient } from "matrix-js-sdk/src/client"; import { Room } from "matrix-js-sdk/src/matrix"; import { EventType } from "matrix-js-sdk/src/@types/event"; -import { act } from "react-dom/test-utils"; import { mocked } from "jest-mock"; -import { render, screen, fireEvent, RenderResult } from "@testing-library/react"; +import { act, render, screen, fireEvent, RenderResult } from "@testing-library/react"; import SpaceStore from "../../../../src/stores/spaces/SpaceStore"; import { MetaSpace } from "../../../../src/stores/spaces"; diff --git a/test/components/views/settings/CryptographyPanel-test.tsx b/test/components/views/settings/CryptographyPanel-test.tsx index 7455b2adc5..41be17b076 100644 --- a/test/components/views/settings/CryptographyPanel-test.tsx +++ b/test/components/views/settings/CryptographyPanel-test.tsx @@ -14,8 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { ReactElement } from "react"; -import ReactDOM from "react-dom"; +import React from "react"; +import { render } from "@testing-library/react"; import { MatrixClient } from "matrix-js-sdk/src/matrix"; import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; @@ -37,16 +37,9 @@ describe("CryptographyPanel", () => { const rendered = render(<CryptographyPanel />); // Then it displays info about the user's session - const codes = rendered.querySelectorAll("code"); + const codes = rendered.container.querySelectorAll("code"); expect(codes.length).toEqual(2); expect(codes[0].innerHTML).toEqual(sessionId); expect(codes[1].innerHTML).toEqual(sessionKeyFormatted); }); }); - -function render(component: ReactElement<CryptographyPanel>): HTMLDivElement { - const parentDiv = document.createElement("div"); - document.body.appendChild(parentDiv); - ReactDOM.render(component, parentDiv); - return parentDiv; -} diff --git a/test/components/views/settings/DevicesPanel-test.tsx b/test/components/views/settings/DevicesPanel-test.tsx index 530bf55a79..391472f027 100644 --- a/test/components/views/settings/DevicesPanel-test.tsx +++ b/test/components/views/settings/DevicesPanel-test.tsx @@ -14,8 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ import React from "react"; -import { fireEvent, render } from "@testing-library/react"; -import { act } from "react-dom/test-utils"; +import { act, fireEvent, render } from "@testing-library/react"; import { CrossSigningInfo } from "matrix-js-sdk/src/crypto/CrossSigning"; import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo"; import { sleep } from "matrix-js-sdk/src/utils"; diff --git a/test/components/views/settings/Notifications-test.tsx b/test/components/views/settings/Notifications-test.tsx index d956ba0d4b..a30eb1b843 100644 --- a/test/components/views/settings/Notifications-test.tsx +++ b/test/components/views/settings/Notifications-test.tsx @@ -25,8 +25,7 @@ import { PushRuleActionName, } from "matrix-js-sdk/src/matrix"; import { IThreepid, ThreepidMedium } from "matrix-js-sdk/src/@types/threepids"; -import { act } from "react-dom/test-utils"; -import { fireEvent, getByTestId, render, screen, waitFor } from "@testing-library/react"; +import { act, fireEvent, getByTestId, render, screen, waitFor } from "@testing-library/react"; import Notifications from "../../../../src/components/views/settings/Notifications"; import SettingsStore from "../../../../src/settings/SettingsStore"; diff --git a/test/components/views/settings/SettingsFieldset-test.tsx b/test/components/views/settings/SettingsFieldset-test.tsx index 3aafce504d..ead6767ae7 100644 --- a/test/components/views/settings/SettingsFieldset-test.tsx +++ b/test/components/views/settings/SettingsFieldset-test.tsx @@ -12,8 +12,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { render } from "@testing-library/react"; import React from "react"; -import { renderIntoDocument } from "react-dom/test-utils"; import SettingsFieldset from "../../../../src/components/views/settings/SettingsFieldset"; @@ -21,24 +21,19 @@ describe("<SettingsFieldset />", () => { const defaultProps = { "legend": "Who can read history?", "children": <div>test</div>, - "data-test-id": "test", + "data-testid": "test", }; const getComponent = (props = {}) => { - const wrapper = renderIntoDocument<HTMLDivElement>( - <div> - <SettingsFieldset {...defaultProps} {...props} /> - </div>, - ) as HTMLDivElement; - return wrapper.children[0]; + return render(<SettingsFieldset {...defaultProps} {...props} />); }; it("renders fieldset without description", () => { - expect(getComponent()).toMatchSnapshot(); + expect(getComponent().asFragment()).toMatchSnapshot(); }); it("renders fieldset with plain text description", () => { const description = "Changes to who can read history."; - expect(getComponent({ description })).toMatchSnapshot(); + expect(getComponent({ description }).asFragment()).toMatchSnapshot(); }); it("renders fieldset with react description", () => { @@ -48,6 +43,6 @@ describe("<SettingsFieldset />", () => { <a href="#test">a link</a> </> ); - expect(getComponent({ description })).toMatchSnapshot(); + expect(getComponent({ description }).asFragment()).toMatchSnapshot(); }); }); diff --git a/test/components/views/settings/__snapshots__/SettingsFieldset-test.tsx.snap b/test/components/views/settings/__snapshots__/SettingsFieldset-test.tsx.snap index 44cf7af89e..77d37ccaa0 100644 --- a/test/components/views/settings/__snapshots__/SettingsFieldset-test.tsx.snap +++ b/test/components/views/settings/__snapshots__/SettingsFieldset-test.tsx.snap @@ -1,66 +1,72 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`<SettingsFieldset /> renders fieldset with plain text description 1`] = ` -<fieldset - class="mx_SettingsFieldset" - data-test-id="test" -> - <legend - class="mx_SettingsFieldset_legend" +<DocumentFragment> + <fieldset + class="mx_SettingsFieldset" + data-testid="test" > - Who can read history? - </legend> - <div - class="mx_SettingsFieldset_description" - > - Changes to who can read history. - </div> - <div> - test - </div> -</fieldset> + <legend + class="mx_SettingsFieldset_legend" + > + Who can read history? + </legend> + <div + class="mx_SettingsFieldset_description" + > + Changes to who can read history. + </div> + <div> + test + </div> + </fieldset> +</DocumentFragment> `; exports[`<SettingsFieldset /> renders fieldset with react description 1`] = ` -<fieldset - class="mx_SettingsFieldset" - data-test-id="test" -> - <legend - class="mx_SettingsFieldset_legend" +<DocumentFragment> + <fieldset + class="mx_SettingsFieldset" + data-testid="test" > - Who can read history? - </legend> - <div - class="mx_SettingsFieldset_description" - > - <p> - Test - </p> - <a - href="#test" + <legend + class="mx_SettingsFieldset_legend" > - a link - </a> - </div> - <div> - test - </div> -</fieldset> + Who can read history? + </legend> + <div + class="mx_SettingsFieldset_description" + > + <p> + Test + </p> + <a + href="#test" + > + a link + </a> + </div> + <div> + test + </div> + </fieldset> +</DocumentFragment> `; exports[`<SettingsFieldset /> renders fieldset without description 1`] = ` -<fieldset - class="mx_SettingsFieldset" - data-test-id="test" -> - <legend - class="mx_SettingsFieldset_legend" +<DocumentFragment> + <fieldset + class="mx_SettingsFieldset" + data-testid="test" > - Who can read history? - </legend> - <div> - test - </div> -</fieldset> + <legend + class="mx_SettingsFieldset_legend" + > + Who can read history? + </legend> + <div> + test + </div> + </fieldset> +</DocumentFragment> `; diff --git a/test/components/views/settings/devices/CurrentDeviceSection-test.tsx b/test/components/views/settings/devices/CurrentDeviceSection-test.tsx index 2c6557337b..b102a996cb 100644 --- a/test/components/views/settings/devices/CurrentDeviceSection-test.tsx +++ b/test/components/views/settings/devices/CurrentDeviceSection-test.tsx @@ -15,8 +15,7 @@ limitations under the License. */ import React from "react"; -import { fireEvent, render } from "@testing-library/react"; -import { act } from "react-dom/test-utils"; +import { act, fireEvent, render } from "@testing-library/react"; import CurrentDeviceSection from "../../../../../src/components/views/settings/devices/CurrentDeviceSection"; import { DeviceType } from "../../../../../src/utils/device/parseUserAgent"; diff --git a/test/components/views/settings/devices/SelectableDeviceTile-test.tsx b/test/components/views/settings/devices/SelectableDeviceTile-test.tsx index b2e79fc70f..6192e35c22 100644 --- a/test/components/views/settings/devices/SelectableDeviceTile-test.tsx +++ b/test/components/views/settings/devices/SelectableDeviceTile-test.tsx @@ -14,9 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { fireEvent, render } from "@testing-library/react"; +import { act, fireEvent, render } from "@testing-library/react"; import React from "react"; -import { act } from "react-dom/test-utils"; import SelectableDeviceTile from "../../../../../src/components/views/settings/devices/SelectableDeviceTile"; import { DeviceType } from "../../../../../src/utils/device/parseUserAgent"; diff --git a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx index cf2edb97e2..22ba02e542 100644 --- a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx +++ b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx @@ -15,8 +15,7 @@ limitations under the License. */ import React from "react"; -import { fireEvent, render, RenderResult } from "@testing-library/react"; -import { act } from "react-dom/test-utils"; +import { act, fireEvent, render, RenderResult } from "@testing-library/react"; import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo"; import { logger } from "matrix-js-sdk/src/logger"; import { DeviceTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning"; diff --git a/test/components/views/spaces/SpaceSettingsVisibilityTab-test.tsx b/test/components/views/spaces/SpaceSettingsVisibilityTab-test.tsx index c09ec8f8ec..eb9ad8fd7e 100644 --- a/test/components/views/spaces/SpaceSettingsVisibilityTab-test.tsx +++ b/test/components/views/spaces/SpaceSettingsVisibilityTab-test.tsx @@ -16,8 +16,8 @@ limitations under the License. import React from "react"; import { mocked } from "jest-mock"; -import { renderIntoDocument, Simulate } from "react-dom/test-utils"; -import { act } from "react-dom/test-utils"; +import { act, Simulate } from "react-dom/test-utils"; +import { fireEvent, render, RenderResult } from "@testing-library/react"; import { EventType, MatrixClient, Room } from "matrix-js-sdk/src/matrix"; import { GuestAccess, HistoryVisibility, JoinRule } from "matrix-js-sdk/src/@types/partials"; @@ -83,25 +83,18 @@ describe("<SpaceSettingsVisibilityTab />", () => { }; const getComponent = (props = {}) => { - const wrapper = renderIntoDocument<HTMLSpanElement>( - // wrap in element so renderIntoDocument can render functional component - <span> - <SpaceSettingsVisibilityTab {...defaultProps} {...props} /> - </span>, - ) as HTMLSpanElement; - return wrapper.children[0]; + return render(<SpaceSettingsVisibilityTab {...defaultProps} {...props} />); }; - const getByTestId = (container: Element, id: string) => container.querySelector(`[data-test-id=${id}]`); - const toggleGuestAccessSection = async (component: Element) => { - const toggleButton = getByTestId(component, "toggle-guest-access-btn")!; - await act(async () => { - Simulate.click(toggleButton); - }); + const toggleGuestAccessSection = async ({ getByTestId }: RenderResult) => { + const toggleButton = getByTestId("toggle-guest-access-btn")!; + fireEvent.click(toggleButton); }; - const getGuestAccessToggle = (component: Element) => component.querySelector('[aria-label="Enable guest access"]'); - const getHistoryVisibilityToggle = (component: Element) => component.querySelector('[aria-label="Preview Space"]'); - const getErrorMessage = (component: Element) => getByTestId(component, "space-settings-error")?.textContent; + const getGuestAccessToggle = ({ container }: RenderResult) => + container.querySelector('[aria-label="Enable guest access"]'); + const getHistoryVisibilityToggle = ({ container }: RenderResult) => + container.querySelector('[aria-label="Preview Space"]'); + const getErrorMessage = ({ getByTestId }: RenderResult) => getByTestId("space-settings-error")?.textContent; beforeEach(() => { (mockMatrixClient.sendStateEvent as jest.Mock).mockClear().mockResolvedValue({}); @@ -113,18 +106,18 @@ describe("<SpaceSettingsVisibilityTab />", () => { }); it("renders container", () => { - const component = getComponent(); - expect(component).toMatchSnapshot(); + const { asFragment } = getComponent(); + expect(asFragment()).toMatchSnapshot(); }); describe("for a private space", () => { const joinRule = JoinRule.Invite; it("does not render addresses section", () => { const space = makeMockSpace(mockMatrixClient, joinRule); - const component = getComponent({ space }); + const { queryByTestId } = getComponent({ space }); - expect(getByTestId(component, "published-address-fieldset")).toBeFalsy(); - expect(getByTestId(component, "local-address-fieldset")).toBeFalsy(); + expect(queryByTestId("published-address-fieldset")).toBeFalsy(); + expect(queryByTestId("local-address-fieldset")).toBeFalsy(); }); }); @@ -152,10 +145,7 @@ describe("<SpaceSettingsVisibilityTab />", () => { expect(guestAccessInput?.getAttribute("aria-checked")).toEqual("true"); - await act(async () => { - Simulate.click(guestAccessInput!); - }); - + fireEvent.click(guestAccessInput!); expect(mockMatrixClient.sendStateEvent).toHaveBeenCalledWith( mockSpaceId, EventType.RoomGuestAccess, @@ -200,17 +190,14 @@ describe("<SpaceSettingsVisibilityTab />", () => { expect(getHistoryVisibilityToggle(component)?.getAttribute("aria-checked")).toEqual("false"); }); - it("updates history visibility on toggle", async () => { + it("updates history visibility on toggle", () => { const space = makeMockSpace(mockMatrixClient, joinRule, guestRule, historyRule); const component = getComponent({ space }); // toggle off because space settings is != WorldReadable expect(getHistoryVisibilityToggle(component)?.getAttribute("aria-checked")).toEqual("false"); - await act(async () => { - Simulate.click(getHistoryVisibilityToggle(component)!); - }); - + fireEvent.click(getHistoryVisibilityToggle(component)!); expect(mockMatrixClient.sendStateEvent).toHaveBeenCalledWith( mockSpaceId, EventType.RoomHistoryVisibility, @@ -243,10 +230,10 @@ describe("<SpaceSettingsVisibilityTab />", () => { it("renders addresses section", () => { const space = makeMockSpace(mockMatrixClient, joinRule, guestRule); - const component = getComponent({ space }); + const { getByTestId } = getComponent({ space }); - expect(getByTestId(component, "published-address-fieldset")).toBeTruthy(); - expect(getByTestId(component, "local-address-fieldset")).toBeTruthy(); + expect(getByTestId("published-address-fieldset")).toBeTruthy(); + expect(getByTestId("local-address-fieldset")).toBeTruthy(); }); }); }); diff --git a/test/components/views/spaces/__snapshots__/SpaceSettingsVisibilityTab-test.tsx.snap b/test/components/views/spaces/__snapshots__/SpaceSettingsVisibilityTab-test.tsx.snap index 48756eed70..8de0ae2c15 100644 --- a/test/components/views/spaces/__snapshots__/SpaceSettingsVisibilityTab-test.tsx.snap +++ b/test/components/views/spaces/__snapshots__/SpaceSettingsVisibilityTab-test.tsx.snap @@ -16,118 +16,119 @@ exports[`<SpaceSettingsVisibilityTab /> for a public space Access renders guest `; exports[`<SpaceSettingsVisibilityTab /> renders container 1`] = ` -<div - class="mx_SettingsTab" -> +<DocumentFragment> <div - class="mx_SettingsTab_heading" + class="mx_SettingsTab" > - Visibility - </div> - - <fieldset - class="mx_SettingsFieldset" - data-test-id="access-fieldset" - > - <legend - class="mx_SettingsFieldset_legend" - > - Access - </legend> <div - class="mx_SettingsFieldset_description" + class="mx_SettingsTab_heading" > - Decide who can view and join mock-space. + Visibility </div> - <label - class="mx_StyledRadioButton mx_JoinRuleSettings_radioButton mx_StyledRadioButton_disabled mx_StyledRadioButton_checked" + <fieldset + class="mx_SettingsFieldset" + data-testid="access-fieldset" > - <input - aria-describedby="joinRule-invite-description" - checked="" - disabled="" - id="joinRule-invite" - name="joinRule" - type="radio" - value="invite" - /> - <div> - <div /> - </div> - <div - class="mx_StyledRadioButton_content" + <legend + class="mx_SettingsFieldset_legend" > - Private (invite only) - </div> + Access + </legend> <div - class="mx_StyledRadioButton_spacer" - /> - </label> - <span - id="joinRule-invite-description" - > - Only invited people can join. - </span> - <label - class="mx_StyledRadioButton mx_JoinRuleSettings_radioButton mx_StyledRadioButton_disabled" - > - <input - aria-describedby="joinRule-public-description" - disabled="" - id="joinRule-public" - name="joinRule" - type="radio" - value="public" - /> - <div> - <div /> - </div> - <div - class="mx_StyledRadioButton_content" + class="mx_SettingsFieldset_description" > - Public + Decide who can view and join mock-space. </div> - <div - class="mx_StyledRadioButton_spacer" - /> - </label> - <span - id="joinRule-public-description" - > - Anyone can find and join. - </span> - <div - class="mx_SettingsTab_toggleWithDescription" - > - <div - class="mx_SettingsFlag" + <label + class="mx_StyledRadioButton mx_JoinRuleSettings_radioButton mx_StyledRadioButton_disabled mx_StyledRadioButton_checked" > - <span - class="mx_SettingsFlag_label" - > - Preview Space - </span> - <div - aria-checked="true" - aria-disabled="false" - aria-label="Preview Space" - class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on mx_ToggleSwitch_enabled" - role="switch" - tabindex="0" - > - <div - class="mx_ToggleSwitch_ball" - /> + <input + aria-describedby="joinRule-invite-description" + checked="" + disabled="" + id="joinRule-invite" + name="joinRule" + type="radio" + value="invite" + /> + <div> + <div /> </div> + <div + class="mx_StyledRadioButton_content" + > + Private (invite only) + </div> + <div + class="mx_StyledRadioButton_spacer" + /> + </label> + <span + id="joinRule-invite-description" + > + Only invited people can join. + </span> + <label + class="mx_StyledRadioButton mx_JoinRuleSettings_radioButton mx_StyledRadioButton_disabled" + > + <input + aria-describedby="joinRule-public-description" + disabled="" + id="joinRule-public" + name="joinRule" + type="radio" + value="public" + /> + <div> + <div /> + </div> + <div + class="mx_StyledRadioButton_content" + > + Public + </div> + <div + class="mx_StyledRadioButton_spacer" + /> + </label> + <span + id="joinRule-public-description" + > + Anyone can find and join. + </span> + <div + class="mx_SettingsTab_toggleWithDescription" + > + <div + class="mx_SettingsFlag" + > + <span + class="mx_SettingsFlag_label" + > + Preview Space + </span> + <div + aria-checked="true" + aria-disabled="false" + aria-label="Preview Space" + class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on mx_ToggleSwitch_enabled" + role="switch" + tabindex="0" + > + <div + class="mx_ToggleSwitch_ball" + /> + </div> + </div> + <p> + Allow people to preview your space before they join. + <br /> + <b> + Recommended for public spaces. + </b> + </p> </div> - <p> - Allow people to preview your space before they join. - <br /> - <b> - Recommended for public spaces. - </b> - </p> - </div> - </fieldset> -</div> + </fieldset> + </div> +</DocumentFragment> `; diff --git a/test/components/views/typography/Heading-test.tsx b/test/components/views/typography/Heading-test.tsx index 74dc12c782..62006ccebd 100644 --- a/test/components/views/typography/Heading-test.tsx +++ b/test/components/views/typography/Heading-test.tsx @@ -14,37 +14,32 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { render } from "@testing-library/react"; import React from "react"; -import { renderIntoDocument } from "react-dom/test-utils"; import Heading from "../../../../src/components/views/typography/Heading"; describe("<Heading />", () => { const defaultProps = { - size: "h1", - children: <div>test</div>, - ["data-test-id"]: "test", - className: "test", + "size": "h1", + "children": <div>test</div>, + "data-testid": "test", + "className": "test", } as any; const getComponent = (props = {}) => { - const wrapper = renderIntoDocument<HTMLDivElement>( - <div> - <Heading {...defaultProps} {...props} /> - </div>, - ) as HTMLDivElement; - return wrapper.children[0]; + return render(<Heading {...defaultProps} {...props} />); }; it("renders h1 with correct attributes", () => { - expect(getComponent({ size: "h1" })).toMatchSnapshot(); + expect(getComponent({ size: "h1" }).asFragment()).toMatchSnapshot(); }); it("renders h2 with correct attributes", () => { - expect(getComponent({ size: "h2" })).toMatchSnapshot(); + expect(getComponent({ size: "h2" }).asFragment()).toMatchSnapshot(); }); it("renders h3 with correct attributes", () => { - expect(getComponent({ size: "h3" })).toMatchSnapshot(); + expect(getComponent({ size: "h3" }).asFragment()).toMatchSnapshot(); }); it("renders h4 with correct attributes", () => { - expect(getComponent({ size: "h4" })).toMatchSnapshot(); + expect(getComponent({ size: "h4" }).asFragment()).toMatchSnapshot(); }); }); diff --git a/test/components/views/typography/__snapshots__/Heading-test.tsx.snap b/test/components/views/typography/__snapshots__/Heading-test.tsx.snap index d9511fd4d9..490f84148c 100644 --- a/test/components/views/typography/__snapshots__/Heading-test.tsx.snap +++ b/test/components/views/typography/__snapshots__/Heading-test.tsx.snap @@ -1,45 +1,53 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`<Heading /> renders h1 with correct attributes 1`] = ` -<h1 - class="mx_Heading_h1 test" - data-test-id="test" -> - <div> - test - </div> -</h1> +<DocumentFragment> + <h1 + class="mx_Heading_h1 test" + data-testid="test" + > + <div> + test + </div> + </h1> +</DocumentFragment> `; exports[`<Heading /> renders h2 with correct attributes 1`] = ` -<h2 - class="mx_Heading_h2 test" - data-test-id="test" -> - <div> - test - </div> -</h2> +<DocumentFragment> + <h2 + class="mx_Heading_h2 test" + data-testid="test" + > + <div> + test + </div> + </h2> +</DocumentFragment> `; exports[`<Heading /> renders h3 with correct attributes 1`] = ` -<h3 - class="mx_Heading_h3 test" - data-test-id="test" -> - <div> - test - </div> -</h3> +<DocumentFragment> + <h3 + class="mx_Heading_h3 test" + data-testid="test" + > + <div> + test + </div> + </h3> +</DocumentFragment> `; exports[`<Heading /> renders h4 with correct attributes 1`] = ` -<h4 - class="mx_Heading_h4 test" - data-test-id="test" -> - <div> - test - </div> -</h4> +<DocumentFragment> + <h4 + class="mx_Heading_h4 test" + data-testid="test" + > + <div> + test + </div> + </h4> +</DocumentFragment> `; diff --git a/test/useTopic-test.tsx b/test/useTopic-test.tsx index 2f7e0ac757..2fffa3aff9 100644 --- a/test/useTopic-test.tsx +++ b/test/useTopic-test.tsx @@ -16,8 +16,7 @@ limitations under the License. import React from "react"; import { Room } from "matrix-js-sdk/src/models/room"; -import { act } from "react-dom/test-utils"; -import { render, screen } from "@testing-library/react"; +import { act, render, screen } from "@testing-library/react"; import { useTopic } from "../src/hooks/room/useTopic"; import { mkEvent, stubClient } from "./test-utils"; diff --git a/test/utils/exportUtils/HTMLExport-test.ts b/test/utils/exportUtils/HTMLExport-test.ts index 7e1f7a53b7..bbdfa9e647 100644 --- a/test/utils/exportUtils/HTMLExport-test.ts +++ b/test/utils/exportUtils/HTMLExport-test.ts @@ -315,4 +315,45 @@ describe("HTMLExport", () => { expect(fileName).not.toMatch(/^files\/hello/); } }); + + it("should add link to next and previous file", async () => { + const exporter = new HTMLExporter( + room, + ExportType.LastNMessages, + { + attachmentsIncluded: false, + maxSize: 1_024 * 1_024, + }, + () => {}, + ); + + // test link to the first page + //@ts-ignore private access + exporter.wrapHTML("", 0, 3).then((res) => { + expect(res).not.toContain("Previous group of messages"); + expect(res).toContain( + '<div style="text-align:center;margin:10px"><a href="./messages2.html" style="font-weight:bold">Next group of messages</a></div>', + ); + }); + + // test link for a middle page + //@ts-ignore private access + exporter.wrapHTML("", 1, 3).then((res) => { + expect(res).toContain( + '<div style="text-align:center"><a href="./messages.html" style="font-weight:bold">Previous group of messages</a></div>', + ); + expect(res).toContain( + '<div style="text-align:center;margin:10px"><a href="./messages3.html" style="font-weight:bold">Next group of messages</a></div>', + ); + }); + + // test link for last page + //@ts-ignore private access + exporter.wrapHTML("", 2, 3).then((res) => { + expect(res).toContain( + '<div style="text-align:center"><a href="./messages2.html" style="font-weight:bold">Previous group of messages</a></div>', + ); + expect(res).not.toContain("Next group of messages"); + }); + }); }); diff --git a/test/utils/exportUtils/__snapshots__/HTMLExport-test.ts.snap b/test/utils/exportUtils/__snapshots__/HTMLExport-test.ts.snap index c47170d3ed..e4b6872ebf 100644 --- a/test/utils/exportUtils/__snapshots__/HTMLExport-test.ts.snap +++ b/test/utils/exportUtils/__snapshots__/HTMLExport-test.ts.snap @@ -40,6 +40,7 @@ exports[`HTMLExport should export 1`] = ` <div class="mx_RoomHeader_topic" dir="auto"> </div> </div> </div> + <div class="mx_MainSplit"> <div class="mx_RoomView_body"> <div @@ -59,12 +60,12 @@ exports[`HTMLExport should export 1`] = ` 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> + <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> @@ -77,6 +78,7 @@ exports[`HTMLExport should export 1`] = ` </div> </div> </div> + </main> </div> </div> diff --git a/test/voice-broadcast/components/atoms/VoiceBroadcastHeader-test.tsx b/test/voice-broadcast/components/atoms/VoiceBroadcastHeader-test.tsx index cd26723226..988d2b52e6 100644 --- a/test/voice-broadcast/components/atoms/VoiceBroadcastHeader-test.tsx +++ b/test/voice-broadcast/components/atoms/VoiceBroadcastHeader-test.tsx @@ -12,7 +12,6 @@ limitations under the License. */ import React from "react"; -import { Container } from "react-dom"; import { MatrixClient, Room, RoomMember } from "matrix-js-sdk/src/matrix"; import { render, RenderResult } from "@testing-library/react"; @@ -33,7 +32,7 @@ describe("VoiceBroadcastHeader", () => { let client: MatrixClient; let room: Room; const sender = new RoomMember(roomId, userId); - let container: Container; + let container: RenderResult["container"]; const renderHeader = (live: VoiceBroadcastLiveness, showBroadcast?: boolean, buffering?: boolean): RenderResult => { return render( diff --git a/yarn.lock b/yarn.lock index d167242f21..2fa60a1f30 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2336,13 +2336,6 @@ hoist-non-react-statics "^3.3.0" redux "^4.0.0" -"@types/react-test-renderer@^17.0.1": - version "17.0.2" - resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-17.0.2.tgz#5f800a39b12ac8d2a2149e7e1885215bcf4edbbf" - integrity sha512-+F1KONQTBHDBBhbHuT2GNydeMpPuviduXIVJRB7Y4nma4NR5DrTJfMMZ+jbhEHbpwL+Uqhs1WXh4KHiyrtYTPg== - dependencies: - "@types/react" "^17" - "@types/react-transition-group@^4.4.0": version "4.4.5" resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416" @@ -2364,7 +2357,7 @@ resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== -"@types/sanitize-html@^2.3.1": +"@types/sanitize-html@2.8.0": version "2.8.0" resolved "https://registry.yarnpkg.com/@types/sanitize-html/-/sanitize-html-2.8.0.tgz#c53d3114d832734fc299568a3458a49f9edc1eef" integrity sha512-Uih6caOm3DsBYnVGOYn0A9NoTNe1c4aPStmHC/YA2JrpP9kx//jzaRcIklFvSpvVQEcpl/ZCr4DgISSf/YxTvg== @@ -3846,15 +3839,6 @@ dom-helpers@^5.0.1: "@babel/runtime" "^7.8.7" csstype "^3.0.2" -dom-serializer@^1.0.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" - integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.2.0" - entities "^2.0.0" - dom-serializer@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" @@ -3864,7 +3848,7 @@ dom-serializer@^2.0.0: domhandler "^5.0.2" entities "^4.2.0" -domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: +domelementtype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== @@ -3876,13 +3860,6 @@ domexception@^4.0.0: dependencies: webidl-conversions "^7.0.0" -domhandler@^4.0.0, domhandler@^4.2.0: - version "4.3.1" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" - integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== - dependencies: - domelementtype "^2.2.0" - domhandler@^5.0.1, domhandler@^5.0.2, domhandler@^5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" @@ -3890,15 +3867,6 @@ domhandler@^5.0.1, domhandler@^5.0.2, domhandler@^5.0.3: dependencies: domelementtype "^2.3.0" -domutils@^2.5.2: - version "2.8.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" - integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== - dependencies: - dom-serializer "^1.0.1" - domelementtype "^2.2.0" - domhandler "^4.2.0" - domutils@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c" @@ -3980,11 +3948,6 @@ enquirer@^2.3.6: dependencies: ansi-colors "^4.1.1" -entities@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" - integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== - entities@^4.2.0, entities@^4.3.0, entities@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174" @@ -5137,16 +5100,6 @@ html-tags@^3.2.0: resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.2.0.tgz#dbb3518d20b726524e4dd43de397eb0a95726961" integrity sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg== -htmlparser2@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" - integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.0.0" - domutils "^2.5.2" - entities "^2.0.0" - htmlparser2@^8.0.0, htmlparser2@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.1.tgz#abaa985474fcefe269bc761a779b544d7196d010" @@ -7535,7 +7488,7 @@ react-shallow-renderer@^16.13.1: object-assign "^4.1.1" react-is "^16.12.0 || ^17.0.0 || ^18.0.0" -react-test-renderer@^17.0.0, react-test-renderer@^17.0.2: +react-test-renderer@^17.0.0: version "17.0.2" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.2.tgz#4cd4ae5ef1ad5670fc0ef776e8cc7e1231d9866c" integrity sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ== @@ -7863,14 +7816,14 @@ sanitize-filename@^1.6.3: dependencies: truncate-utf8-bytes "^1.0.0" -sanitize-html@^2.3.2: - version "2.7.3" - resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.7.3.tgz#166c868444ee4f9fd7352ac8c63fa86c343fc2bd" - integrity sha512-jMaHG29ak4miiJ8wgqA1849iInqORgNv7SLfSw9LtfOhEUQ1C0YHKH73R+hgyufBW9ZFeJrb057k9hjlfBCVlw== +sanitize-html@2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.8.0.tgz#651d1d0e5b2d61b4ec6147cc46f6d6680eef98ce" + integrity sha512-ZsGyc6avnqgvEm3eMKrcy8xa7WM1MrGrfkGsUgQee2CU+vg3PCfNCexXwBDF/6dEPvaQ4k/QqRjnYKHL8xgNjg== dependencies: deepmerge "^4.2.2" escape-string-regexp "^4.0.0" - htmlparser2 "^6.0.0" + htmlparser2 "^8.0.0" is-plain-object "^5.0.0" parse-srcset "^1.0.2" postcss "^8.3.11"