Merge branch 'develop' into staging
This commit is contained in:
commit
9d6ad99bfc
60 changed files with 870 additions and 717 deletions
2
.github/workflows/pull_request.yaml
vendored
2
.github/workflows/pull_request.yaml
vendored
|
@ -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 }}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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: "";
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -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";
|
||||
|
||||
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 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>;
|
||||
}
|
||||
export function DecryptionFailureBody({ mxEvent }: Partial<IBodyProps>): JSX.Element {
|
||||
return <div className="mx_DecryptionFailureBody mx_EventTile_content">{getErrorMessage(mxEvent)}</div>;
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
)}
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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 })}
|
||||
>
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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: [],
|
||||
|
|
|
@ -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">
|
||||
${
|
||||
currentPage == 0
|
||||
? `<div class="mx_NewRoomIntro">
|
||||
${roomAvatar}
|
||||
<h2> ${this.room.name} </h2>
|
||||
<p> ${createdText} <br/><br/> ${exportedText} </p>
|
||||
<br/>
|
||||
<p> ${topicText} </p>
|
||||
</div>
|
||||
</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));
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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
|
||||
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"
|
||||
>
|
||||
<div
|
||||
className="mx_MapFallback_bg"
|
||||
class="mx_MapFallback_bg"
|
||||
/>
|
||||
<div
|
||||
className="mx_MapFallback_icon"
|
||||
class="mx_MapFallback_icon"
|
||||
/>
|
||||
<span
|
||||
className="mx_BeaconViewDialog_mapFallbackMessage"
|
||||
class="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]}
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||
data-testid="beacon-view-dialog-fallback-close"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
tabindex="0"
|
||||
>
|
||||
Close
|
||||
</div>
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
</MapFallback>,
|
||||
<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>,
|
||||
]
|
||||
`;
|
||||
|
||||
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
|
||||
class="mx_BeaconStatus_description"
|
||||
>
|
||||
<span
|
||||
class="mx_BeaconStatus_label"
|
||||
>
|
||||
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>
|
||||
`;
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<ExternalLink /> renders link correctly 1`] = `
|
||||
<DocumentFragment>
|
||||
<a
|
||||
class="mx_ExternalLink myCustomClass"
|
||||
data-test-id="test"
|
||||
data-testid="test"
|
||||
href="test.com"
|
||||
rel="noopener"
|
||||
target="_self"
|
||||
|
@ -18,12 +19,14 @@ exports[`<ExternalLink /> renders link correctly 1`] = `
|
|||
class="mx_ExternalLink_icon"
|
||||
/>
|
||||
</a>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<ExternalLink /> renders plain text link correctly 1`] = `
|
||||
<DocumentFragment>
|
||||
<a
|
||||
class="mx_ExternalLink myCustomClass"
|
||||
data-test-id="test"
|
||||
data-testid="test"
|
||||
href="test.com"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
|
@ -33,4 +36,5 @@ exports[`<ExternalLink /> renders plain text link correctly 1`] = `
|
|||
class="mx_ExternalLink_icon"
|
||||
/>
|
||||
</a>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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>
|
||||
`;
|
|
@ -10,6 +10,12 @@ 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>
|
||||
<span
|
||||
class="mx_Caption"
|
||||
>
|
||||
Ended a poll
|
||||
</span>
|
||||
<div
|
||||
class="mx_MPollBody"
|
||||
>
|
||||
|
@ -105,4 +111,5 @@ exports[`<MPollEndBody /> when poll start event exists in current timeline rende
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<SettingsFieldset /> renders fieldset with plain text description 1`] = `
|
||||
<DocumentFragment>
|
||||
<fieldset
|
||||
class="mx_SettingsFieldset"
|
||||
data-test-id="test"
|
||||
data-testid="test"
|
||||
>
|
||||
<legend
|
||||
class="mx_SettingsFieldset_legend"
|
||||
|
@ -19,12 +20,14 @@ exports[`<SettingsFieldset /> renders fieldset with plain text description 1`] =
|
|||
test
|
||||
</div>
|
||||
</fieldset>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<SettingsFieldset /> renders fieldset with react description 1`] = `
|
||||
<DocumentFragment>
|
||||
<fieldset
|
||||
class="mx_SettingsFieldset"
|
||||
data-test-id="test"
|
||||
data-testid="test"
|
||||
>
|
||||
<legend
|
||||
class="mx_SettingsFieldset_legend"
|
||||
|
@ -47,12 +50,14 @@ exports[`<SettingsFieldset /> renders fieldset with react description 1`] = `
|
|||
test
|
||||
</div>
|
||||
</fieldset>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<SettingsFieldset /> renders fieldset without description 1`] = `
|
||||
<DocumentFragment>
|
||||
<fieldset
|
||||
class="mx_SettingsFieldset"
|
||||
data-test-id="test"
|
||||
data-testid="test"
|
||||
>
|
||||
<legend
|
||||
class="mx_SettingsFieldset_legend"
|
||||
|
@ -63,4 +68,5 @@ exports[`<SettingsFieldset /> renders fieldset without description 1`] = `
|
|||
test
|
||||
</div>
|
||||
</fieldset>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -16,6 +16,7 @@ exports[`<SpaceSettingsVisibilityTab /> for a public space Access renders guest
|
|||
`;
|
||||
|
||||
exports[`<SpaceSettingsVisibilityTab /> renders container 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_SettingsTab"
|
||||
>
|
||||
|
@ -24,10 +25,9 @@ exports[`<SpaceSettingsVisibilityTab /> renders container 1`] = `
|
|||
>
|
||||
Visibility
|
||||
</div>
|
||||
|
||||
<fieldset
|
||||
class="mx_SettingsFieldset"
|
||||
data-test-id="access-fieldset"
|
||||
data-testid="access-fieldset"
|
||||
>
|
||||
<legend
|
||||
class="mx_SettingsFieldset_legend"
|
||||
|
@ -130,4 +130,5 @@ exports[`<SpaceSettingsVisibilityTab /> renders container 1`] = `
|
|||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,45 +1,53 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<Heading /> renders h1 with correct attributes 1`] = `
|
||||
<DocumentFragment>
|
||||
<h1
|
||||
class="mx_Heading_h1 test"
|
||||
data-test-id="test"
|
||||
data-testid="test"
|
||||
>
|
||||
<div>
|
||||
test
|
||||
</div>
|
||||
</h1>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<Heading /> renders h2 with correct attributes 1`] = `
|
||||
<DocumentFragment>
|
||||
<h2
|
||||
class="mx_Heading_h2 test"
|
||||
data-test-id="test"
|
||||
data-testid="test"
|
||||
>
|
||||
<div>
|
||||
test
|
||||
</div>
|
||||
</h2>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<Heading /> renders h3 with correct attributes 1`] = `
|
||||
<DocumentFragment>
|
||||
<h3
|
||||
class="mx_Heading_h3 test"
|
||||
data-test-id="test"
|
||||
data-testid="test"
|
||||
>
|
||||
<div>
|
||||
test
|
||||
</div>
|
||||
</h3>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<Heading /> renders h4 with correct attributes 1`] = `
|
||||
<DocumentFragment>
|
||||
<h4
|
||||
class="mx_Heading_h4 test"
|
||||
data-test-id="test"
|
||||
data-testid="test"
|
||||
>
|
||||
<div>
|
||||
test
|
||||
</div>
|
||||
</h4>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
@ -77,6 +78,7 @@ exports[`HTMLExport should export 1`] = `
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -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(
|
||||
|
|
63
yarn.lock
63
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"
|
||||
|
|
Loading…
Reference in a new issue