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:
|
jobs:
|
||||||
action:
|
action:
|
||||||
uses: matrix-org/matrix-js-sdk/.github/workflows/pull_request.yaml@develop
|
uses: matrix-org/matrix-js-sdk/.github/workflows/pull_request.yaml@develop
|
||||||
with:
|
|
||||||
labels: "T-Defect,T-Enhancement,T-Task"
|
|
||||||
secrets:
|
secrets:
|
||||||
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
|
|
|
@ -111,7 +111,7 @@
|
||||||
"react-transition-group": "^4.4.1",
|
"react-transition-group": "^4.4.1",
|
||||||
"rfc4648": "^1.4.0",
|
"rfc4648": "^1.4.0",
|
||||||
"sanitize-filename": "^1.6.3",
|
"sanitize-filename": "^1.6.3",
|
||||||
"sanitize-html": "^2.3.2",
|
"sanitize-html": "2.8.0",
|
||||||
"tar-js": "^0.3.0",
|
"tar-js": "^0.3.0",
|
||||||
"ua-parser-js": "^1.0.2",
|
"ua-parser-js": "^1.0.2",
|
||||||
"url": "^0.11.0",
|
"url": "^0.11.0",
|
||||||
|
@ -167,9 +167,8 @@
|
||||||
"@types/react": "17.0.49",
|
"@types/react": "17.0.49",
|
||||||
"@types/react-beautiful-dnd": "^13.0.0",
|
"@types/react-beautiful-dnd": "^13.0.0",
|
||||||
"@types/react-dom": "17.0.17",
|
"@types/react-dom": "17.0.17",
|
||||||
"@types/react-test-renderer": "^17.0.1",
|
|
||||||
"@types/react-transition-group": "^4.4.0",
|
"@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/tar-js": "^0.3.2",
|
||||||
"@types/ua-parser-js": "^0.7.36",
|
"@types/ua-parser-js": "^0.7.36",
|
||||||
"@types/zxcvbn": "^4.4.0",
|
"@types/zxcvbn": "^4.4.0",
|
||||||
|
@ -212,7 +211,6 @@
|
||||||
"postcss-scss": "^4.0.4",
|
"postcss-scss": "^4.0.4",
|
||||||
"prettier": "2.8.0",
|
"prettier": "2.8.0",
|
||||||
"raw-loader": "^4.0.2",
|
"raw-loader": "^4.0.2",
|
||||||
"react-test-renderer": "^17.0.2",
|
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"stylelint": "^14.9.1",
|
"stylelint": "^14.9.1",
|
||||||
"stylelint-config-prettier": "^9.0.4",
|
"stylelint-config-prettier": "^9.0.4",
|
||||||
|
|
|
@ -38,9 +38,12 @@ limitations under the License.
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
display: block;
|
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 */
|
/* center to first line */
|
||||||
position: relative;
|
|
||||||
top: 0.15em;
|
top: 0.15em;
|
||||||
|
background-color: $background;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: "";
|
content: "";
|
||||||
|
|
|
@ -28,4 +28,9 @@ limitations under the License.
|
||||||
margin-bottom: $spacing-16;
|
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 />}
|
{mapDisplayError && <MapError error={mapDisplayError.message as LocationShareError} isMinimised />}
|
||||||
{!centerGeoUri && !mapDisplayError && (
|
{!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>
|
<span className="mx_BeaconViewDialog_mapFallbackMessage">{_t("No live locations")}</span>
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
kind="primary"
|
kind="primary"
|
||||||
onClick={onFinished}
|
onClick={onFinished}
|
||||||
data-test-id="beacon-view-dialog-fallback-close"
|
data-testid="beacon-view-dialog-fallback-close"
|
||||||
>
|
>
|
||||||
{_t("Close")}
|
{_t("Close")}
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
|
@ -179,7 +179,7 @@ const BeaconViewDialog: React.FC<IProps> = ({ initialFocusedBeacon, roomId, matr
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
kind="primary"
|
kind="primary"
|
||||||
onClick={() => setSidebarOpen(true)}
|
onClick={() => setSidebarOpen(true)}
|
||||||
data-test-id="beacon-view-dialog-open-sidebar"
|
data-testid="beacon-view-dialog-open-sidebar"
|
||||||
className="mx_BeaconViewDialog_viewListButton"
|
className="mx_BeaconViewDialog_viewListButton"
|
||||||
>
|
>
|
||||||
<LiveLocationIcon height={12} />
|
<LiveLocationIcon height={12} />
|
||||||
|
|
|
@ -14,14 +14,19 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
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 { _t } from "../../../languageHandler";
|
||||||
import { IBodyProps } from "./IBodyProps";
|
import { IBodyProps } from "./IBodyProps";
|
||||||
|
|
||||||
// A placeholder element for messages that could not be decrypted
|
function getErrorMessage(mxEvent?: MatrixEvent): string {
|
||||||
export default class DecryptionFailureBody extends React.Component<Partial<IBodyProps>> {
|
return mxEvent?.isEncryptedDisabledForUnverifiedDevices
|
||||||
public render(): ReactNode {
|
? _t("The sender has blocked you from receiving this message")
|
||||||
return <div className="mx_DecryptionFailureBody mx_EventTile_content">{_t("Unable to decrypt message")}</div>;
|
: _t("Unable to decrypt message");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A placeholder element for messages that could not be decrypted
|
||||||
|
export function DecryptionFailureBody({ mxEvent }: Partial<IBodyProps>): JSX.Element {
|
||||||
|
return <div className="mx_DecryptionFailureBody mx_EventTile_content">{getErrorMessage(mxEvent)}</div>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,12 +182,14 @@ export default class MPollBody extends React.Component<IBodyProps, IState> {
|
||||||
private addListeners(): void {
|
private addListeners(): void {
|
||||||
this.state.poll?.on(PollEvent.Responses, this.onResponsesChange);
|
this.state.poll?.on(PollEvent.Responses, this.onResponsesChange);
|
||||||
this.state.poll?.on(PollEvent.End, this.onRelationsChange);
|
this.state.poll?.on(PollEvent.End, this.onRelationsChange);
|
||||||
|
this.state.poll?.on(PollEvent.UndecryptableRelations, this.render.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
private removeListeners(): void {
|
private removeListeners(): void {
|
||||||
if (this.state.poll) {
|
if (this.state.poll) {
|
||||||
this.state.poll.off(PollEvent.Responses, this.onResponsesChange);
|
this.state.poll.off(PollEvent.Responses, this.onResponsesChange);
|
||||||
this.state.poll.off(PollEvent.End, this.onRelationsChange);
|
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);
|
const showResults = poll.isEnded || (disclosed && myVote !== undefined);
|
||||||
|
|
||||||
let totalText: string;
|
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 });
|
totalText = _t("Final result based on %(count)s votes", { count: totalVotes });
|
||||||
} else if (!disclosed) {
|
} else if (!disclosed) {
|
||||||
totalText = _t("Results will be visible when the poll is ended");
|
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 { Icon as PollIcon } from "../../../../res/img/element-icons/room/composer/poll.svg";
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
|
import { _t } from "../../../languageHandler";
|
||||||
import { textForEvent } from "../../../TextForEvent";
|
import { textForEvent } from "../../../TextForEvent";
|
||||||
|
import { Caption } from "../typography/Caption";
|
||||||
import { IBodyProps } from "./IBodyProps";
|
import { IBodyProps } from "./IBodyProps";
|
||||||
import MPollBody from "./MPollBody";
|
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 MLocationBody from "./MLocationBody";
|
||||||
import MjolnirBody from "./MjolnirBody";
|
import MjolnirBody from "./MjolnirBody";
|
||||||
import MBeaconBody from "./MBeaconBody";
|
import MBeaconBody from "./MBeaconBody";
|
||||||
import DecryptionFailureBody from "./DecryptionFailureBody";
|
import { DecryptionFailureBody } from "./DecryptionFailureBody";
|
||||||
import { GetRelationsForEvent, IEventTileOps } from "../rooms/EventTile";
|
import { GetRelationsForEvent, IEventTileOps } from "../rooms/EventTile";
|
||||||
import { VoiceBroadcastBody, VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "../../../voice-broadcast";
|
import { VoiceBroadcastBody, VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "../../../voice-broadcast";
|
||||||
|
|
||||||
|
|
|
@ -399,7 +399,7 @@ export default class AliasSettings extends React.Component<IProps, IState> {
|
||||||
return (
|
return (
|
||||||
<div className="mx_AliasSettings">
|
<div className="mx_AliasSettings">
|
||||||
<SettingsFieldset
|
<SettingsFieldset
|
||||||
data-test-id="published-address-fieldset"
|
data-testid="published-address-fieldset"
|
||||||
legend={_t("Published Addresses")}
|
legend={_t("Published Addresses")}
|
||||||
description={
|
description={
|
||||||
<>
|
<>
|
||||||
|
@ -450,7 +450,7 @@ export default class AliasSettings extends React.Component<IProps, IState> {
|
||||||
/>
|
/>
|
||||||
</SettingsFieldset>
|
</SettingsFieldset>
|
||||||
<SettingsFieldset
|
<SettingsFieldset
|
||||||
data-test-id="local-address-fieldset"
|
data-testid="local-address-fieldset"
|
||||||
legend={_t("Local Addresses")}
|
legend={_t("Local Addresses")}
|
||||||
description={
|
description={
|
||||||
isSpaceRoom
|
isSpaceRoom
|
||||||
|
|
|
@ -35,7 +35,7 @@ import { Layout } from "../../../settings/enums/Layout";
|
||||||
import { formatTime } from "../../../DateUtils";
|
import { formatTime } from "../../../DateUtils";
|
||||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import DecryptionFailureBody from "../messages/DecryptionFailureBody";
|
import { DecryptionFailureBody } from "../messages/DecryptionFailureBody";
|
||||||
import { E2EState } from "./E2EIcon";
|
import { E2EState } from "./E2EIcon";
|
||||||
import RoomAvatar from "../avatars/RoomAvatar";
|
import RoomAvatar from "../avatars/RoomAvatar";
|
||||||
import MessageContextMenu from "../context_menus/MessageContextMenu";
|
import MessageContextMenu from "../context_menus/MessageContextMenu";
|
||||||
|
@ -1270,7 +1270,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
|
||||||
{this.props.mxEvent.isRedacted() ? (
|
{this.props.mxEvent.isRedacted() ? (
|
||||||
<RedactedBody mxEvent={this.props.mxEvent} />
|
<RedactedBody mxEvent={this.props.mxEvent} />
|
||||||
) : this.props.mxEvent.isDecryptionFailure() ? (
|
) : this.props.mxEvent.isDecryptionFailure() ? (
|
||||||
<DecryptionFailureBody />
|
<DecryptionFailureBody mxEvent={this.props.mxEvent} />
|
||||||
) : (
|
) : (
|
||||||
MessagePreviewStore.instance.generatePreviewForEvent(this.props.mxEvent)
|
MessagePreviewStore.instance.generatePreviewForEvent(this.props.mxEvent)
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -89,6 +89,8 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
|
||||||
"useOnlyCurrentProfiles",
|
"useOnlyCurrentProfiles",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
private static ROOM_DIRECTORY_SETTINGS = ["SpotlightSearch.showNsfwPublicRooms"];
|
||||||
|
|
||||||
private static GENERAL_SETTINGS = [
|
private static GENERAL_SETTINGS = [
|
||||||
"promptBeforeInviteUnknownUsers",
|
"promptBeforeInviteUnknownUsers",
|
||||||
// Start automatically after startup (electron-only)
|
// Start automatically after startup (electron-only)
|
||||||
|
@ -234,6 +236,11 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
|
||||||
{this.renderGroup(PreferencesUserSettingsTab.TIMELINE_SETTINGS)}
|
{this.renderGroup(PreferencesUserSettingsTab.TIMELINE_SETTINGS)}
|
||||||
</div>
|
</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">
|
<div className="mx_SettingsTab_section">
|
||||||
<span className="mx_SettingsTab_subheading">{_t("General")}</span>
|
<span className="mx_SettingsTab_subheading">{_t("General")}</span>
|
||||||
{this.renderGroup(PreferencesUserSettingsTab.GENERAL_SETTINGS)}
|
{this.renderGroup(PreferencesUserSettingsTab.GENERAL_SETTINGS)}
|
||||||
|
|
|
@ -93,7 +93,7 @@ const SpaceSettingsVisibilityTab: React.FC<IProps> = ({ matrixClient: cli, space
|
||||||
advancedSection = (
|
advancedSection = (
|
||||||
<div>
|
<div>
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
data-test-id="toggle-guest-access-btn"
|
data-testid="toggle-guest-access-btn"
|
||||||
onClick={toggleAdvancedSection}
|
onClick={toggleAdvancedSection}
|
||||||
kind="link"
|
kind="link"
|
||||||
className="mx_SettingsTab_showAdvanced"
|
className="mx_SettingsTab_showAdvanced"
|
||||||
|
@ -141,13 +141,13 @@ const SpaceSettingsVisibilityTab: React.FC<IProps> = ({ matrixClient: cli, space
|
||||||
<div className="mx_SettingsTab_heading">{_t("Visibility")}</div>
|
<div className="mx_SettingsTab_heading">{_t("Visibility")}</div>
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<div data-test-id="space-settings-error" className="mx_SpaceRoomView_errorText">
|
<div data-testid="space-settings-error" className="mx_SpaceRoomView_errorText">
|
||||||
{error}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<SettingsFieldset
|
<SettingsFieldset
|
||||||
data-test-id="access-fieldset"
|
data-testid="access-fieldset"
|
||||||
legend={_t("Access")}
|
legend={_t("Access")}
|
||||||
description={_t("Decide who can view and join %(spaceName)s.", { spaceName: space.name })}
|
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 SettingsStore from "../settings/SettingsStore";
|
||||||
import { Protocols } from "../utils/DirectoryUtils";
|
import { Protocols } from "../utils/DirectoryUtils";
|
||||||
import { useLatestResult } from "./useLatestResult";
|
import { useLatestResult } from "./useLatestResult";
|
||||||
|
import { useSettingValue } from "./useSettings";
|
||||||
|
|
||||||
export const ALL_ROOMS = "ALL_ROOMS";
|
export const ALL_ROOMS = "ALL_ROOMS";
|
||||||
const LAST_SERVER_KEY = "mx_last_room_directory_server";
|
const LAST_SERVER_KEY = "mx_last_room_directory_server";
|
||||||
|
@ -38,6 +39,10 @@ export interface IPublicRoomsOpts {
|
||||||
|
|
||||||
let thirdParty: Protocols;
|
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 = (): {
|
export const usePublicRoomDirectory = (): {
|
||||||
ready: boolean;
|
ready: boolean;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
|
@ -58,6 +63,8 @@ export const usePublicRoomDirectory = (): {
|
||||||
|
|
||||||
const [updateQuery, updateResult] = useLatestResult<IRoomDirectoryOptions, IPublicRoomsChunkRoom[]>(setPublicRooms);
|
const [updateQuery, updateResult] = useLatestResult<IRoomDirectoryOptions, IPublicRoomsChunkRoom[]>(setPublicRooms);
|
||||||
|
|
||||||
|
const showNsfwPublicRooms = useSettingValue<boolean>("SpotlightSearch.showNsfwPublicRooms");
|
||||||
|
|
||||||
async function initProtocols(): Promise<void> {
|
async function initProtocols(): Promise<void> {
|
||||||
if (!MatrixClientPeg.get()) {
|
if (!MatrixClientPeg.get()) {
|
||||||
// We may not have a client yet when invoked from welcome page
|
// We may not have a client yet when invoked from welcome page
|
||||||
|
@ -108,7 +115,7 @@ export const usePublicRoomDirectory = (): {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const { chunk } = await MatrixClientPeg.get().publicRooms(opts);
|
const { chunk } = await MatrixClientPeg.get().publicRooms(opts);
|
||||||
updateResult(opts, chunk);
|
updateResult(opts, showNsfwPublicRooms ? chunk : chunk.filter(cheapNsfwFilter));
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Could not fetch public rooms for params", opts, e);
|
console.error("Could not fetch public rooms for params", opts, e);
|
||||||
|
@ -118,7 +125,7 @@ export const usePublicRoomDirectory = (): {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[config, updateQuery, updateResult],
|
[config, updateQuery, updateResult, showNsfwPublicRooms],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -1018,6 +1018,7 @@
|
||||||
"Automatic gain control": "Automatic gain control",
|
"Automatic gain control": "Automatic gain control",
|
||||||
"Echo cancellation": "Echo cancellation",
|
"Echo cancellation": "Echo cancellation",
|
||||||
"Noise suppression": "Noise suppression",
|
"Noise suppression": "Noise suppression",
|
||||||
|
"Show NSFW content": "Show NSFW content",
|
||||||
"Send analytics data": "Send analytics data",
|
"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",
|
"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",
|
"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",
|
"Code blocks": "Code blocks",
|
||||||
"Images, GIFs and videos": "Images, GIFs and videos",
|
"Images, GIFs and videos": "Images, GIFs and videos",
|
||||||
"Timeline": "Timeline",
|
"Timeline": "Timeline",
|
||||||
|
"Room directory": "Room directory",
|
||||||
"Enable hardware acceleration (restart %(appName)s to take effect)": "Enable hardware acceleration (restart %(appName)s to take effect)",
|
"Enable hardware acceleration (restart %(appName)s to take effect)": "Enable hardware acceleration (restart %(appName)s to take effect)",
|
||||||
"Autocomplete delay (ms)": "Autocomplete delay (ms)",
|
"Autocomplete delay (ms)": "Autocomplete delay (ms)",
|
||||||
"Read Marker lifetime (ms)": "Read Marker lifetime (ms)",
|
"Read Marker lifetime (ms)": "Read Marker lifetime (ms)",
|
||||||
|
@ -2328,6 +2330,7 @@
|
||||||
"Last month": "Last month",
|
"Last month": "Last month",
|
||||||
"The beginning of the room": "The beginning of the room",
|
"The beginning of the room": "The beginning of the room",
|
||||||
"Jump to date": "Jump to date",
|
"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)",
|
"%(displayName)s (%(matrixId)s)": "%(displayName)s (%(matrixId)s)",
|
||||||
"Downloading": "Downloading",
|
"Downloading": "Downloading",
|
||||||
"Decrypting": "Decrypting",
|
"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.",
|
"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",
|
"Vote not registered": "Vote not registered",
|
||||||
"Sorry, your vote was not registered. Please try again.": "Sorry, your vote was not registered. Please try again.",
|
"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|other": "Final result based on %(count)s votes",
|
||||||
"Final result based on %(count)s votes|one": "Final result based on %(count)s vote",
|
"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",
|
"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|other": "Based on %(count)s votes",
|
||||||
"Based on %(count)s votes|one": "Based on %(count)s vote",
|
"Based on %(count)s votes|one": "Based on %(count)s vote",
|
||||||
"edited": "edited",
|
"edited": "edited",
|
||||||
|
"Ended a poll": "Ended a poll",
|
||||||
"Error decrypting video": "Error decrypting video",
|
"Error decrypting video": "Error decrypting video",
|
||||||
"Error processing voice message": "Error processing voice message",
|
"Error processing voice message": "Error processing voice message",
|
||||||
"Add reaction": "Add reaction",
|
"Add reaction": "Add reaction",
|
||||||
|
|
|
@ -743,6 +743,11 @@ export const SETTINGS: { [setting: string]: ISetting } = {
|
||||||
supportedLevels: [SettingLevel.ACCOUNT],
|
supportedLevels: [SettingLevel.ACCOUNT],
|
||||||
default: [], // list of room IDs, most recent first
|
default: [], // list of room IDs, most recent first
|
||||||
},
|
},
|
||||||
|
"SpotlightSearch.showNsfwPublicRooms": {
|
||||||
|
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||||
|
displayName: _td("Show NSFW content"),
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
"room_directory_servers": {
|
"room_directory_servers": {
|
||||||
supportedLevels: [SettingLevel.ACCOUNT],
|
supportedLevels: [SettingLevel.ACCOUNT],
|
||||||
default: [],
|
default: [],
|
||||||
|
|
|
@ -89,7 +89,7 @@ export default class HTMLExporter extends Exporter {
|
||||||
return renderToStaticMarkup(avatar);
|
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 roomAvatar = await this.getRoomAvatar();
|
||||||
const exportDate = formatFullDateNoDayNoTime(new Date());
|
const exportDate = formatFullDateNoDayNoTime(new Date());
|
||||||
const creator = this.room.currentState.getStateEvents(EventType.RoomCreate, "")?.getSender();
|
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 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 `
|
return `
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
|
@ -168,6 +191,7 @@ export default class HTMLExporter extends Exporter {
|
||||||
<div class="mx_RoomHeader_topic" dir="auto"> ${topic} </div>
|
<div class="mx_RoomHeader_topic" dir="auto"> ${topic} </div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
${previousMessagesLink}
|
||||||
<div class="mx_MainSplit">
|
<div class="mx_MainSplit">
|
||||||
<div class="mx_RoomView_body">
|
<div class="mx_RoomView_body">
|
||||||
<div
|
<div
|
||||||
|
@ -186,13 +210,17 @@ export default class HTMLExporter extends Exporter {
|
||||||
aria-live="polite"
|
aria-live="polite"
|
||||||
role="list"
|
role="list"
|
||||||
>
|
>
|
||||||
<div class="mx_NewRoomIntro">
|
${
|
||||||
${roomAvatar}
|
currentPage == 0
|
||||||
<h2> ${this.room.name} </h2>
|
? `<div class="mx_NewRoomIntro">
|
||||||
<p> ${createdText} <br/><br/> ${exportedText} </p>
|
${roomAvatar}
|
||||||
<br/>
|
<h2> ${this.room.name} </h2>
|
||||||
<p> ${topicText} </p>
|
<p> ${createdText} <br/><br/> ${exportedText} </p>
|
||||||
</div>
|
<br/>
|
||||||
|
<p> ${topicText} </p>
|
||||||
|
</div>`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
${content}
|
${content}
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
|
@ -205,6 +233,7 @@ export default class HTMLExporter extends Exporter {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
${nextMessagesLink}
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -381,7 +410,12 @@ export default class HTMLExporter extends Exporter {
|
||||||
return eventTile;
|
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 content = "";
|
||||||
let prevEvent: MatrixEvent | null = null;
|
let prevEvent: MatrixEvent | null = null;
|
||||||
for (let i = start; i < Math.min(start + 1000, events.length); i++) {
|
for (let i = start; i < Math.min(start + 1000, events.length); i++) {
|
||||||
|
@ -405,7 +439,7 @@ export default class HTMLExporter extends Exporter {
|
||||||
content += body;
|
content += body;
|
||||||
prevEvent = event;
|
prevEvent = event;
|
||||||
}
|
}
|
||||||
return this.wrapHTML(content);
|
return this.wrapHTML(content, currentPage, nbPages);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async export(): Promise<void> {
|
public async export(): Promise<void> {
|
||||||
|
@ -428,7 +462,7 @@ export default class HTMLExporter extends Exporter {
|
||||||
|
|
||||||
const usedClasses = new Set<string>();
|
const usedClasses = new Set<string>();
|
||||||
for (let page = 0; page < res.length / 1000; page++) {
|
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");
|
const document = new DOMParser().parseFromString(html, "text/html");
|
||||||
document.querySelectorAll("*").forEach((element) => {
|
document.querySelectorAll("*").forEach((element) => {
|
||||||
element.classList.forEach((c) => usedClasses.add(c));
|
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 { 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 { ReactElement } from "react";
|
||||||
import { Mocked, mocked } from "jest-mock";
|
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("TextForEvent", () => {
|
||||||
describe("getSenderName()", () => {
|
describe("getSenderName()", () => {
|
||||||
it("Prefers sender.name", () => {
|
it("Prefers sender.name", () => {
|
||||||
|
@ -105,71 +68,71 @@ describe("TextForEvent", () => {
|
||||||
it("mentions message when a single message was pinned, with no previously pinned messages", () => {
|
it("mentions message when a single message was pinned, with no previously pinned messages", () => {
|
||||||
const event = mockPinnedEvent(["message-1"]);
|
const event = mockPinnedEvent(["message-1"]);
|
||||||
const plainText = textForEvent(event);
|
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.";
|
const expectedText = "@foo:example.com pinned a message to this room. See all pinned messages.";
|
||||||
expect(plainText).toBe(expectedText);
|
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", () => {
|
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 event = mockPinnedEvent(["message-1", "message-2", "message-3"], ["message-1", "message-2"]);
|
||||||
const plainText = textForEvent(event);
|
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.";
|
const expectedText = "@foo:example.com pinned a message to this room. See all pinned messages.";
|
||||||
expect(plainText).toBe(expectedText);
|
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", () => {
|
it("mentions message when a single message was unpinned, with a single message previously pinned", () => {
|
||||||
const event = mockPinnedEvent([], ["message-1"]);
|
const event = mockPinnedEvent([], ["message-1"]);
|
||||||
const plainText = textForEvent(event);
|
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.";
|
const expectedText = "@foo:example.com unpinned a message from this room. See all pinned messages.";
|
||||||
expect(plainText).toBe(expectedText);
|
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", () => {
|
it("mentions message when a single message was unpinned, with multiple previously pinned messages", () => {
|
||||||
const event = mockPinnedEvent(["message-2"], ["message-1", "message-2"]);
|
const event = mockPinnedEvent(["message-2"], ["message-1", "message-2"]);
|
||||||
const plainText = textForEvent(event);
|
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.";
|
const expectedText = "@foo:example.com unpinned a message from this room. See all pinned messages.";
|
||||||
expect(plainText).toBe(expectedText);
|
expect(plainText).toBe(expectedText);
|
||||||
expect(renderComponent(component)).toBe(expectedText);
|
expect(component.container).toHaveTextContent(expectedText);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows generic text when multiple messages were pinned", () => {
|
it("shows generic text when multiple messages were pinned", () => {
|
||||||
const event = mockPinnedEvent(["message-1", "message-2", "message-3"], ["message-1"]);
|
const event = mockPinnedEvent(["message-1", "message-2", "message-3"], ["message-1"]);
|
||||||
const plainText = textForEvent(event);
|
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.";
|
const expectedText = "@foo:example.com changed the pinned messages for the room.";
|
||||||
expect(plainText).toBe(expectedText);
|
expect(plainText).toBe(expectedText);
|
||||||
expect(renderComponent(component)).toBe(expectedText);
|
expect(component.container).toHaveTextContent(expectedText);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows generic text when multiple messages were unpinned", () => {
|
it("shows generic text when multiple messages were unpinned", () => {
|
||||||
const event = mockPinnedEvent(["message-3"], ["message-1", "message-2", "message-3"]);
|
const event = mockPinnedEvent(["message-3"], ["message-1", "message-2", "message-3"]);
|
||||||
const plainText = textForEvent(event);
|
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.";
|
const expectedText = "@foo:example.com changed the pinned messages for the room.";
|
||||||
expect(plainText).toBe(expectedText);
|
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", () => {
|
it("shows generic text when one message was pinned, and another unpinned", () => {
|
||||||
const event = mockPinnedEvent(["message-2"], ["message-1"]);
|
const event = mockPinnedEvent(["message-2"], ["message-1"]);
|
||||||
const plainText = textForEvent(event);
|
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.";
|
const expectedText = "@foo:example.com changed the pinned messages for the room.";
|
||||||
expect(plainText).toBe(expectedText);
|
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 React from "react";
|
||||||
import ReactDOM from "react-dom";
|
|
||||||
import { EventEmitter } from "events";
|
import { EventEmitter } from "events";
|
||||||
import { MatrixEvent, Room, RoomMember } from "matrix-js-sdk/src/matrix";
|
import { MatrixEvent, Room, RoomMember } from "matrix-js-sdk/src/matrix";
|
||||||
import FakeTimers from "@sinonjs/fake-timers";
|
import FakeTimers from "@sinonjs/fake-timers";
|
||||||
|
@ -358,7 +357,7 @@ describe("MessagePanel", function () {
|
||||||
const [rm] = container.getElementsByClassName("mx_RoomView_myReadMarker_container");
|
const [rm] = container.getElementsByClassName("mx_RoomView_myReadMarker_container");
|
||||||
|
|
||||||
// it should follow the <li> which wraps the event tile for event 4
|
// 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);
|
expect(rm.previousSibling).toEqual(eventContainer);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { fireEvent, render } from "@testing-library/react";
|
import { act, fireEvent, render } from "@testing-library/react";
|
||||||
import { act } from "react-dom/test-utils";
|
|
||||||
|
|
||||||
import TabbedView, { Tab, TabLocation } from "../../../src/components/structures/TabbedView";
|
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.
|
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 userEvent from "@testing-library/user-event";
|
||||||
import { mocked } from "jest-mock";
|
import { mocked } from "jest-mock";
|
||||||
import { MsgType, RelationType } from "matrix-js-sdk/src/@types/event";
|
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 { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread";
|
import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { act } from "react-dom/test-utils";
|
|
||||||
|
|
||||||
import ThreadView from "../../../src/components/structures/ThreadView";
|
import ThreadView from "../../../src/components/structures/ThreadView";
|
||||||
import MatrixClientContext from "../../../src/contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../src/contexts/MatrixClientContext";
|
||||||
|
|
|
@ -15,10 +15,9 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
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 { Beacon, RoomMember, MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||||
import { LocationAssetType } from "matrix-js-sdk/src/@types/location";
|
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 BeaconListItem from "../../../../src/components/views/beacon/BeaconListItem";
|
||||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||||
|
|
|
@ -15,9 +15,8 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
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 * 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 { Beacon, Room, RoomMember, MatrixEvent, getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import BeaconMarker from "../../../../src/components/views/beacon/BeaconMarker";
|
import BeaconMarker from "../../../../src/components/views/beacon/BeaconMarker";
|
||||||
|
|
|
@ -15,17 +15,14 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
// eslint-disable-next-line deprecate/import
|
|
||||||
import { mount, ReactWrapper } from "enzyme";
|
|
||||||
import { act } from "react-dom/test-utils";
|
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 { MatrixClient, MatrixEvent, Room, RoomMember, getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix";
|
||||||
import * as maplibregl from "maplibre-gl";
|
import * as maplibregl from "maplibre-gl";
|
||||||
import { mocked } from "jest-mock";
|
import { mocked } from "jest-mock";
|
||||||
|
|
||||||
import BeaconViewDialog from "../../../../src/components/views/beacon/BeaconViewDialog";
|
import BeaconViewDialog from "../../../../src/components/views/beacon/BeaconViewDialog";
|
||||||
import {
|
import {
|
||||||
findByAttr,
|
|
||||||
findByTestId,
|
|
||||||
getMockClientWithEventEmitter,
|
getMockClientWithEventEmitter,
|
||||||
makeBeaconEvent,
|
makeBeaconEvent,
|
||||||
makeBeaconInfoEvent,
|
makeBeaconInfoEvent,
|
||||||
|
@ -34,8 +31,6 @@ import {
|
||||||
} from "../../../test-utils";
|
} from "../../../test-utils";
|
||||||
import { TILE_SERVER_WK_KEY } from "../../../../src/utils/WellKnownUtils";
|
import { TILE_SERVER_WK_KEY } from "../../../../src/utils/WellKnownUtils";
|
||||||
import { OwnBeaconStore } from "../../../../src/stores/OwnBeaconStore";
|
import { OwnBeaconStore } from "../../../../src/stores/OwnBeaconStore";
|
||||||
import { BeaconDisplayStatus } from "../../../../src/components/views/beacon/displayStatus";
|
|
||||||
import BeaconListItem from "../../../../src/components/views/beacon/BeaconListItem";
|
|
||||||
|
|
||||||
describe("<BeaconViewDialog />", () => {
|
describe("<BeaconViewDialog />", () => {
|
||||||
// 14.03.2022 16:15
|
// 14.03.2022 16:15
|
||||||
|
@ -60,6 +55,7 @@ describe("<BeaconViewDialog />", () => {
|
||||||
|
|
||||||
const mapOptions = { container: {} as unknown as HTMLElement, style: "" };
|
const mapOptions = { container: {} as unknown as HTMLElement, style: "" };
|
||||||
const mockMap = new maplibregl.Map(mapOptions);
|
const mockMap = new maplibregl.Map(mapOptions);
|
||||||
|
const mockMarker = new maplibregl.Marker();
|
||||||
|
|
||||||
// make fresh rooms every time
|
// make fresh rooms every time
|
||||||
// as we update room state
|
// as we update room state
|
||||||
|
@ -84,13 +80,11 @@ describe("<BeaconViewDialog />", () => {
|
||||||
matrixClient: mockClient as MatrixClient,
|
matrixClient: mockClient as MatrixClient,
|
||||||
};
|
};
|
||||||
|
|
||||||
const getComponent = (props = {}) => mount(<BeaconViewDialog {...defaultProps} {...props} />);
|
const getComponent = (props = {}): RenderResult => render(<BeaconViewDialog {...defaultProps} {...props} />);
|
||||||
|
|
||||||
const openSidebar = (component: ReactWrapper) =>
|
const openSidebar = (getByTestId: RenderResult["getByTestId"]) => {
|
||||||
act(() => {
|
fireEvent.click(getByTestId("beacon-view-dialog-open-sidebar"));
|
||||||
findByTestId(component, "beacon-view-dialog-open-sidebar").at(0).simulate("click");
|
};
|
||||||
component.setProps({});
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.spyOn(OwnBeaconStore.instance, "getLiveBeaconIds").mockRestore();
|
jest.spyOn(OwnBeaconStore.instance, "getLiveBeaconIds").mockRestore();
|
||||||
|
@ -103,14 +97,14 @@ describe("<BeaconViewDialog />", () => {
|
||||||
const room = setupRoom([defaultEvent]);
|
const room = setupRoom([defaultEvent]);
|
||||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
||||||
beacon.addLocations([location1]);
|
beacon.addLocations([location1]);
|
||||||
const component = getComponent();
|
getComponent();
|
||||||
expect(component.find("Map").props()).toEqual(
|
// centered on default event
|
||||||
expect.objectContaining({
|
expect(mockMap.setCenter).toHaveBeenCalledWith({
|
||||||
centerGeoUri: "geo:51,41",
|
lon: 41,
|
||||||
interactive: true,
|
lat: 51,
|
||||||
}),
|
});
|
||||||
);
|
// marker added
|
||||||
expect(component.find("SmartMarker").length).toEqual(1);
|
expect(mockMarker.addTo).toHaveBeenCalledWith(mockMap);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not render any own beacon status when user is not live sharing", () => {
|
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 room = setupRoom([defaultEvent]);
|
||||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
||||||
beacon.addLocations([location1]);
|
beacon.addLocations([location1]);
|
||||||
const component = getComponent();
|
const { queryByText } = getComponent();
|
||||||
expect(component.find("DialogOwnBeaconStatus").html()).toBeNull();
|
expect(queryByText("Live location enabled")).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders own beacon status when user is live sharing", () => {
|
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
|
// 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, "getLiveBeaconIds").mockReturnValue([beacon.identifier]);
|
||||||
jest.spyOn(OwnBeaconStore.instance, "getBeaconById").mockReturnValue(beacon);
|
jest.spyOn(OwnBeaconStore.instance, "getBeaconById").mockReturnValue(beacon);
|
||||||
const component = getComponent();
|
const { container } = getComponent();
|
||||||
expect(component.find("MemberAvatar").length).toBeTruthy();
|
expect(container.querySelector(".mx_DialogOwnBeaconStatus")).toMatchSnapshot();
|
||||||
expect(component.find("OwnBeaconStatus").props()).toEqual({
|
|
||||||
beacon,
|
|
||||||
displayStatus: BeaconDisplayStatus.Active,
|
|
||||||
className: "mx_DialogOwnBeaconStatus_status",
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("updates markers on changes to beacons", () => {
|
it("updates markers on changes to beacons", async () => {
|
||||||
const room = setupRoom([defaultEvent]);
|
const room = setupRoom([defaultEvent]);
|
||||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
||||||
beacon.addLocations([location1]);
|
beacon.addLocations([location1]);
|
||||||
const component = getComponent();
|
const { container } = getComponent();
|
||||||
expect(component.find("BeaconMarker").length).toEqual(1);
|
|
||||||
|
// 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");
|
const anotherBeaconEvent = makeBeaconInfoEvent(bobId, roomId, { isLive: true }, "$bob-room1-1");
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
// emits RoomStateEvent.BeaconLiveness
|
// emits RoomStateEvent.BeaconLiveness
|
||||||
room.currentState.setStateEvents([anotherBeaconEvent]);
|
room.currentState.setStateEvents([anotherBeaconEvent]);
|
||||||
|
const beacon2 = room.currentState.beacons.get(getBeaconInfoIdentifier(anotherBeaconEvent))!;
|
||||||
|
beacon2.addLocations([location1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
component.setProps({});
|
|
||||||
|
|
||||||
// two markers now!
|
// 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", () => {
|
it("does not update bounds or center on changing beacons", () => {
|
||||||
const room = setupRoom([defaultEvent]);
|
const room = setupRoom([defaultEvent]);
|
||||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
||||||
beacon.addLocations([location1]);
|
beacon.addLocations([location1]);
|
||||||
const component = getComponent();
|
const { container } = getComponent();
|
||||||
expect(component.find("BeaconMarker").length).toEqual(1);
|
expect(container.getElementsByClassName("mx_Marker").length).toEqual(1);
|
||||||
|
|
||||||
const anotherBeaconEvent = makeBeaconInfoEvent(bobId, roomId, { isLive: true }, "$bob-room1-1");
|
const anotherBeaconEvent = makeBeaconInfoEvent(bobId, roomId, { isLive: true }, "$bob-room1-1");
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
// emits RoomStateEvent.BeaconLiveness
|
// emits RoomStateEvent.BeaconLiveness
|
||||||
room.currentState.setStateEvents([anotherBeaconEvent]);
|
room.currentState.setStateEvents([anotherBeaconEvent]);
|
||||||
|
const beacon2 = room.currentState.beacons.get(getBeaconInfoIdentifier(anotherBeaconEvent))!;
|
||||||
|
beacon2.addLocations([location1]);
|
||||||
});
|
});
|
||||||
|
// called once on init
|
||||||
component.setProps({});
|
|
||||||
|
|
||||||
// two markers now!
|
|
||||||
expect(mockMap.setCenter).toHaveBeenCalledTimes(1);
|
expect(mockMap.setCenter).toHaveBeenCalledTimes(1);
|
||||||
expect(mockMap.fitBounds).toHaveBeenCalledTimes(1);
|
expect(mockMap.fitBounds).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
@ -185,14 +174,12 @@ describe("<BeaconViewDialog />", () => {
|
||||||
const onFinished = jest.fn();
|
const onFinished = jest.fn();
|
||||||
const room = setupRoom([defaultEvent]);
|
const room = setupRoom([defaultEvent]);
|
||||||
room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
|
room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
|
||||||
const component = getComponent({ onFinished });
|
const { getByTestId } = getComponent({ onFinished });
|
||||||
|
|
||||||
// map placeholder
|
// map placeholder
|
||||||
expect(findByTestId(component, "beacon-view-dialog-map-fallback")).toMatchSnapshot();
|
expect(getByTestId("beacon-view-dialog-map-fallback")).toMatchSnapshot();
|
||||||
|
|
||||||
act(() => {
|
fireEvent.click(getByTestId("beacon-view-dialog-fallback-close"));
|
||||||
findByTestId(component, "beacon-view-dialog-fallback-close").at(0).simulate("click");
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(onFinished).toHaveBeenCalled();
|
expect(onFinished).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
@ -202,8 +189,8 @@ describe("<BeaconViewDialog />", () => {
|
||||||
const room = setupRoom([defaultEvent]);
|
const room = setupRoom([defaultEvent]);
|
||||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
||||||
beacon.addLocations([location1]);
|
beacon.addLocations([location1]);
|
||||||
const component = getComponent({ onFinished });
|
const { container } = getComponent({ onFinished });
|
||||||
expect(component.find("BeaconMarker").length).toEqual(1);
|
expect(container.getElementsByClassName("mx_Marker").length).toEqual(1);
|
||||||
|
|
||||||
// this will replace the defaultEvent
|
// this will replace the defaultEvent
|
||||||
// leading to no more live beacons
|
// leading to no more live beacons
|
||||||
|
@ -219,12 +206,10 @@ describe("<BeaconViewDialog />", () => {
|
||||||
room.currentState.setStateEvents([anotherBeaconEvent]);
|
room.currentState.setStateEvents([anotherBeaconEvent]);
|
||||||
});
|
});
|
||||||
|
|
||||||
component.setProps({});
|
|
||||||
|
|
||||||
// no more avatars
|
// no more avatars
|
||||||
expect(component.find("MemberAvatar").length).toBeFalsy();
|
expect(container.getElementsByClassName("mx_Marker").length).toEqual(0);
|
||||||
// map still rendered
|
// map still rendered
|
||||||
expect(component.find("Map").length).toBeTruthy();
|
expect(container.querySelector("#mx_Map_mx_BeaconViewDialog")).toBeInTheDocument();
|
||||||
// map location unchanged
|
// map location unchanged
|
||||||
expect(mockMap.setCenter).not.toHaveBeenCalled();
|
expect(mockMap.setCenter).not.toHaveBeenCalled();
|
||||||
expect(mockMap.fitBounds).not.toHaveBeenCalled();
|
expect(mockMap.fitBounds).not.toHaveBeenCalled();
|
||||||
|
@ -235,31 +220,28 @@ describe("<BeaconViewDialog />", () => {
|
||||||
const room = setupRoom([defaultEvent]);
|
const room = setupRoom([defaultEvent]);
|
||||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
||||||
beacon.addLocations([location1]);
|
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", () => {
|
it("closes sidebar on close button click", () => {
|
||||||
const room = setupRoom([defaultEvent]);
|
const room = setupRoom([defaultEvent]);
|
||||||
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
|
||||||
beacon.addLocations([location1]);
|
beacon.addLocations([location1]);
|
||||||
const component = getComponent();
|
const { container, getByTestId } = getComponent();
|
||||||
|
|
||||||
// open the sidebar
|
// open the sidebar
|
||||||
openSidebar(component);
|
openSidebar(getByTestId);
|
||||||
|
|
||||||
expect(component.find("DialogSidebar").length).toBeTruthy();
|
expect(container.querySelector(".mx_DialogSidebar")).toBeInTheDocument();
|
||||||
|
|
||||||
// now close it
|
// now close it
|
||||||
act(() => {
|
fireEvent.click(getByTestId("dialog-sidebar-close"));
|
||||||
findByAttr("data-testid")(component, "dialog-sidebar-close").at(0).simulate("click");
|
|
||||||
component.setProps({});
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(component.find("DialogSidebar").length).toBeFalsy();
|
expect(container.querySelector(".mx_DialogSidebar")).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -326,16 +308,17 @@ describe("<BeaconViewDialog />", () => {
|
||||||
[location1, location2],
|
[location1, location2],
|
||||||
);
|
);
|
||||||
|
|
||||||
const component = getComponent({ beacons: [beacon1, beacon2] });
|
const { container, getByTestId } = getComponent({ beacons: [beacon1, beacon2] });
|
||||||
|
|
||||||
// reset call counts on map mocks after initial render
|
// reset call counts on map mocks after initial render
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
|
||||||
openSidebar(component);
|
openSidebar(getByTestId);
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
|
const listItems = container.querySelectorAll(".mx_BeaconListItem");
|
||||||
// click on the first beacon in the list
|
// click on the first beacon in the list
|
||||||
component.find(BeaconListItem).at(0).simulate("click");
|
fireEvent.click(listItems[0]!);
|
||||||
});
|
});
|
||||||
|
|
||||||
// centered on clicked beacon
|
// centered on clicked beacon
|
||||||
|
@ -359,16 +342,17 @@ describe("<BeaconViewDialog />", () => {
|
||||||
[location1, location2],
|
[location1, location2],
|
||||||
);
|
);
|
||||||
|
|
||||||
const component = getComponent({ beacons: [beacon1, beacon2] });
|
const { container, getByTestId } = getComponent({ beacons: [beacon1, beacon2] });
|
||||||
|
|
||||||
// reset call counts on map mocks after initial render
|
// reset call counts on map mocks after initial render
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
|
||||||
openSidebar(component);
|
openSidebar(getByTestId);
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
// click on the second beacon in the list
|
// 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]);
|
const expectedBounds = new maplibregl.LngLatBounds([22, 33], [22, 33]);
|
||||||
|
@ -378,7 +362,8 @@ describe("<BeaconViewDialog />", () => {
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
// click on the second beacon in the list
|
// 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
|
// centered on clicked beacon
|
||||||
|
|
|
@ -15,8 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { ComponentProps } from "react";
|
import React, { ComponentProps } from "react";
|
||||||
import { fireEvent, render } from "@testing-library/react";
|
import { act, fireEvent, render } from "@testing-library/react";
|
||||||
import { act } from "react-dom/test-utils";
|
|
||||||
|
|
||||||
import DialogSidebar from "../../../../src/components/views/beacon/DialogSidebar";
|
import DialogSidebar from "../../../../src/components/views/beacon/DialogSidebar";
|
||||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||||
|
|
|
@ -16,8 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { mocked } from "jest-mock";
|
import { mocked } from "jest-mock";
|
||||||
import { fireEvent, render } from "@testing-library/react";
|
import { act, fireEvent, render } from "@testing-library/react";
|
||||||
import { act } from "react-dom/test-utils";
|
|
||||||
import { Beacon, BeaconIdentifier } from "matrix-js-sdk/src/matrix";
|
import { Beacon, BeaconIdentifier } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import LeftPanelLiveShareWarning from "../../../../src/components/views/beacon/LeftPanelLiveShareWarning";
|
import LeftPanelLiveShareWarning from "../../../../src/components/views/beacon/LeftPanelLiveShareWarning";
|
||||||
|
|
|
@ -15,10 +15,9 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { act } from "react-dom/test-utils";
|
|
||||||
import { Room, PendingEventOrdering, MatrixClient, RoomMember, RoomStateEvent } from "matrix-js-sdk/src/matrix";
|
import { Room, PendingEventOrdering, MatrixClient, RoomMember, RoomStateEvent } from "matrix-js-sdk/src/matrix";
|
||||||
import { ClientWidgetApi, Widget } from "matrix-widget-api";
|
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 { mocked, Mocked } from "jest-mock";
|
||||||
|
|
||||||
import { mkRoomMember, MockedCall, setupAsyncStoreWithClient, stubClient, useMockedCalls } from "../../../test-utils";
|
import { mkRoomMember, MockedCall, setupAsyncStoreWithClient, stubClient, useMockedCalls } from "../../../test-utils";
|
||||||
|
|
|
@ -15,10 +15,9 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { act } from "react-dom/test-utils";
|
|
||||||
import { Room, Beacon, BeaconEvent, getBeaconInfoIdentifier, MatrixEvent } from "matrix-js-sdk/src/matrix";
|
import { Room, Beacon, BeaconEvent, getBeaconInfoIdentifier, MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
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 RoomLiveShareWarning from "../../../../src/components/views/beacon/RoomLiveShareWarning";
|
||||||
import { OwnBeaconStore, OwnBeaconStoreEvent } from "../../../../src/stores/OwnBeaconStore";
|
import { OwnBeaconStore, OwnBeaconStoreEvent } from "../../../../src/stores/OwnBeaconStore";
|
||||||
|
|
|
@ -1,83 +1,83 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`<BeaconViewDialog /> renders a fallback when there are no locations 1`] = `
|
exports[`<BeaconViewDialog /> renders a fallback when there are no locations 1`] = `
|
||||||
[
|
<div
|
||||||
<MapFallback
|
class="mx_MapFallback mx_BeaconViewDialog_map"
|
||||||
className="mx_BeaconViewDialog_map"
|
data-testid="beacon-view-dialog-map-fallback"
|
||||||
data-test-id="beacon-view-dialog-map-fallback"
|
>
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="mx_MapFallback mx_BeaconViewDialog_map"
|
|
||||||
data-test-id="beacon-view-dialog-map-fallback"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="mx_MapFallback_bg"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
className="mx_MapFallback_icon"
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
className="mx_BeaconViewDialog_mapFallbackMessage"
|
|
||||||
>
|
|
||||||
No live locations
|
|
||||||
</span>
|
|
||||||
<AccessibleButton
|
|
||||||
data-test-id="beacon-view-dialog-fallback-close"
|
|
||||||
element="div"
|
|
||||||
kind="primary"
|
|
||||||
onClick={[MockFunction]}
|
|
||||||
role="button"
|
|
||||||
tabIndex={0}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
|
||||||
data-test-id="beacon-view-dialog-fallback-close"
|
|
||||||
onClick={[MockFunction]}
|
|
||||||
onKeyDown={[Function]}
|
|
||||||
onKeyUp={[Function]}
|
|
||||||
role="button"
|
|
||||||
tabIndex={0}
|
|
||||||
>
|
|
||||||
Close
|
|
||||||
</div>
|
|
||||||
</AccessibleButton>
|
|
||||||
</div>
|
|
||||||
</MapFallback>,
|
|
||||||
<div
|
<div
|
||||||
className="mx_MapFallback mx_BeaconViewDialog_map"
|
class="mx_MapFallback_bg"
|
||||||
data-test-id="beacon-view-dialog-map-fallback"
|
/>
|
||||||
|
<div
|
||||||
|
class="mx_MapFallback_icon"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class="mx_BeaconViewDialog_mapFallbackMessage"
|
||||||
|
>
|
||||||
|
No live locations
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||||
|
data-testid="beacon-view-dialog-fallback-close"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`<BeaconViewDialog /> renders own beacon status when user is live sharing 1`] = `
|
||||||
|
<div
|
||||||
|
class="mx_DialogOwnBeaconStatus"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="mx_BaseAvatar mx_DialogOwnBeaconStatus_avatar"
|
||||||
|
role="presentation"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
class="mx_BaseAvatar_initial"
|
||||||
|
style="font-size: 20.8px; width: 32px; line-height: 32px;"
|
||||||
|
>
|
||||||
|
A
|
||||||
|
</span>
|
||||||
|
<img
|
||||||
|
alt=""
|
||||||
|
aria-hidden="true"
|
||||||
|
class="mx_BaseAvatar_image"
|
||||||
|
data-testid="avatar-img"
|
||||||
|
src="data:image/png;base64,00"
|
||||||
|
style="width: 32px; height: 32px;"
|
||||||
|
title="@alice:server"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
class="mx_BeaconStatus mx_BeaconStatus_Active mx_DialogOwnBeaconStatus_status"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="mx_MapFallback_bg"
|
class="mx_BeaconStatus_description"
|
||||||
/>
|
|
||||||
<div
|
|
||||||
className="mx_MapFallback_icon"
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
className="mx_BeaconViewDialog_mapFallbackMessage"
|
|
||||||
>
|
>
|
||||||
No live locations
|
<span
|
||||||
</span>
|
class="mx_BeaconStatus_label"
|
||||||
<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
|
Live location enabled
|
||||||
</div>
|
</span>
|
||||||
</AccessibleButton>
|
<span
|
||||||
</div>,
|
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 React from "react";
|
||||||
import { act } from "react-dom/test-utils";
|
|
||||||
import { MatrixEvent, EventType } from "matrix-js-sdk/src/matrix";
|
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 { 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 { 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 userEvent from "@testing-library/user-event";
|
||||||
|
|
||||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||||
|
|
|
@ -367,4 +367,67 @@ describe("Spotlight Dialog", () => {
|
||||||
expect(screen.queryByText("give feedback")).not.toBeInTheDocument();
|
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.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { render } from "@testing-library/react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { renderIntoDocument } from "react-dom/test-utils";
|
|
||||||
|
|
||||||
import ExternalLink from "../../../../src/components/views/elements/ExternalLink";
|
import ExternalLink from "../../../../src/components/views/elements/ExternalLink";
|
||||||
|
|
||||||
|
@ -22,15 +22,10 @@ describe("<ExternalLink />", () => {
|
||||||
"href": "test.com",
|
"href": "test.com",
|
||||||
"onClick": jest.fn(),
|
"onClick": jest.fn(),
|
||||||
"className": "myCustomClass",
|
"className": "myCustomClass",
|
||||||
"data-test-id": "test",
|
"data-testid": "test",
|
||||||
};
|
};
|
||||||
const getComponent = (props = {}) => {
|
const getComponent = (props = {}) => {
|
||||||
const wrapper = renderIntoDocument<HTMLDivElement>(
|
return render(<ExternalLink {...defaultProps} {...props} />);
|
||||||
<div>
|
|
||||||
<ExternalLink {...defaultProps} {...props} />
|
|
||||||
</div>,
|
|
||||||
) as HTMLDivElement;
|
|
||||||
return wrapper.children[0];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
it("renders link correctly", () => {
|
it("renders link correctly", () => {
|
||||||
|
@ -39,18 +34,19 @@ describe("<ExternalLink />", () => {
|
||||||
react element <b>children</b>
|
react element <b>children</b>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
expect(getComponent({ children, target: "_self", rel: "noopener" })).toMatchSnapshot();
|
expect(getComponent({ children, target: "_self", rel: "noopener" }).asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("defaults target and rel", () => {
|
it("defaults target and rel", () => {
|
||||||
const children = "test";
|
const children = "test";
|
||||||
const component = getComponent({ children });
|
const { getByTestId } = getComponent({ children });
|
||||||
expect(component.getAttribute("rel")).toEqual("noreferrer noopener");
|
const container = getByTestId("test");
|
||||||
expect(component.getAttribute("target")).toEqual("_blank");
|
expect(container.getAttribute("rel")).toEqual("noreferrer noopener");
|
||||||
|
expect(container.getAttribute("target")).toEqual("_blank");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders plain text link correctly", () => {
|
it("renders plain text link correctly", () => {
|
||||||
const children = "test";
|
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 React from "react";
|
||||||
import { renderIntoDocument, Simulate } from "react-dom/test-utils";
|
import { act, renderIntoDocument, Simulate } from "react-dom/test-utils";
|
||||||
import { act } from "react-dom/test-utils";
|
|
||||||
|
|
||||||
import { Alignment } from "../../../../src/components/views/elements/Tooltip";
|
import { Alignment } from "../../../../src/components/views/elements/Tooltip";
|
||||||
import TooltipTarget from "../../../../src/components/views/elements/TooltipTarget";
|
import TooltipTarget from "../../../../src/components/views/elements/TooltipTarget";
|
||||||
|
|
|
@ -1,36 +1,40 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`<ExternalLink /> renders link correctly 1`] = `
|
exports[`<ExternalLink /> renders link correctly 1`] = `
|
||||||
<a
|
<DocumentFragment>
|
||||||
class="mx_ExternalLink myCustomClass"
|
<a
|
||||||
data-test-id="test"
|
class="mx_ExternalLink myCustomClass"
|
||||||
href="test.com"
|
data-testid="test"
|
||||||
rel="noopener"
|
href="test.com"
|
||||||
target="_self"
|
rel="noopener"
|
||||||
>
|
target="_self"
|
||||||
<span>
|
>
|
||||||
react element
|
<span>
|
||||||
<b>
|
react element
|
||||||
children
|
<b>
|
||||||
</b>
|
children
|
||||||
</span>
|
</b>
|
||||||
<i
|
</span>
|
||||||
class="mx_ExternalLink_icon"
|
<i
|
||||||
/>
|
class="mx_ExternalLink_icon"
|
||||||
</a>
|
/>
|
||||||
|
</a>
|
||||||
|
</DocumentFragment>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`<ExternalLink /> renders plain text link correctly 1`] = `
|
exports[`<ExternalLink /> renders plain text link correctly 1`] = `
|
||||||
<a
|
<DocumentFragment>
|
||||||
class="mx_ExternalLink myCustomClass"
|
<a
|
||||||
data-test-id="test"
|
class="mx_ExternalLink myCustomClass"
|
||||||
href="test.com"
|
data-testid="test"
|
||||||
rel="noreferrer noopener"
|
href="test.com"
|
||||||
target="_blank"
|
rel="noreferrer noopener"
|
||||||
>
|
target="_blank"
|
||||||
test
|
>
|
||||||
<i
|
test
|
||||||
class="mx_ExternalLink_icon"
|
<i
|
||||||
/>
|
class="mx_ExternalLink_icon"
|
||||||
</a>
|
/>
|
||||||
|
</a>
|
||||||
|
</DocumentFragment>
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -15,9 +15,8 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
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 * as maplibregl from "maplibre-gl";
|
||||||
import { act } from "react-dom/test-utils";
|
|
||||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||||
import { mocked } from "jest-mock";
|
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 { RelationType } from "matrix-js-sdk/src/matrix";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { M_ASSET, LocationAssetType } from "matrix-js-sdk/src/@types/location";
|
import { M_ASSET, LocationAssetType } from "matrix-js-sdk/src/@types/location";
|
||||||
import { act } from "react-dom/test-utils";
|
import { act, fireEvent, render, RenderResult } from "@testing-library/react";
|
||||||
import { fireEvent, render, RenderResult } from "@testing-library/react";
|
|
||||||
import * as maplibregl from "maplibre-gl";
|
import * as maplibregl from "maplibre-gl";
|
||||||
|
|
||||||
import LocationShareMenu from "../../../../src/components/views/location/LocationShareMenu";
|
import LocationShareMenu from "../../../../src/components/views/location/LocationShareMenu";
|
||||||
|
|
|
@ -15,8 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { act } from "react-dom/test-utils";
|
import { act, fireEvent, getByTestId, render } from "@testing-library/react";
|
||||||
import { fireEvent, getByTestId, render } from "@testing-library/react";
|
|
||||||
import * as maplibregl from "maplibre-gl";
|
import * as maplibregl from "maplibre-gl";
|
||||||
import { ClientEvent } from "matrix-js-sdk/src/matrix";
|
import { ClientEvent } from "matrix-js-sdk/src/matrix";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
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 React from "react";
|
||||||
import TestRenderer from "react-test-renderer";
|
import { render } from "@testing-library/react";
|
||||||
import { EventEmitter } from "events";
|
import { EventEmitter } from "events";
|
||||||
import { MatrixEvent, EventType } from "matrix-js-sdk/src/matrix";
|
import { MatrixEvent, EventType } from "matrix-js-sdk/src/matrix";
|
||||||
import { CryptoEvent } from "matrix-js-sdk/src/crypto";
|
import { CryptoEvent } from "matrix-js-sdk/src/crypto";
|
||||||
|
@ -72,29 +72,29 @@ describe("MKeyVerificationConclusion", () => {
|
||||||
|
|
||||||
it("shouldn't render if there's no verificationRequest", () => {
|
it("shouldn't render if there's no verificationRequest", () => {
|
||||||
const event = new MatrixEvent({});
|
const event = new MatrixEvent({});
|
||||||
const renderer = TestRenderer.create(<MKeyVerificationConclusion mxEvent={event} />);
|
const { container } = render(<MKeyVerificationConclusion mxEvent={event} />);
|
||||||
expect(renderer.toJSON()).toBeNull();
|
expect(container).toBeEmpty();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shouldn't render if the verificationRequest is pending", () => {
|
it("shouldn't render if the verificationRequest is pending", () => {
|
||||||
const event = new MatrixEvent({});
|
const event = new MatrixEvent({});
|
||||||
event.verificationRequest = getMockVerificationRequest({ pending: true });
|
event.verificationRequest = getMockVerificationRequest({ pending: true });
|
||||||
const renderer = TestRenderer.create(<MKeyVerificationConclusion mxEvent={event} />);
|
const { container } = render(<MKeyVerificationConclusion mxEvent={event} />);
|
||||||
expect(renderer.toJSON()).toBeNull();
|
expect(container).toBeEmpty();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shouldn't render if the event type is cancel but the request type isn't", () => {
|
it("shouldn't render if the event type is cancel but the request type isn't", () => {
|
||||||
const event = new MatrixEvent({ type: EventType.KeyVerificationCancel });
|
const event = new MatrixEvent({ type: EventType.KeyVerificationCancel });
|
||||||
event.verificationRequest = getMockVerificationRequest({ cancelled: false });
|
event.verificationRequest = getMockVerificationRequest({ cancelled: false });
|
||||||
const renderer = TestRenderer.create(<MKeyVerificationConclusion mxEvent={event} />);
|
const { container } = render(<MKeyVerificationConclusion mxEvent={event} />);
|
||||||
expect(renderer.toJSON()).toBeNull();
|
expect(container).toBeEmpty();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shouldn't render if the event type is done but the request type isn't", () => {
|
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" });
|
const event = new MatrixEvent({ type: "m.key.verification.done" });
|
||||||
event.verificationRequest = getMockVerificationRequest({ done: false });
|
event.verificationRequest = getMockVerificationRequest({ done: false });
|
||||||
const renderer = TestRenderer.create(<MKeyVerificationConclusion mxEvent={event} />);
|
const { container } = render(<MKeyVerificationConclusion mxEvent={event} />);
|
||||||
expect(renderer.toJSON()).toBeNull();
|
expect(container).toBeEmpty();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shouldn't render if the user isn't actually trusted", () => {
|
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" });
|
const event = new MatrixEvent({ type: "m.key.verification.done" });
|
||||||
event.verificationRequest = getMockVerificationRequest({ done: true });
|
event.verificationRequest = getMockVerificationRequest({ done: true });
|
||||||
const renderer = TestRenderer.create(<MKeyVerificationConclusion mxEvent={event} />);
|
const { container } = render(<MKeyVerificationConclusion mxEvent={event} />);
|
||||||
expect(renderer.toJSON()).toBeNull();
|
expect(container).toBeEmpty();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should rerender appropriately if user trust status changes", () => {
|
it("should rerender appropriately if user trust status changes", () => {
|
||||||
|
@ -111,8 +111,8 @@ describe("MKeyVerificationConclusion", () => {
|
||||||
|
|
||||||
const event = new MatrixEvent({ type: "m.key.verification.done" });
|
const event = new MatrixEvent({ type: "m.key.verification.done" });
|
||||||
event.verificationRequest = getMockVerificationRequest({ done: true, otherUserId: "@someuser:domain" });
|
event.verificationRequest = getMockVerificationRequest({ done: true, otherUserId: "@someuser:domain" });
|
||||||
const renderer = TestRenderer.create(<MKeyVerificationConclusion mxEvent={event} />);
|
const { container } = render(<MKeyVerificationConclusion mxEvent={event} />);
|
||||||
expect(renderer.toJSON()).toBeNull();
|
expect(container).toBeEmpty();
|
||||||
|
|
||||||
mockClient.checkUserTrust.mockReturnValue(trustworthy);
|
mockClient.checkUserTrust.mockReturnValue(trustworthy);
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ describe("MKeyVerificationConclusion", () => {
|
||||||
"@anotheruser:domain",
|
"@anotheruser:domain",
|
||||||
new UserTrustLevel(true, true, true),
|
new UserTrustLevel(true, true, true),
|
||||||
);
|
);
|
||||||
expect(renderer.toJSON()).toBeNull();
|
expect(container).toBeEmpty();
|
||||||
|
|
||||||
/* But when our user changes, we do rerender */
|
/* But when our user changes, we do rerender */
|
||||||
mockClient.emit(
|
mockClient.emit(
|
||||||
|
@ -130,6 +130,6 @@ describe("MKeyVerificationConclusion", () => {
|
||||||
event.verificationRequest.otherUserId,
|
event.verificationRequest.otherUserId,
|
||||||
new UserTrustLevel(true, true, true),
|
new UserTrustLevel(true, true, true),
|
||||||
);
|
);
|
||||||
expect(renderer.toJSON()).not.toBeNull();
|
expect(container).not.toBeEmpty();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -765,6 +765,20 @@ describe("MPollBody", () => {
|
||||||
expect(container).toMatchSnapshot();
|
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 () => {
|
it("renders a poll with local, non-local and invalid votes", async () => {
|
||||||
const votes = [
|
const votes = [
|
||||||
responseEvent("@a:example.com", "pizza", 12),
|
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,97 +10,104 @@ exports[`<MPollEndBody /> when poll start event does not exist in current timeli
|
||||||
|
|
||||||
exports[`<MPollEndBody /> when poll start event exists in current timeline renders an ended poll 1`] = `
|
exports[`<MPollEndBody /> when poll start event exists in current timeline renders an ended poll 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div>
|
||||||
class="mx_MPollBody"
|
<span
|
||||||
>
|
class="mx_Caption"
|
||||||
<h2
|
|
||||||
data-testid="pollQuestion"
|
|
||||||
>
|
>
|
||||||
Question?
|
Ended a poll
|
||||||
</h2>
|
</span>
|
||||||
<div
|
<div
|
||||||
class="mx_MPollBody_allOptions"
|
class="mx_MPollBody"
|
||||||
>
|
>
|
||||||
|
<h2
|
||||||
|
data-testid="pollQuestion"
|
||||||
|
>
|
||||||
|
Question?
|
||||||
|
</h2>
|
||||||
<div
|
<div
|
||||||
class="mx_PollOption mx_PollOption_ended"
|
class="mx_MPollBody_allOptions"
|
||||||
data-testid="pollOption-socks"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_PollOption_endedOption"
|
class="mx_PollOption mx_PollOption_ended"
|
||||||
data-value="socks"
|
data-testid="pollOption-socks"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_PollOption_content"
|
class="mx_PollOption_endedOption"
|
||||||
|
data-value="socks"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_PollOption_optionText"
|
class="mx_PollOption_content"
|
||||||
>
|
>
|
||||||
Socks
|
<div
|
||||||
|
class="mx_PollOption_optionText"
|
||||||
|
>
|
||||||
|
Socks
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_PollOption_optionVoteCount"
|
||||||
|
>
|
||||||
|
0 votes
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_PollOption_popularityBackground"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_PollOption_optionVoteCount"
|
class="mx_PollOption_popularityAmount"
|
||||||
>
|
style="width: 0%;"
|
||||||
0 votes
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_PollOption_popularityBackground"
|
class="mx_PollOption mx_PollOption_ended"
|
||||||
|
data-testid="pollOption-shoes"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_PollOption_popularityAmount"
|
class="mx_PollOption_endedOption"
|
||||||
style="width: 0%;"
|
data-value="shoes"
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_PollOption mx_PollOption_ended"
|
|
||||||
data-testid="pollOption-shoes"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_PollOption_endedOption"
|
|
||||||
data-value="shoes"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_PollOption_content"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_PollOption_optionText"
|
class="mx_PollOption_content"
|
||||||
>
|
>
|
||||||
Shoes
|
<div
|
||||||
</div>
|
class="mx_PollOption_optionText"
|
||||||
<div
|
>
|
||||||
class="mx_PollOption_optionVoteCount"
|
Shoes
|
||||||
>
|
</div>
|
||||||
0 votes
|
<div
|
||||||
|
class="mx_PollOption_optionVoteCount"
|
||||||
|
>
|
||||||
|
0 votes
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_PollOption_popularityBackground"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="mx_PollOption_popularityAmount"
|
class="mx_PollOption_popularityBackground"
|
||||||
style="width: 0%;"
|
>
|
||||||
/>
|
<div
|
||||||
|
class="mx_PollOption_popularityAmount"
|
||||||
|
style="width: 0%;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_MPollBody_totalVotes"
|
|
||||||
data-testid="totalVotes"
|
|
||||||
>
|
|
||||||
Final result based on 0 votes
|
|
||||||
<div
|
<div
|
||||||
class="mx_Spinner"
|
class="mx_MPollBody_totalVotes"
|
||||||
|
data-testid="totalVotes"
|
||||||
>
|
>
|
||||||
|
Final result based on 0 votes
|
||||||
<div
|
<div
|
||||||
aria-label="Loading…"
|
class="mx_Spinner"
|
||||||
class="mx_Spinner_icon"
|
>
|
||||||
data-testid="spinner"
|
<div
|
||||||
role="progressbar"
|
aria-label="Loading…"
|
||||||
style="width: 16px; height: 16px;"
|
class="mx_Spinner_icon"
|
||||||
/>
|
data-testid="spinner"
|
||||||
|
role="progressbar"
|
||||||
|
style="width: 16px; height: 16px;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,9 +18,8 @@ import React from "react";
|
||||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||||
import { Room } from "matrix-js-sdk/src/matrix";
|
import { Room } from "matrix-js-sdk/src/matrix";
|
||||||
import { EventType } from "matrix-js-sdk/src/@types/event";
|
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||||
import { act } from "react-dom/test-utils";
|
|
||||||
import { mocked } from "jest-mock";
|
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 SpaceStore from "../../../../src/stores/spaces/SpaceStore";
|
||||||
import { MetaSpace } from "../../../../src/stores/spaces";
|
import { MetaSpace } from "../../../../src/stores/spaces";
|
||||||
|
|
|
@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { ReactElement } from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom";
|
import { render } from "@testing-library/react";
|
||||||
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||||
|
@ -37,16 +37,9 @@ describe("CryptographyPanel", () => {
|
||||||
const rendered = render(<CryptographyPanel />);
|
const rendered = render(<CryptographyPanel />);
|
||||||
|
|
||||||
// Then it displays info about the user's session
|
// 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.length).toEqual(2);
|
||||||
expect(codes[0].innerHTML).toEqual(sessionId);
|
expect(codes[0].innerHTML).toEqual(sessionId);
|
||||||
expect(codes[1].innerHTML).toEqual(sessionKeyFormatted);
|
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.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { fireEvent, render } from "@testing-library/react";
|
import { act, fireEvent, render } from "@testing-library/react";
|
||||||
import { act } from "react-dom/test-utils";
|
|
||||||
import { CrossSigningInfo } from "matrix-js-sdk/src/crypto/CrossSigning";
|
import { CrossSigningInfo } from "matrix-js-sdk/src/crypto/CrossSigning";
|
||||||
import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo";
|
import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo";
|
||||||
import { sleep } from "matrix-js-sdk/src/utils";
|
import { sleep } from "matrix-js-sdk/src/utils";
|
||||||
|
|
|
@ -25,8 +25,7 @@ import {
|
||||||
PushRuleActionName,
|
PushRuleActionName,
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import { IThreepid, ThreepidMedium } from "matrix-js-sdk/src/@types/threepids";
|
import { IThreepid, ThreepidMedium } from "matrix-js-sdk/src/@types/threepids";
|
||||||
import { act } from "react-dom/test-utils";
|
import { act, fireEvent, getByTestId, render, screen, waitFor } from "@testing-library/react";
|
||||||
import { fireEvent, getByTestId, render, screen, waitFor } from "@testing-library/react";
|
|
||||||
|
|
||||||
import Notifications from "../../../../src/components/views/settings/Notifications";
|
import Notifications from "../../../../src/components/views/settings/Notifications";
|
||||||
import SettingsStore from "../../../../src/settings/SettingsStore";
|
import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||||
|
|
|
@ -12,8 +12,8 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { render } from "@testing-library/react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { renderIntoDocument } from "react-dom/test-utils";
|
|
||||||
|
|
||||||
import SettingsFieldset from "../../../../src/components/views/settings/SettingsFieldset";
|
import SettingsFieldset from "../../../../src/components/views/settings/SettingsFieldset";
|
||||||
|
|
||||||
|
@ -21,24 +21,19 @@ describe("<SettingsFieldset />", () => {
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
"legend": "Who can read history?",
|
"legend": "Who can read history?",
|
||||||
"children": <div>test</div>,
|
"children": <div>test</div>,
|
||||||
"data-test-id": "test",
|
"data-testid": "test",
|
||||||
};
|
};
|
||||||
const getComponent = (props = {}) => {
|
const getComponent = (props = {}) => {
|
||||||
const wrapper = renderIntoDocument<HTMLDivElement>(
|
return render(<SettingsFieldset {...defaultProps} {...props} />);
|
||||||
<div>
|
|
||||||
<SettingsFieldset {...defaultProps} {...props} />
|
|
||||||
</div>,
|
|
||||||
) as HTMLDivElement;
|
|
||||||
return wrapper.children[0];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
it("renders fieldset without description", () => {
|
it("renders fieldset without description", () => {
|
||||||
expect(getComponent()).toMatchSnapshot();
|
expect(getComponent().asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders fieldset with plain text description", () => {
|
it("renders fieldset with plain text description", () => {
|
||||||
const description = "Changes to who can read history.";
|
const description = "Changes to who can read history.";
|
||||||
expect(getComponent({ description })).toMatchSnapshot();
|
expect(getComponent({ description }).asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders fieldset with react description", () => {
|
it("renders fieldset with react description", () => {
|
||||||
|
@ -48,6 +43,6 @@ describe("<SettingsFieldset />", () => {
|
||||||
<a href="#test">a link</a>
|
<a href="#test">a link</a>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
expect(getComponent({ description })).toMatchSnapshot();
|
expect(getComponent({ description }).asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,66 +1,72 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`<SettingsFieldset /> renders fieldset with plain text description 1`] = `
|
exports[`<SettingsFieldset /> renders fieldset with plain text description 1`] = `
|
||||||
<fieldset
|
<DocumentFragment>
|
||||||
class="mx_SettingsFieldset"
|
<fieldset
|
||||||
data-test-id="test"
|
class="mx_SettingsFieldset"
|
||||||
>
|
data-testid="test"
|
||||||
<legend
|
|
||||||
class="mx_SettingsFieldset_legend"
|
|
||||||
>
|
>
|
||||||
Who can read history?
|
<legend
|
||||||
</legend>
|
class="mx_SettingsFieldset_legend"
|
||||||
<div
|
>
|
||||||
class="mx_SettingsFieldset_description"
|
Who can read history?
|
||||||
>
|
</legend>
|
||||||
Changes to who can read history.
|
<div
|
||||||
</div>
|
class="mx_SettingsFieldset_description"
|
||||||
<div>
|
>
|
||||||
test
|
Changes to who can read history.
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
<div>
|
||||||
|
test
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</DocumentFragment>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`<SettingsFieldset /> renders fieldset with react description 1`] = `
|
exports[`<SettingsFieldset /> renders fieldset with react description 1`] = `
|
||||||
<fieldset
|
<DocumentFragment>
|
||||||
class="mx_SettingsFieldset"
|
<fieldset
|
||||||
data-test-id="test"
|
class="mx_SettingsFieldset"
|
||||||
>
|
data-testid="test"
|
||||||
<legend
|
|
||||||
class="mx_SettingsFieldset_legend"
|
|
||||||
>
|
>
|
||||||
Who can read history?
|
<legend
|
||||||
</legend>
|
class="mx_SettingsFieldset_legend"
|
||||||
<div
|
|
||||||
class="mx_SettingsFieldset_description"
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
Test
|
|
||||||
</p>
|
|
||||||
<a
|
|
||||||
href="#test"
|
|
||||||
>
|
>
|
||||||
a link
|
Who can read history?
|
||||||
</a>
|
</legend>
|
||||||
</div>
|
<div
|
||||||
<div>
|
class="mx_SettingsFieldset_description"
|
||||||
test
|
>
|
||||||
</div>
|
<p>
|
||||||
</fieldset>
|
Test
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href="#test"
|
||||||
|
>
|
||||||
|
a link
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
test
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</DocumentFragment>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`<SettingsFieldset /> renders fieldset without description 1`] = `
|
exports[`<SettingsFieldset /> renders fieldset without description 1`] = `
|
||||||
<fieldset
|
<DocumentFragment>
|
||||||
class="mx_SettingsFieldset"
|
<fieldset
|
||||||
data-test-id="test"
|
class="mx_SettingsFieldset"
|
||||||
>
|
data-testid="test"
|
||||||
<legend
|
|
||||||
class="mx_SettingsFieldset_legend"
|
|
||||||
>
|
>
|
||||||
Who can read history?
|
<legend
|
||||||
</legend>
|
class="mx_SettingsFieldset_legend"
|
||||||
<div>
|
>
|
||||||
test
|
Who can read history?
|
||||||
</div>
|
</legend>
|
||||||
</fieldset>
|
<div>
|
||||||
|
test
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</DocumentFragment>
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -15,8 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { fireEvent, render } from "@testing-library/react";
|
import { act, fireEvent, render } from "@testing-library/react";
|
||||||
import { act } from "react-dom/test-utils";
|
|
||||||
|
|
||||||
import CurrentDeviceSection from "../../../../../src/components/views/settings/devices/CurrentDeviceSection";
|
import CurrentDeviceSection from "../../../../../src/components/views/settings/devices/CurrentDeviceSection";
|
||||||
import { DeviceType } from "../../../../../src/utils/device/parseUserAgent";
|
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.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { fireEvent, render } from "@testing-library/react";
|
import { act, fireEvent, render } from "@testing-library/react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { act } from "react-dom/test-utils";
|
|
||||||
|
|
||||||
import SelectableDeviceTile from "../../../../../src/components/views/settings/devices/SelectableDeviceTile";
|
import SelectableDeviceTile from "../../../../../src/components/views/settings/devices/SelectableDeviceTile";
|
||||||
import { DeviceType } from "../../../../../src/utils/device/parseUserAgent";
|
import { DeviceType } from "../../../../../src/utils/device/parseUserAgent";
|
||||||
|
|
|
@ -15,8 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { fireEvent, render, RenderResult } from "@testing-library/react";
|
import { act, fireEvent, render, RenderResult } from "@testing-library/react";
|
||||||
import { act } from "react-dom/test-utils";
|
|
||||||
import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo";
|
import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { DeviceTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning";
|
import { DeviceTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning";
|
||||||
|
|
|
@ -16,8 +16,8 @@ limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { mocked } from "jest-mock";
|
import { mocked } from "jest-mock";
|
||||||
import { renderIntoDocument, Simulate } from "react-dom/test-utils";
|
import { act, Simulate } from "react-dom/test-utils";
|
||||||
import { act } from "react-dom/test-utils";
|
import { fireEvent, render, RenderResult } from "@testing-library/react";
|
||||||
import { EventType, MatrixClient, Room } from "matrix-js-sdk/src/matrix";
|
import { EventType, MatrixClient, Room } from "matrix-js-sdk/src/matrix";
|
||||||
import { GuestAccess, HistoryVisibility, JoinRule } from "matrix-js-sdk/src/@types/partials";
|
import { GuestAccess, HistoryVisibility, JoinRule } from "matrix-js-sdk/src/@types/partials";
|
||||||
|
|
||||||
|
@ -83,25 +83,18 @@ describe("<SpaceSettingsVisibilityTab />", () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const getComponent = (props = {}) => {
|
const getComponent = (props = {}) => {
|
||||||
const wrapper = renderIntoDocument<HTMLSpanElement>(
|
return render(<SpaceSettingsVisibilityTab {...defaultProps} {...props} />);
|
||||||
// wrap in element so renderIntoDocument can render functional component
|
|
||||||
<span>
|
|
||||||
<SpaceSettingsVisibilityTab {...defaultProps} {...props} />
|
|
||||||
</span>,
|
|
||||||
) as HTMLSpanElement;
|
|
||||||
return wrapper.children[0];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getByTestId = (container: Element, id: string) => container.querySelector(`[data-test-id=${id}]`);
|
const toggleGuestAccessSection = async ({ getByTestId }: RenderResult) => {
|
||||||
const toggleGuestAccessSection = async (component: Element) => {
|
const toggleButton = getByTestId("toggle-guest-access-btn")!;
|
||||||
const toggleButton = getByTestId(component, "toggle-guest-access-btn")!;
|
fireEvent.click(toggleButton);
|
||||||
await act(async () => {
|
|
||||||
Simulate.click(toggleButton);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
const getGuestAccessToggle = (component: Element) => component.querySelector('[aria-label="Enable guest access"]');
|
const getGuestAccessToggle = ({ container }: RenderResult) =>
|
||||||
const getHistoryVisibilityToggle = (component: Element) => component.querySelector('[aria-label="Preview Space"]');
|
container.querySelector('[aria-label="Enable guest access"]');
|
||||||
const getErrorMessage = (component: Element) => getByTestId(component, "space-settings-error")?.textContent;
|
const getHistoryVisibilityToggle = ({ container }: RenderResult) =>
|
||||||
|
container.querySelector('[aria-label="Preview Space"]');
|
||||||
|
const getErrorMessage = ({ getByTestId }: RenderResult) => getByTestId("space-settings-error")?.textContent;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
(mockMatrixClient.sendStateEvent as jest.Mock).mockClear().mockResolvedValue({});
|
(mockMatrixClient.sendStateEvent as jest.Mock).mockClear().mockResolvedValue({});
|
||||||
|
@ -113,18 +106,18 @@ describe("<SpaceSettingsVisibilityTab />", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders container", () => {
|
it("renders container", () => {
|
||||||
const component = getComponent();
|
const { asFragment } = getComponent();
|
||||||
expect(component).toMatchSnapshot();
|
expect(asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("for a private space", () => {
|
describe("for a private space", () => {
|
||||||
const joinRule = JoinRule.Invite;
|
const joinRule = JoinRule.Invite;
|
||||||
it("does not render addresses section", () => {
|
it("does not render addresses section", () => {
|
||||||
const space = makeMockSpace(mockMatrixClient, joinRule);
|
const space = makeMockSpace(mockMatrixClient, joinRule);
|
||||||
const component = getComponent({ space });
|
const { queryByTestId } = getComponent({ space });
|
||||||
|
|
||||||
expect(getByTestId(component, "published-address-fieldset")).toBeFalsy();
|
expect(queryByTestId("published-address-fieldset")).toBeFalsy();
|
||||||
expect(getByTestId(component, "local-address-fieldset")).toBeFalsy();
|
expect(queryByTestId("local-address-fieldset")).toBeFalsy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -152,10 +145,7 @@ describe("<SpaceSettingsVisibilityTab />", () => {
|
||||||
|
|
||||||
expect(guestAccessInput?.getAttribute("aria-checked")).toEqual("true");
|
expect(guestAccessInput?.getAttribute("aria-checked")).toEqual("true");
|
||||||
|
|
||||||
await act(async () => {
|
fireEvent.click(guestAccessInput!);
|
||||||
Simulate.click(guestAccessInput!);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(mockMatrixClient.sendStateEvent).toHaveBeenCalledWith(
|
expect(mockMatrixClient.sendStateEvent).toHaveBeenCalledWith(
|
||||||
mockSpaceId,
|
mockSpaceId,
|
||||||
EventType.RoomGuestAccess,
|
EventType.RoomGuestAccess,
|
||||||
|
@ -200,17 +190,14 @@ describe("<SpaceSettingsVisibilityTab />", () => {
|
||||||
expect(getHistoryVisibilityToggle(component)?.getAttribute("aria-checked")).toEqual("false");
|
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 space = makeMockSpace(mockMatrixClient, joinRule, guestRule, historyRule);
|
||||||
const component = getComponent({ space });
|
const component = getComponent({ space });
|
||||||
|
|
||||||
// toggle off because space settings is != WorldReadable
|
// toggle off because space settings is != WorldReadable
|
||||||
expect(getHistoryVisibilityToggle(component)?.getAttribute("aria-checked")).toEqual("false");
|
expect(getHistoryVisibilityToggle(component)?.getAttribute("aria-checked")).toEqual("false");
|
||||||
|
|
||||||
await act(async () => {
|
fireEvent.click(getHistoryVisibilityToggle(component)!);
|
||||||
Simulate.click(getHistoryVisibilityToggle(component)!);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(mockMatrixClient.sendStateEvent).toHaveBeenCalledWith(
|
expect(mockMatrixClient.sendStateEvent).toHaveBeenCalledWith(
|
||||||
mockSpaceId,
|
mockSpaceId,
|
||||||
EventType.RoomHistoryVisibility,
|
EventType.RoomHistoryVisibility,
|
||||||
|
@ -243,10 +230,10 @@ describe("<SpaceSettingsVisibilityTab />", () => {
|
||||||
|
|
||||||
it("renders addresses section", () => {
|
it("renders addresses section", () => {
|
||||||
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule);
|
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule);
|
||||||
const component = getComponent({ space });
|
const { getByTestId } = getComponent({ space });
|
||||||
|
|
||||||
expect(getByTestId(component, "published-address-fieldset")).toBeTruthy();
|
expect(getByTestId("published-address-fieldset")).toBeTruthy();
|
||||||
expect(getByTestId(component, "local-address-fieldset")).toBeTruthy();
|
expect(getByTestId("local-address-fieldset")).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,118 +16,119 @@ exports[`<SpaceSettingsVisibilityTab /> for a public space Access renders guest
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`<SpaceSettingsVisibilityTab /> renders container 1`] = `
|
exports[`<SpaceSettingsVisibilityTab /> renders container 1`] = `
|
||||||
<div
|
<DocumentFragment>
|
||||||
class="mx_SettingsTab"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsTab_heading"
|
class="mx_SettingsTab"
|
||||||
>
|
>
|
||||||
Visibility
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<fieldset
|
|
||||||
class="mx_SettingsFieldset"
|
|
||||||
data-test-id="access-fieldset"
|
|
||||||
>
|
|
||||||
<legend
|
|
||||||
class="mx_SettingsFieldset_legend"
|
|
||||||
>
|
|
||||||
Access
|
|
||||||
</legend>
|
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsFieldset_description"
|
class="mx_SettingsTab_heading"
|
||||||
>
|
>
|
||||||
Decide who can view and join mock-space.
|
Visibility
|
||||||
</div>
|
</div>
|
||||||
<label
|
<fieldset
|
||||||
class="mx_StyledRadioButton mx_JoinRuleSettings_radioButton mx_StyledRadioButton_disabled mx_StyledRadioButton_checked"
|
class="mx_SettingsFieldset"
|
||||||
|
data-testid="access-fieldset"
|
||||||
>
|
>
|
||||||
<input
|
<legend
|
||||||
aria-describedby="joinRule-invite-description"
|
class="mx_SettingsFieldset_legend"
|
||||||
checked=""
|
|
||||||
disabled=""
|
|
||||||
id="joinRule-invite"
|
|
||||||
name="joinRule"
|
|
||||||
type="radio"
|
|
||||||
value="invite"
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<div />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_StyledRadioButton_content"
|
|
||||||
>
|
>
|
||||||
Private (invite only)
|
Access
|
||||||
</div>
|
</legend>
|
||||||
<div
|
<div
|
||||||
class="mx_StyledRadioButton_spacer"
|
class="mx_SettingsFieldset_description"
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<span
|
|
||||||
id="joinRule-invite-description"
|
|
||||||
>
|
|
||||||
Only invited people can join.
|
|
||||||
</span>
|
|
||||||
<label
|
|
||||||
class="mx_StyledRadioButton mx_JoinRuleSettings_radioButton mx_StyledRadioButton_disabled"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
aria-describedby="joinRule-public-description"
|
|
||||||
disabled=""
|
|
||||||
id="joinRule-public"
|
|
||||||
name="joinRule"
|
|
||||||
type="radio"
|
|
||||||
value="public"
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<div />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mx_StyledRadioButton_content"
|
|
||||||
>
|
>
|
||||||
Public
|
Decide who can view and join mock-space.
|
||||||
</div>
|
</div>
|
||||||
<div
|
<label
|
||||||
class="mx_StyledRadioButton_spacer"
|
class="mx_StyledRadioButton mx_JoinRuleSettings_radioButton mx_StyledRadioButton_disabled mx_StyledRadioButton_checked"
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<span
|
|
||||||
id="joinRule-public-description"
|
|
||||||
>
|
|
||||||
Anyone can find and join.
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
class="mx_SettingsTab_toggleWithDescription"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_SettingsFlag"
|
|
||||||
>
|
>
|
||||||
<span
|
<input
|
||||||
class="mx_SettingsFlag_label"
|
aria-describedby="joinRule-invite-description"
|
||||||
>
|
checked=""
|
||||||
Preview Space
|
disabled=""
|
||||||
</span>
|
id="joinRule-invite"
|
||||||
<div
|
name="joinRule"
|
||||||
aria-checked="true"
|
type="radio"
|
||||||
aria-disabled="false"
|
value="invite"
|
||||||
aria-label="Preview Space"
|
/>
|
||||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on mx_ToggleSwitch_enabled"
|
<div>
|
||||||
role="switch"
|
<div />
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_ToggleSwitch_ball"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_StyledRadioButton_content"
|
||||||
|
>
|
||||||
|
Private (invite only)
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_StyledRadioButton_spacer"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<span
|
||||||
|
id="joinRule-invite-description"
|
||||||
|
>
|
||||||
|
Only invited people can join.
|
||||||
|
</span>
|
||||||
|
<label
|
||||||
|
class="mx_StyledRadioButton mx_JoinRuleSettings_radioButton mx_StyledRadioButton_disabled"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
aria-describedby="joinRule-public-description"
|
||||||
|
disabled=""
|
||||||
|
id="joinRule-public"
|
||||||
|
name="joinRule"
|
||||||
|
type="radio"
|
||||||
|
value="public"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_StyledRadioButton_content"
|
||||||
|
>
|
||||||
|
Public
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_StyledRadioButton_spacer"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<span
|
||||||
|
id="joinRule-public-description"
|
||||||
|
>
|
||||||
|
Anyone can find and join.
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
class="mx_SettingsTab_toggleWithDescription"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_SettingsFlag"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="mx_SettingsFlag_label"
|
||||||
|
>
|
||||||
|
Preview Space
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
aria-checked="true"
|
||||||
|
aria-disabled="false"
|
||||||
|
aria-label="Preview Space"
|
||||||
|
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_on mx_ToggleSwitch_enabled"
|
||||||
|
role="switch"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mx_ToggleSwitch_ball"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
Allow people to preview your space before they join.
|
||||||
|
<br />
|
||||||
|
<b>
|
||||||
|
Recommended for public spaces.
|
||||||
|
</b>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
</fieldset>
|
||||||
Allow people to preview your space before they join.
|
</div>
|
||||||
<br />
|
</DocumentFragment>
|
||||||
<b>
|
|
||||||
Recommended for public spaces.
|
|
||||||
</b>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
</div>
|
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -14,37 +14,32 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { render } from "@testing-library/react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { renderIntoDocument } from "react-dom/test-utils";
|
|
||||||
|
|
||||||
import Heading from "../../../../src/components/views/typography/Heading";
|
import Heading from "../../../../src/components/views/typography/Heading";
|
||||||
describe("<Heading />", () => {
|
describe("<Heading />", () => {
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
size: "h1",
|
"size": "h1",
|
||||||
children: <div>test</div>,
|
"children": <div>test</div>,
|
||||||
["data-test-id"]: "test",
|
"data-testid": "test",
|
||||||
className: "test",
|
"className": "test",
|
||||||
} as any;
|
} as any;
|
||||||
const getComponent = (props = {}) => {
|
const getComponent = (props = {}) => {
|
||||||
const wrapper = renderIntoDocument<HTMLDivElement>(
|
return render(<Heading {...defaultProps} {...props} />);
|
||||||
<div>
|
|
||||||
<Heading {...defaultProps} {...props} />
|
|
||||||
</div>,
|
|
||||||
) as HTMLDivElement;
|
|
||||||
return wrapper.children[0];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
it("renders h1 with correct attributes", () => {
|
it("renders h1 with correct attributes", () => {
|
||||||
expect(getComponent({ size: "h1" })).toMatchSnapshot();
|
expect(getComponent({ size: "h1" }).asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
it("renders h2 with correct attributes", () => {
|
it("renders h2 with correct attributes", () => {
|
||||||
expect(getComponent({ size: "h2" })).toMatchSnapshot();
|
expect(getComponent({ size: "h2" }).asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
it("renders h3 with correct attributes", () => {
|
it("renders h3 with correct attributes", () => {
|
||||||
expect(getComponent({ size: "h3" })).toMatchSnapshot();
|
expect(getComponent({ size: "h3" }).asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders h4 with correct attributes", () => {
|
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
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`<Heading /> renders h1 with correct attributes 1`] = `
|
exports[`<Heading /> renders h1 with correct attributes 1`] = `
|
||||||
<h1
|
<DocumentFragment>
|
||||||
class="mx_Heading_h1 test"
|
<h1
|
||||||
data-test-id="test"
|
class="mx_Heading_h1 test"
|
||||||
>
|
data-testid="test"
|
||||||
<div>
|
>
|
||||||
test
|
<div>
|
||||||
</div>
|
test
|
||||||
</h1>
|
</div>
|
||||||
|
</h1>
|
||||||
|
</DocumentFragment>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`<Heading /> renders h2 with correct attributes 1`] = `
|
exports[`<Heading /> renders h2 with correct attributes 1`] = `
|
||||||
<h2
|
<DocumentFragment>
|
||||||
class="mx_Heading_h2 test"
|
<h2
|
||||||
data-test-id="test"
|
class="mx_Heading_h2 test"
|
||||||
>
|
data-testid="test"
|
||||||
<div>
|
>
|
||||||
test
|
<div>
|
||||||
</div>
|
test
|
||||||
</h2>
|
</div>
|
||||||
|
</h2>
|
||||||
|
</DocumentFragment>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`<Heading /> renders h3 with correct attributes 1`] = `
|
exports[`<Heading /> renders h3 with correct attributes 1`] = `
|
||||||
<h3
|
<DocumentFragment>
|
||||||
class="mx_Heading_h3 test"
|
<h3
|
||||||
data-test-id="test"
|
class="mx_Heading_h3 test"
|
||||||
>
|
data-testid="test"
|
||||||
<div>
|
>
|
||||||
test
|
<div>
|
||||||
</div>
|
test
|
||||||
</h3>
|
</div>
|
||||||
|
</h3>
|
||||||
|
</DocumentFragment>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`<Heading /> renders h4 with correct attributes 1`] = `
|
exports[`<Heading /> renders h4 with correct attributes 1`] = `
|
||||||
<h4
|
<DocumentFragment>
|
||||||
class="mx_Heading_h4 test"
|
<h4
|
||||||
data-test-id="test"
|
class="mx_Heading_h4 test"
|
||||||
>
|
data-testid="test"
|
||||||
<div>
|
>
|
||||||
test
|
<div>
|
||||||
</div>
|
test
|
||||||
</h4>
|
</div>
|
||||||
|
</h4>
|
||||||
|
</DocumentFragment>
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -16,8 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import { act } from "react-dom/test-utils";
|
import { act, render, screen } from "@testing-library/react";
|
||||||
import { render, screen } from "@testing-library/react";
|
|
||||||
|
|
||||||
import { useTopic } from "../src/hooks/room/useTopic";
|
import { useTopic } from "../src/hooks/room/useTopic";
|
||||||
import { mkEvent, stubClient } from "./test-utils";
|
import { mkEvent, stubClient } from "./test-utils";
|
||||||
|
|
|
@ -315,4 +315,45 @@ describe("HTMLExport", () => {
|
||||||
expect(fileName).not.toMatch(/^files\/hello/);
|
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");
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -12,7 +12,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Container } from "react-dom";
|
|
||||||
import { MatrixClient, Room, RoomMember } from "matrix-js-sdk/src/matrix";
|
import { MatrixClient, Room, RoomMember } from "matrix-js-sdk/src/matrix";
|
||||||
import { render, RenderResult } from "@testing-library/react";
|
import { render, RenderResult } from "@testing-library/react";
|
||||||
|
|
||||||
|
@ -33,7 +32,7 @@ describe("VoiceBroadcastHeader", () => {
|
||||||
let client: MatrixClient;
|
let client: MatrixClient;
|
||||||
let room: Room;
|
let room: Room;
|
||||||
const sender = new RoomMember(roomId, userId);
|
const sender = new RoomMember(roomId, userId);
|
||||||
let container: Container;
|
let container: RenderResult["container"];
|
||||||
|
|
||||||
const renderHeader = (live: VoiceBroadcastLiveness, showBroadcast?: boolean, buffering?: boolean): RenderResult => {
|
const renderHeader = (live: VoiceBroadcastLiveness, showBroadcast?: boolean, buffering?: boolean): RenderResult => {
|
||||||
return render(
|
return render(
|
||||||
|
|
63
yarn.lock
63
yarn.lock
|
@ -2336,13 +2336,6 @@
|
||||||
hoist-non-react-statics "^3.3.0"
|
hoist-non-react-statics "^3.3.0"
|
||||||
redux "^4.0.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":
|
"@types/react-transition-group@^4.4.0":
|
||||||
version "4.4.5"
|
version "4.4.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416"
|
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"
|
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
|
||||||
integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
|
integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
|
||||||
|
|
||||||
"@types/sanitize-html@^2.3.1":
|
"@types/sanitize-html@2.8.0":
|
||||||
version "2.8.0"
|
version "2.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/sanitize-html/-/sanitize-html-2.8.0.tgz#c53d3114d832734fc299568a3458a49f9edc1eef"
|
resolved "https://registry.yarnpkg.com/@types/sanitize-html/-/sanitize-html-2.8.0.tgz#c53d3114d832734fc299568a3458a49f9edc1eef"
|
||||||
integrity sha512-Uih6caOm3DsBYnVGOYn0A9NoTNe1c4aPStmHC/YA2JrpP9kx//jzaRcIklFvSpvVQEcpl/ZCr4DgISSf/YxTvg==
|
integrity sha512-Uih6caOm3DsBYnVGOYn0A9NoTNe1c4aPStmHC/YA2JrpP9kx//jzaRcIklFvSpvVQEcpl/ZCr4DgISSf/YxTvg==
|
||||||
|
@ -3846,15 +3839,6 @@ dom-helpers@^5.0.1:
|
||||||
"@babel/runtime" "^7.8.7"
|
"@babel/runtime" "^7.8.7"
|
||||||
csstype "^3.0.2"
|
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:
|
dom-serializer@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53"
|
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"
|
domhandler "^5.0.2"
|
||||||
entities "^4.2.0"
|
entities "^4.2.0"
|
||||||
|
|
||||||
domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0:
|
domelementtype@^2.3.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
|
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
|
||||||
integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
|
integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
|
||||||
|
@ -3876,13 +3860,6 @@ domexception@^4.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
webidl-conversions "^7.0.0"
|
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:
|
domhandler@^5.0.1, domhandler@^5.0.2, domhandler@^5.0.3:
|
||||||
version "5.0.3"
|
version "5.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31"
|
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:
|
dependencies:
|
||||||
domelementtype "^2.3.0"
|
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:
|
domutils@^3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c"
|
resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c"
|
||||||
|
@ -3980,11 +3948,6 @@ enquirer@^2.3.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
ansi-colors "^4.1.1"
|
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:
|
entities@^4.2.0, entities@^4.3.0, entities@^4.4.0:
|
||||||
version "4.4.0"
|
version "4.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174"
|
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"
|
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.2.0.tgz#dbb3518d20b726524e4dd43de397eb0a95726961"
|
||||||
integrity sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==
|
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:
|
htmlparser2@^8.0.0, htmlparser2@^8.0.1:
|
||||||
version "8.0.1"
|
version "8.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.1.tgz#abaa985474fcefe269bc761a779b544d7196d010"
|
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"
|
object-assign "^4.1.1"
|
||||||
react-is "^16.12.0 || ^17.0.0 || ^18.0.0"
|
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"
|
version "17.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.2.tgz#4cd4ae5ef1ad5670fc0ef776e8cc7e1231d9866c"
|
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.2.tgz#4cd4ae5ef1ad5670fc0ef776e8cc7e1231d9866c"
|
||||||
integrity sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ==
|
integrity sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ==
|
||||||
|
@ -7863,14 +7816,14 @@ sanitize-filename@^1.6.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
truncate-utf8-bytes "^1.0.0"
|
truncate-utf8-bytes "^1.0.0"
|
||||||
|
|
||||||
sanitize-html@^2.3.2:
|
sanitize-html@2.8.0:
|
||||||
version "2.7.3"
|
version "2.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.7.3.tgz#166c868444ee4f9fd7352ac8c63fa86c343fc2bd"
|
resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.8.0.tgz#651d1d0e5b2d61b4ec6147cc46f6d6680eef98ce"
|
||||||
integrity sha512-jMaHG29ak4miiJ8wgqA1849iInqORgNv7SLfSw9LtfOhEUQ1C0YHKH73R+hgyufBW9ZFeJrb057k9hjlfBCVlw==
|
integrity sha512-ZsGyc6avnqgvEm3eMKrcy8xa7WM1MrGrfkGsUgQee2CU+vg3PCfNCexXwBDF/6dEPvaQ4k/QqRjnYKHL8xgNjg==
|
||||||
dependencies:
|
dependencies:
|
||||||
deepmerge "^4.2.2"
|
deepmerge "^4.2.2"
|
||||||
escape-string-regexp "^4.0.0"
|
escape-string-regexp "^4.0.0"
|
||||||
htmlparser2 "^6.0.0"
|
htmlparser2 "^8.0.0"
|
||||||
is-plain-object "^5.0.0"
|
is-plain-object "^5.0.0"
|
||||||
parse-srcset "^1.0.2"
|
parse-srcset "^1.0.2"
|
||||||
postcss "^8.3.11"
|
postcss "^8.3.11"
|
||||||
|
|
Loading…
Reference in a new issue