diff --git a/.eslintrc.js b/.eslintrc.js index a5c8070e61..8cc5890ca4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,12 +1,6 @@ module.exports = { - plugins: [ - "matrix-org", - ], - extends: [ - "plugin:matrix-org/babel", - "plugin:matrix-org/react", - "plugin:matrix-org/a11y", - ], + plugins: ["matrix-org"], + extends: ["plugin:matrix-org/babel", "plugin:matrix-org/react", "plugin:matrix-org/a11y"], env: { browser: true, node: true, @@ -40,34 +34,47 @@ module.exports = { ], // Ban matrix-js-sdk/src imports in favour of matrix-js-sdk/src/matrix imports to prevent unleashing hell. - "no-restricted-imports": ["error", { - "paths": [{ - "name": "matrix-js-sdk", - "message": "Please use matrix-js-sdk/src/matrix instead", - }, { - "name": "matrix-js-sdk/", - "message": "Please use matrix-js-sdk/src/matrix instead", - }, { - "name": "matrix-js-sdk/src", - "message": "Please use matrix-js-sdk/src/matrix instead", - }, { - "name": "matrix-js-sdk/src/", - "message": "Please use matrix-js-sdk/src/matrix instead", - }, { - "name": "matrix-js-sdk/src/index", - "message": "Please use matrix-js-sdk/src/matrix instead", - }, { - "name": "matrix-react-sdk", - "message": "Please use matrix-react-sdk/src/index instead", - }, { - "name": "matrix-react-sdk/", - "message": "Please use matrix-react-sdk/src/index instead", - }], - "patterns": [{ - "group": ["matrix-js-sdk/lib", "matrix-js-sdk/lib/", "matrix-js-sdk/lib/**"], - "message": "Please use matrix-js-sdk/src/* instead", - }], - }], + "no-restricted-imports": [ + "error", + { + paths: [ + { + name: "matrix-js-sdk", + message: "Please use matrix-js-sdk/src/matrix instead", + }, + { + name: "matrix-js-sdk/", + message: "Please use matrix-js-sdk/src/matrix instead", + }, + { + name: "matrix-js-sdk/src", + message: "Please use matrix-js-sdk/src/matrix instead", + }, + { + name: "matrix-js-sdk/src/", + message: "Please use matrix-js-sdk/src/matrix instead", + }, + { + name: "matrix-js-sdk/src/index", + message: "Please use matrix-js-sdk/src/matrix instead", + }, + { + name: "matrix-react-sdk", + message: "Please use matrix-react-sdk/src/index instead", + }, + { + name: "matrix-react-sdk/", + message: "Please use matrix-react-sdk/src/index instead", + }, + ], + patterns: [ + { + group: ["matrix-js-sdk/lib", "matrix-js-sdk/lib/", "matrix-js-sdk/lib/**"], + message: "Please use matrix-js-sdk/src/* instead", + }, + ], + }, + ], // There are too many a11y violations to fix at once // Turn violated rules off until they are fixed @@ -90,15 +97,8 @@ module.exports = { }, overrides: [ { - files: [ - "src/**/*.{ts,tsx}", - "test/**/*.{ts,tsx}", - "cypress/**/*.ts", - ], - extends: [ - "plugin:matrix-org/typescript", - "plugin:matrix-org/react", - ], + files: ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}", "cypress/**/*.ts"], + extends: ["plugin:matrix-org/typescript", "plugin:matrix-org/react"], rules: { // temporary disabled "@typescript-eslint/explicit-function-return-type": "off", @@ -151,12 +151,12 @@ module.exports = { "src/components/views/rooms/MessageComposer.tsx", "src/components/views/rooms/ReplyPreview.tsx", "src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx", - "src/components/views/settings/tabs/user/GeneralUserSettingsTab.tsx" + "src/components/views/settings/tabs/user/GeneralUserSettingsTab.tsx", ], rules: { "@typescript-eslint/no-var-requires": "off", }, - } + }, ], settings: { react: { @@ -166,7 +166,7 @@ module.exports = { }; function buildRestrictedPropertiesOptions(properties, message) { - return properties.map(prop => { + return properties.map((prop) => { let [object, property] = prop.split("."); if (object === "*") { object = undefined; diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 068c3ffccc..0527dcf64c 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,9 +2,9 @@ ## Checklist -* [ ] Tests written for new code (and old code if feasible) -* [ ] Linter and other CI checks pass -* [ ] Sign-off given on the changes (see [CONTRIBUTING.md](https://github.com/matrix-org/matrix-react-sdk/blob/develop/CONTRIBUTING.md)) +- [ ] Tests written for new code (and old code if feasible) +- [ ] Linter and other CI checks pass +- [ ] Sign-off given on the changes (see [CONTRIBUTING.md](https://github.com/matrix-org/matrix-react-sdk/blob/develop/CONTRIBUTING.md)) - - + --> + diff --git a/src/usercontent/index.ts b/src/usercontent/index.ts index 91a384cfc0..db710358c6 100644 --- a/src/usercontent/index.ts +++ b/src/usercontent/index.ts @@ -56,7 +56,7 @@ function remoteRender(event: MessageEvent): void { const body = document.body; // Don't display scrollbars if the link takes more than one line to display. - body.style .margin = "0px"; + body.style.margin = "0px"; body.style.overflow = "hidden"; body.appendChild(a); @@ -65,7 +65,7 @@ function remoteRender(event: MessageEvent): void { } } -window.onmessage = function(e: MessageEvent): void { +window.onmessage = function (e: MessageEvent): void { if (e.origin === window.location.origin) { if (e.data.blob) remoteRender(e); } diff --git a/src/utils/AutoDiscoveryUtils.tsx b/src/utils/AutoDiscoveryUtils.tsx index f995f93304..54829cd407 100644 --- a/src/utils/AutoDiscoveryUtils.tsx +++ b/src/utils/AutoDiscoveryUtils.tsx @@ -14,14 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { ReactNode } from 'react'; +import React, { ReactNode } from "react"; import { AutoDiscovery } from "matrix-js-sdk/src/autodiscovery"; import { logger } from "matrix-js-sdk/src/logger"; import { _t, _td, newTranslatableError } from "../languageHandler"; import { makeType } from "./TypeUtils"; -import SdkConfig from '../SdkConfig'; -import { ValidatedServerConfig } from './ValidatedServerConfig'; +import SdkConfig from "../SdkConfig"; +import { ValidatedServerConfig } from "./ValidatedServerConfig"; const LIVELINESS_DISCOVERY_ERRORS: string[] = [ AutoDiscovery.ERROR_INVALID_HOMESERVER, @@ -44,7 +44,9 @@ export default class AutoDiscoveryUtils { */ static isLivelinessError(error: string | Error): boolean { if (!error) return false; - return !!LIVELINESS_DISCOVERY_ERRORS.find(e => typeof error === "string" ? e === error : e === error.message); + return !!LIVELINESS_DISCOVERY_ERRORS.find((e) => + typeof error === "string" ? e === error : e === error.message, + ); } /** @@ -75,11 +77,15 @@ export default class AutoDiscoveryUtils { }, { a: (sub) => { - return { sub }; + return ( + + {sub} + + ); }, }, ); @@ -96,20 +102,20 @@ export default class AutoDiscoveryUtils { if (pageName === "register") { body = _t( "You can register, but some features will be unavailable until the identity server is " + - "back online. If you keep seeing this warning, check your configuration or contact a server " + - "admin.", + "back online. If you keep seeing this warning, check your configuration or contact a server " + + "admin.", ); } else if (pageName === "reset_password") { body = _t( "You can reset your password, but some features will be unavailable until the identity " + - "server is back online. If you keep seeing this warning, check your configuration or contact " + - "a server admin.", + "server is back online. If you keep seeing this warning, check your configuration or contact " + + "a server admin.", ); } else { body = _t( "You can log in, but some features will be unavailable until the identity server is " + - "back online. If you keep seeing this warning, check your configuration or contact a server " + - "admin.", + "back online. If you keep seeing this warning, check your configuration or contact a server " + + "admin.", ); } } @@ -119,8 +125,8 @@ export default class AutoDiscoveryUtils { serverErrorIsFatal: isFatalError, serverDeadError: (
- { title } -
{ body }
+ {title} +
{body}
), }; @@ -150,7 +156,7 @@ export default class AutoDiscoveryUtils { }; if (identityUrl) { - wellknownConfig['m.identity_server'] = { + wellknownConfig["m.identity_server"] = { base_url: identityUrl, }; } @@ -183,7 +189,11 @@ export default class AutoDiscoveryUtils { * @returns {Promise} Resolves to the validated configuration. */ static buildValidatedConfigFromDiscovery( - serverName: string, discoveryResult, syntaxOnly=false, isSynthetic=false): ValidatedServerConfig { + serverName: string, + discoveryResult, + syntaxOnly = false, + isSynthetic = false, + ): ValidatedServerConfig { if (!discoveryResult || !discoveryResult["m.homeserver"]) { // This shouldn't happen without major misconfiguration, so we'll log a bit of information // in the log so we can find this bit of codee but otherwise tell teh user "it broke". @@ -191,8 +201,8 @@ export default class AutoDiscoveryUtils { throw newTranslatableError(_td("Unexpected error resolving homeserver configuration")); } - const hsResult = discoveryResult['m.homeserver']; - const isResult = discoveryResult['m.identity_server']; + const hsResult = discoveryResult["m.homeserver"]; + const isResult = discoveryResult["m.identity_server"]; const defaultConfig = SdkConfig.get("validated_server_config"); @@ -203,7 +213,7 @@ export default class AutoDiscoveryUtils { // lack of identity server provided by the discovery method), we intentionally do not // validate it. This has already been validated and this helps some off-the-grid usage // of Element. - let preferredIdentityUrl = defaultConfig && defaultConfig['isUrl']; + let preferredIdentityUrl = defaultConfig && defaultConfig["isUrl"]; if (isResult && isResult.state === AutoDiscovery.SUCCESS) { preferredIdentityUrl = isResult["base_url"]; } else if (isResult && isResult.state !== AutoDiscovery.PROMPT) { diff --git a/src/utils/BrowserWorkarounds.ts b/src/utils/BrowserWorkarounds.ts index ea8ea2a04f..d241af3733 100644 --- a/src/utils/BrowserWorkarounds.ts +++ b/src/utils/BrowserWorkarounds.ts @@ -20,5 +20,5 @@ export function chromeFileInputFix(event: MouseEvent): void { // Workaround for Chromium Bug // Chrome does not fire onChange events if the same file is selected twice // Only required on Chromium-based browsers (Electron, Chrome, Edge, Opera, Vivaldi, etc) - event.currentTarget.value = ''; + event.currentTarget.value = ""; } diff --git a/src/utils/DMRoomMap.ts b/src/utils/DMRoomMap.ts index 811522a667..2844f5c8ef 100644 --- a/src/utils/DMRoomMap.ts +++ b/src/utils/DMRoomMap.ts @@ -22,7 +22,7 @@ import { EventType } from "matrix-js-sdk/src/@types/event"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { Optional } from "matrix-events-sdk"; -import { MatrixClientPeg } from '../MatrixClientPeg'; +import { MatrixClientPeg } from "../MatrixClientPeg"; /** * Class that takes a Matrix Client and flips the m.direct map @@ -35,10 +35,10 @@ export default class DMRoomMap { private static sharedInstance: DMRoomMap; // TODO: convert these to maps - private roomToUser: {[key: string]: string} = null; - private userToRooms: {[key: string]: string[]} = null; + private roomToUser: { [key: string]: string } = null; + private userToRooms: { [key: string]: string[] } = null; private hasSentOutPatchDirectAccountDataPatch: boolean; - private mDirectEvent: {[key: string]: string[]}; + private mDirectEvent: { [key: string]: string[] }; constructor(private readonly matrixClient: MatrixClient) { // see onAccountData @@ -102,23 +102,24 @@ export default class DMRoomMap { const selfRoomIds = userToRooms[myUserId]; if (selfRoomIds) { // any self-chats that should not be self-chats? - const guessedUserIdsThatChanged = selfRoomIds.map((roomId) => { - const room = this.matrixClient.getRoom(roomId); - if (room) { - const userId = room.guessDMUserId(); - if (userId && userId !== myUserId) { - return { userId, roomId }; + const guessedUserIdsThatChanged = selfRoomIds + .map((roomId) => { + const room = this.matrixClient.getRoom(roomId); + if (room) { + const userId = room.guessDMUserId(); + if (userId && userId !== myUserId) { + return { userId, roomId }; + } } - } - }).filter((ids) => !!ids); //filter out + }) + .filter((ids) => !!ids); //filter out // these are actually all legit self-chats // bail out if (!guessedUserIdsThatChanged.length) { return false; } userToRooms[myUserId] = selfRoomIds.filter((roomId) => { - return !guessedUserIdsThatChanged - .some((ids) => ids.roomId === roomId); + return !guessedUserIdsThatChanged.some((ids) => ids.roomId === roomId); }); guessedUserIdsThatChanged.forEach(({ userId, roomId }) => { const roomIds = userToRooms[userId]; @@ -151,11 +152,12 @@ export default class DMRoomMap { let commonRooms = this.getDMRoomsForUserId(ids[0]); for (let i = 1; i < ids.length; i++) { const userRooms = this.getDMRoomsForUserId(ids[i]); - commonRooms = commonRooms.filter(r => userRooms.includes(r)); + commonRooms = commonRooms.filter((r) => userRooms.includes(r)); } - const joinedRooms = commonRooms.map(r => MatrixClientPeg.get().getRoom(r)) - .filter(r => r && r.getMyMembership() === 'join'); + const joinedRooms = commonRooms + .map((r) => MatrixClientPeg.get().getRoom(r)) + .filter((r) => r && r.getMyMembership() === "join"); return joinedRooms[0]; } @@ -182,15 +184,15 @@ export default class DMRoomMap { return this.roomToUser[roomId]; } - public getUniqueRoomsWithIndividuals(): {[userId: string]: Room} { + public getUniqueRoomsWithIndividuals(): { [userId: string]: Room } { if (!this.roomToUser) return {}; // No rooms means no map. return Object.keys(this.roomToUser) - .map(r => ({ userId: this.getUserIdForRoomId(r), room: this.matrixClient.getRoom(r) })) - .filter(r => r.userId && r.room && r.room.getInvitedAndJoinedMemberCount() === 2) + .map((r) => ({ userId: this.getUserIdForRoomId(r), room: this.matrixClient.getRoom(r) })) + .filter((r) => r.userId && r.room && r.room.getInvitedAndJoinedMemberCount() === 2) .reduce((obj, r) => (obj[r.userId] = r.room) && obj, {}); } - private getUserToRooms(): {[key: string]: string[]} { + private getUserToRooms(): { [key: string]: string[] } { if (!this.userToRooms) { const userToRooms = this.mDirectEvent; const myUserId = this.matrixClient.getUserId(); @@ -200,8 +202,9 @@ export default class DMRoomMap { // to avoid multiple devices fighting to correct // the account data, only try to send the corrected // version once. - logger.warn(`Invalid m.direct account data detected ` + - `(self-chats that shouldn't be), patching it up.`); + logger.warn( + `Invalid m.direct account data detected ` + `(self-chats that shouldn't be), patching it up.`, + ); if (neededPatching && !this.hasSentOutPatchDirectAccountDataPatch) { this.hasSentOutPatchDirectAccountDataPatch = true; this.matrixClient.setAccountData(EventType.Direct, userToRooms); diff --git a/src/utils/DecryptFile.ts b/src/utils/DecryptFile.ts index dbcb0a85ed..aa9aaa428e 100644 --- a/src/utils/DecryptFile.ts +++ b/src/utils/DecryptFile.ts @@ -15,8 +15,8 @@ limitations under the License. */ // Pull in the encryption lib so that we can decrypt attachments. -import encrypt from 'matrix-encrypt-attachment'; -import { parseErrorResponse } from 'matrix-js-sdk/src/http-api'; +import encrypt from "matrix-encrypt-attachment"; +import { parseErrorResponse } from "matrix-js-sdk/src/http-api"; import { mediaFromContent } from "../customisations/Media"; import { IEncryptedFile, IMediaEventInfo } from "../customisations/models/IMediaEventContent"; @@ -47,10 +47,7 @@ export class DecryptError extends Error { * @param {IMediaEventInfo} info The info parameter taken from the matrix event. * @returns {Promise} Resolves to a Blob of the file. */ -export async function decryptFile( - file: IEncryptedFile, - info?: IMediaEventInfo, -): Promise { +export async function decryptFile(file: IEncryptedFile, info?: IMediaEventInfo): Promise { const media = mediaFromContent({ file }); let responseData: ArrayBuffer; @@ -74,7 +71,7 @@ export async function decryptFile( // they introduce XSS attacks if the Blob URI is viewed directly in the // browser (e.g. by copying the URI into a new tab or window.) // See warning at top of file. - let mimetype = info?.mimetype ? info.mimetype.split(";")[0].trim() : ''; + let mimetype = info?.mimetype ? info.mimetype.split(";")[0].trim() : ""; mimetype = getBlobSafeMimeType(mimetype); return new Blob([dataArray], { type: mimetype }); diff --git a/src/utils/DialogOpener.ts b/src/utils/DialogOpener.ts index 82d16962b2..49e6f85658 100644 --- a/src/utils/DialogOpener.ts +++ b/src/utils/DialogOpener.ts @@ -43,8 +43,7 @@ export class DialogOpener { private isRegistered = false; - private constructor() { - } + private constructor() {} // We could do this in the constructor, but then we wouldn't have // a function to call from Lifecycle to capture the class. @@ -56,11 +55,17 @@ export class DialogOpener { private onDispatch = (payload: ActionPayload) => { switch (payload.action) { - case 'open_room_settings': - Modal.createDialog(RoomSettingsDialog, { - roomId: payload.room_id || SdkContextClass.instance.roomViewStore.getRoomId(), - initialTabId: payload.initial_tab_id, - }, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true); + case "open_room_settings": + Modal.createDialog( + RoomSettingsDialog, + { + roomId: payload.room_id || SdkContextClass.instance.roomViewStore.getRoomId(), + initialTabId: payload.initial_tab_id, + }, + /*className=*/ null, + /*isPriority=*/ false, + /*isStatic=*/ true, + ); break; case Action.OpenForwardDialog: Modal.createDialog(ForwardDialog, { @@ -70,31 +75,52 @@ export class DialogOpener { }); break; case Action.OpenReportEventDialog: - Modal.createDialog(ReportEventDialog, { - mxEvent: payload.event, - }, 'mx_Dialog_reportEvent'); + Modal.createDialog( + ReportEventDialog, + { + mxEvent: payload.event, + }, + "mx_Dialog_reportEvent", + ); break; case Action.OpenSpacePreferences: - Modal.createDialog(SpacePreferencesDialog, { - initialTabId: payload.initalTabId, - space: payload.space, - }, null, false, true); + Modal.createDialog( + SpacePreferencesDialog, + { + initialTabId: payload.initalTabId, + space: payload.space, + }, + null, + false, + true, + ); break; case Action.OpenSpaceSettings: - Modal.createDialog(SpaceSettingsDialog, { - matrixClient: payload.space.client, - space: payload.space, - }, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true); + Modal.createDialog( + SpaceSettingsDialog, + { + matrixClient: payload.space.client, + space: payload.space, + }, + /*className=*/ null, + /*isPriority=*/ false, + /*isStatic=*/ true, + ); break; case Action.OpenInviteDialog: - Modal.createDialog(InviteDialog, { - kind: payload.kind, - call: payload.call, - roomId: payload.roomId, - }, classnames("mx_InviteDialog_flexWrapper", payload.className), false, true).finished - .then((results) => { - payload.onFinishedCallback?.(results); - }); + Modal.createDialog( + InviteDialog, + { + kind: payload.kind, + call: payload.call, + roomId: payload.roomId, + }, + classnames("mx_InviteDialog_flexWrapper", payload.className), + false, + true, + ).finished.then((results) => { + payload.onFinishedCallback?.(results); + }); break; case Action.OpenAddToExistingSpaceDialog: { const space = payload.space; diff --git a/src/utils/ErrorUtils.tsx b/src/utils/ErrorUtils.tsx index ff78fe076c..432e5693bf 100644 --- a/src/utils/ErrorUtils.tsx +++ b/src/utils/ErrorUtils.tsx @@ -17,7 +17,7 @@ limitations under the License. import React, { ReactNode } from "react"; import { MatrixError } from "matrix-js-sdk/src/http-api"; -import { _t, _td, Tags, TranslatedString } from '../languageHandler'; +import { _t, _td, Tags, TranslatedString } from "../languageHandler"; /** * Produce a translated error message for a @@ -40,48 +40,44 @@ export function messageForResourceLimitError( extraTranslations?: Tags, ): TranslatedString { let errString = strings[limitType]; - if (errString === undefined) errString = strings['']; + if (errString === undefined) errString = strings[""]; - const linkSub = sub => { + const linkSub = (sub) => { if (adminContact) { - return { sub }; + return ( + + {sub} + + ); } else { return sub; } }; - if (errString.includes('')) { - return _t(errString, {}, Object.assign({ 'a': linkSub }, extraTranslations)); + if (errString.includes("")) { + return _t(errString, {}, Object.assign({ a: linkSub }, extraTranslations)); } else { return _t(errString, {}, extraTranslations); } } export function messageForSyncError(err: Error): ReactNode { - if (err instanceof MatrixError && err.errcode === 'M_RESOURCE_LIMIT_EXCEEDED') { - const limitError = messageForResourceLimitError( - err.data.limit_type, - err.data.admin_contact, - { - 'monthly_active_user': _td("This homeserver has hit its Monthly Active User limit."), - 'hs_blocked': _td("This homeserver has been blocked by its administrator."), - '': _td("This homeserver has exceeded one of its resource limits."), - }, + if (err instanceof MatrixError && err.errcode === "M_RESOURCE_LIMIT_EXCEEDED") { + const limitError = messageForResourceLimitError(err.data.limit_type, err.data.admin_contact, { + "monthly_active_user": _td("This homeserver has hit its Monthly Active User limit."), + "hs_blocked": _td("This homeserver has been blocked by its administrator."), + "": _td("This homeserver has exceeded one of its resource limits."), + }); + const adminContact = messageForResourceLimitError(err.data.limit_type, err.data.admin_contact, { + "": _td("Please contact your service administrator to continue using the service."), + }); + return ( +
+
{limitError}
+
{adminContact}
+
); - const adminContact = messageForResourceLimitError( - err.data.limit_type, - err.data.admin_contact, - { - '': _td("Please contact your service administrator to continue using the service."), - }, - ); - return
-
{ limitError }
-
{ adminContact }
-
; } else { - return
- { _t("Unable to connect to Homeserver. Retrying...") } -
; + return
{_t("Unable to connect to Homeserver. Retrying...")}
; } } diff --git a/src/utils/EventRenderingUtils.ts b/src/utils/EventRenderingUtils.ts index 3ba4ce5705..047d75192f 100644 --- a/src/utils/EventRenderingUtils.ts +++ b/src/utils/EventRenderingUtils.ts @@ -25,7 +25,11 @@ import { MatrixClientPeg } from "../MatrixClientPeg"; import { getMessageModerationState, isLocationEvent, MessageModerationState } from "./EventUtils"; import { ElementCall } from "../models/Call"; -export function getEventDisplayInfo(mxEvent: MatrixEvent, showHiddenEvents: boolean, hideEvent?: boolean): { +export function getEventDisplayInfo( + mxEvent: MatrixEvent, + showHiddenEvents: boolean, + hideEvent?: boolean, +): { isInfoMessage: boolean; hasRenderer: boolean; isBubbleMessage: boolean; @@ -55,17 +59,15 @@ export function getEventDisplayInfo(mxEvent: MatrixEvent, showHiddenEvents: bool let factory = pickFactory(mxEvent, MatrixClientPeg.get(), showHiddenEvents); // Info messages are basically information about commands processed on a room - let isBubbleMessage = ( + let isBubbleMessage = eventType.startsWith("m.key.verification") || (eventType === EventType.RoomMessage && msgtype?.startsWith("m.key.verification")) || - (eventType === EventType.RoomCreate) || - (eventType === EventType.RoomEncryption) || - (factory === JitsiEventFactory) - ); - const isLeftAlignedBubbleMessage = !isBubbleMessage && ( - eventType === EventType.CallInvite || ElementCall.CALL_EVENT_TYPE.matches(eventType) - ); - let isInfoMessage = ( + eventType === EventType.RoomCreate || + eventType === EventType.RoomEncryption || + factory === JitsiEventFactory; + const isLeftAlignedBubbleMessage = + !isBubbleMessage && (eventType === EventType.CallInvite || ElementCall.CALL_EVENT_TYPE.matches(eventType)); + let isInfoMessage = !isBubbleMessage && !isLeftAlignedBubbleMessage && eventType !== EventType.RoomMessage && @@ -73,15 +75,13 @@ export function getEventDisplayInfo(mxEvent: MatrixEvent, showHiddenEvents: bool eventType !== EventType.Sticker && eventType !== EventType.RoomCreate && !M_POLL_START.matches(eventType) && - !M_BEACON_INFO.matches(eventType) - ); + !M_BEACON_INFO.matches(eventType); // Some non-info messages want to be rendered in the appropriate bubble column but without the bubble background - const noBubbleEvent = ( + const noBubbleEvent = (eventType === EventType.RoomMessage && msgtype === MsgType.Emote) || M_POLL_START.matches(eventType) || M_BEACON_INFO.matches(eventType) || - isLocationEvent(mxEvent) - ); + isLocationEvent(mxEvent); // If we're showing hidden events in the timeline, we should use the // source tile when there's no regular tile for an event and also for diff --git a/src/utils/EventUtils.ts b/src/utils/EventUtils.ts index 69c3351def..0f73480712 100644 --- a/src/utils/EventUtils.ts +++ b/src/utils/EventUtils.ts @@ -14,16 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { EventStatus, MatrixEvent } from 'matrix-js-sdk/src/models/event'; +import { EventStatus, MatrixEvent } from "matrix-js-sdk/src/models/event"; import { EventType, EVENT_VISIBILITY_CHANGE_TYPE, MsgType, RelationType } from "matrix-js-sdk/src/@types/event"; -import { MatrixClient } from 'matrix-js-sdk/src/client'; -import { logger } from 'matrix-js-sdk/src/logger'; +import { MatrixClient } from "matrix-js-sdk/src/client"; +import { logger } from "matrix-js-sdk/src/logger"; import { M_POLL_START } from "matrix-events-sdk"; import { M_LOCATION } from "matrix-js-sdk/src/@types/location"; -import { M_BEACON_INFO } from 'matrix-js-sdk/src/@types/beacon'; -import { THREAD_RELATION_TYPE } from 'matrix-js-sdk/src/models/thread'; +import { M_BEACON_INFO } from "matrix-js-sdk/src/@types/beacon"; +import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread"; -import { MatrixClientPeg } from '../MatrixClientPeg'; +import { MatrixClientPeg } from "../MatrixClientPeg"; import shouldHideEvent from "../shouldHideEvent"; import { GetRelationsForEvent } from "../components/views/rooms/EventTile"; import SettingsStore from "../settings/SettingsStore"; @@ -47,13 +47,13 @@ export function isContentActionable(mxEvent: MatrixEvent): boolean { const isSent = !eventStatus || eventStatus === EventStatus.SENT; if (isSent && !mxEvent.isRedacted()) { - if (mxEvent.getType() === 'm.room.message') { + if (mxEvent.getType() === "m.room.message") { const content = mxEvent.getContent(); - if (content.msgtype && content.msgtype !== 'm.bad.encrypted' && content.hasOwnProperty('body')) { + if (content.msgtype && content.msgtype !== "m.bad.encrypted" && content.hasOwnProperty("body")) { return true; } } else if ( - mxEvent.getType() === 'm.sticker' || + mxEvent.getType() === "m.sticker" || M_POLL_START.matches(mxEvent.getType()) || M_BEACON_INFO.matches(mxEvent.getType()) ) { @@ -65,10 +65,7 @@ export function isContentActionable(mxEvent: MatrixEvent): boolean { } export function canEditContent(mxEvent: MatrixEvent): boolean { - const isCancellable = ( - mxEvent.getType() === EventType.RoomMessage || - M_POLL_START.matches(mxEvent.getType()) - ); + const isCancellable = mxEvent.getType() === EventType.RoomMessage || M_POLL_START.matches(mxEvent.getType()); if ( !isCancellable || @@ -83,11 +80,7 @@ export function canEditContent(mxEvent: MatrixEvent): boolean { const { msgtype, body } = mxEvent.getOriginalContent(); return ( M_POLL_START.matches(mxEvent.getType()) || - ( - (msgtype === MsgType.Text || msgtype === MsgType.Emote) && - !!body && - typeof body === 'string' - ) + ((msgtype === MsgType.Text || msgtype === MsgType.Emote) && !!body && typeof body === "string") ); } @@ -117,17 +110,17 @@ export function findEditableEvent({ const beginIdx = isForward ? 0 : maxIdx; let endIdx = isForward ? maxIdx : 0; if (!fromEventId) { - endIdx = Math.min(Math.max(0, beginIdx + (inc * MAX_JUMP_DISTANCE)), maxIdx); + endIdx = Math.min(Math.max(0, beginIdx + inc * MAX_JUMP_DISTANCE), maxIdx); } let foundFromEventId = !fromEventId; - for (let i = beginIdx; i !== (endIdx + inc); i += inc) { + for (let i = beginIdx; i !== endIdx + inc; i += inc) { const e = events[i]; // find start event first if (!foundFromEventId && e.getId() === fromEventId) { foundFromEventId = true; // don't look further than MAX_JUMP_DISTANCE events from `fromEventId` // to not iterate potentially 1000nds of events on key up/down - endIdx = Math.min(Math.max(0, i + (inc * MAX_JUMP_DISTANCE)), maxIdx); + endIdx = Math.min(Math.max(0, i + inc * MAX_JUMP_DISTANCE), maxIdx); } else if (foundFromEventId && !shouldHideEvent(e) && canEditOwnEvent(e)) { // otherwise look for editable event return e; @@ -193,14 +186,16 @@ export function getMessageModerationState(mxEvent: MatrixEvent, client?: MatrixC } const room = client.getRoom(mxEvent.getRoomId()); - if (EVENT_VISIBILITY_CHANGE_TYPE.name - && room.currentState.maySendStateEvent(EVENT_VISIBILITY_CHANGE_TYPE.name, client.getUserId()) + if ( + EVENT_VISIBILITY_CHANGE_TYPE.name && + room.currentState.maySendStateEvent(EVENT_VISIBILITY_CHANGE_TYPE.name, client.getUserId()) ) { // We're a moderator (as indicated by prefixed event name), show the message. return MessageModerationState.SEE_THROUGH_FOR_CURRENT_USER; } - if (EVENT_VISIBILITY_CHANGE_TYPE.altName - && room.currentState.maySendStateEvent(EVENT_VISIBILITY_CHANGE_TYPE.altName, client.getUserId()) + if ( + EVENT_VISIBILITY_CHANGE_TYPE.altName && + room.currentState.maySendStateEvent(EVENT_VISIBILITY_CHANGE_TYPE.altName, client.getUserId()) ) { // We're a moderator (as indicated by unprefixed event name), show the message. return MessageModerationState.SEE_THROUGH_FOR_CURRENT_USER; @@ -212,10 +207,7 @@ export function getMessageModerationState(mxEvent: MatrixEvent, client?: MatrixC export function isVoiceMessage(mxEvent: MatrixEvent): boolean { const content = mxEvent.getContent(); // MSC2516 is a legacy identifier. See https://github.com/matrix-org/matrix-doc/pull/3245 - return ( - !!content['org.matrix.msc2516.voice'] || - !!content['org.matrix.msc3245.voice'] - ); + return !!content["org.matrix.msc2516.voice"] || !!content["org.matrix.msc3245.voice"]; } export async function fetchInitialEvent( @@ -233,15 +225,15 @@ export async function fetchInitialEvent( initialEvent = null; } - if (client.supportsExperimentalThreads() && + if ( + client.supportsExperimentalThreads() && initialEvent?.isRelation(THREAD_RELATION_TYPE.name) && !initialEvent.getThread() ) { const threadId = initialEvent.threadRootId; const room = client.getRoom(roomId); const mapper = client.getEventMapper(); - const rootEvent = room.findEventById(threadId) - ?? mapper(await client.fetchRoomEvent(roomId, threadId)); + const rootEvent = room.findEventById(threadId) ?? mapper(await client.fetchRoomEvent(roomId, threadId)); try { room.createThread(threadId, rootEvent, [initialEvent], true); } catch (e) { @@ -278,10 +270,7 @@ export const isLocationEvent = (event: MatrixEvent): boolean => { const eventType = event.getType(); return ( M_LOCATION.matches(eventType) || - ( - eventType === EventType.RoomMessage && - M_LOCATION.matches(event.getContent().msgtype) - ) + (eventType === EventType.RoomMessage && M_LOCATION.matches(event.getContent().msgtype)) ); }; diff --git a/src/utils/FileDownloader.ts b/src/utils/FileDownloader.ts index 5ec91d71cc..67591a9386 100644 --- a/src/utils/FileDownloader.ts +++ b/src/utils/FileDownloader.ts @@ -33,7 +33,7 @@ type DownloadOptions = { // set up the iframe as a singleton so we don't have to figure out destruction of it down the line. let managedIframe: HTMLIFrameElement; let onLoadPromise: Promise; -function getManagedIframe(): { iframe: HTMLIFrameElement, onLoadPromise: Promise } { +function getManagedIframe(): { iframe: HTMLIFrameElement; onLoadPromise: Promise } { if (managedIframe) return { iframe: managedIframe, onLoadPromise }; managedIframe = document.createElement("iframe"); @@ -49,7 +49,7 @@ function getManagedIframe(): { iframe: HTMLIFrameElement, onLoadPromise: Promise // noinspection JSConstantReassignment managedIframe.sandbox = "allow-scripts allow-downloads allow-downloads-without-user-activation"; - onLoadPromise = new Promise(resolve => { + onLoadPromise = new Promise((resolve) => { managedIframe.onload = () => { resolve(); }; @@ -75,8 +75,7 @@ export class FileDownloader { * @param iframeFn Function to get a pre-configured iframe. Set to null to have the downloader * use a generic, hidden, iframe. */ - constructor(private iframeFn: getIframeFn = null) { - } + constructor(private iframeFn: getIframeFn = null) {} private get iframe(): HTMLIFrameElement { const iframe = this.iframeFn?.(); @@ -92,11 +91,14 @@ export class FileDownloader { public async download({ blob, name, autoDownload = true, opts = DEFAULT_STYLES }: DownloadOptions) { const iframe = this.iframe; // get the iframe first just in case we need to await onload if (this.onLoadPromise) await this.onLoadPromise; - iframe.contentWindow.postMessage({ - ...opts, - blob: blob, - download: name, - auto: autoDownload, - }, '*'); + iframe.contentWindow.postMessage( + { + ...opts, + blob: blob, + download: name, + auto: autoDownload, + }, + "*", + ); } } diff --git a/src/utils/FileUtils.ts b/src/utils/FileUtils.ts index 152f721709..b9cd9a79d3 100644 --- a/src/utils/FileUtils.ts +++ b/src/utils/FileUtils.ts @@ -15,10 +15,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { filesize } from 'filesize'; +import { filesize } from "filesize"; -import { IMediaEventContent } from '../customisations/models/IMediaEventContent'; -import { _t } from '../languageHandler'; +import { IMediaEventContent } from "../customisations/models/IMediaEventContent"; +import { _t } from "../languageHandler"; /** * Extracts a human readable label for the file attachment to use as @@ -47,13 +47,16 @@ export function presentableTextForFile( // will have a 3 character (plus full stop) extension. The goal is to knock // the label down to 15-25 characters, not perfect accuracy. if (shortened && text.length > 19) { - const parts = text.split('.'); - let fileName = parts.slice(0, parts.length - 1).join('.').substring(0, 15); + const parts = text.split("."); + let fileName = parts + .slice(0, parts.length - 1) + .join(".") + .substring(0, 15); const extension = parts[parts.length - 1]; // Trim off any full stops from the file name to avoid a case where we // add an ellipsis that looks really funky. - fileName = fileName.replace(/\.*$/g, ''); + fileName = fileName.replace(/\.*$/g, ""); text = `${fileName}...${extension}`; } @@ -66,7 +69,7 @@ export function presentableTextForFile( // it since it is "ugly", users generally aren't aware what it // means and the type of the attachment can usually be inferred // from the file extension. - text += ' (' + filesize(content.info.size) + ')'; + text += " (" + filesize(content.info.size) + ")"; } return text; } diff --git a/src/utils/FontManager.ts b/src/utils/FontManager.ts index 197e4eda00..4b26bc9db2 100644 --- a/src/utils/FontManager.ts +++ b/src/utils/FontManager.ts @@ -29,13 +29,15 @@ function safariVersionCheck(ua: string): boolean { if (safariVersionMatch) { const macOSVersionStr = safariVersionMatch[1]; const safariVersionStr = safariVersionMatch[2]; - const macOSVersion = macOSVersionStr.split("_").map(n => parseInt(n, 10)); - const safariVersion = safariVersionStr.split(".").map(n => parseInt(n, 10)); + const macOSVersion = macOSVersionStr.split("_").map((n) => parseInt(n, 10)); + const safariVersion = safariVersionStr.split(".").map((n) => parseInt(n, 10)); const colrFontSupported = macOSVersion[0] >= 10 && macOSVersion[1] >= 14 && safariVersion[0] >= 12; // https://www.colorfonts.wtf/ states safari supports COLR fonts from this version on - logger.log(`COLR support on Safari requires macOS 10.14 and Safari 12, ` + - `detected Safari ${safariVersionStr} on macOS ${macOSVersionStr}, ` + - `COLR supported: ${colrFontSupported}`); + logger.log( + `COLR support on Safari requires macOS 10.14 and Safari 12, ` + + `detected Safari ${safariVersionStr} on macOS ${macOSVersionStr}, ` + + `COLR supported: ${colrFontSupported}`, + ); return colrFontSupported; } } catch (err) { @@ -66,11 +68,12 @@ async function isColrFontSupported(): Promise { } try { - const canvas = document.createElement('canvas'); - const context = canvas.getContext('2d'); + const canvas = document.createElement("canvas"); + const context = canvas.getContext("2d"); const img = new Image(); // eslint-disable-next-line - const fontCOLR = 'd09GRgABAAAAAAKAAAwAAAAAAowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABDT0xSAAACVAAAABYAAAAYAAIAJUNQQUwAAAJsAAAAEgAAABLJAAAQT1MvMgAAAYAAAAA6AAAAYBfxJ0pjbWFwAAABxAAAACcAAAAsAAzpM2dseWYAAAH0AAAAGgAAABoNIh0kaGVhZAAAARwAAAAvAAAANgxLumdoaGVhAAABTAAAABUAAAAkCAEEAmhtdHgAAAG8AAAABgAAAAYEAAAAbG9jYQAAAewAAAAGAAAABgANAABtYXhwAAABZAAAABsAAAAgAg4AHW5hbWUAAAIQAAAAOAAAAD4C5wsecG9zdAAAAkgAAAAMAAAAIAADAAB4AWNgZGAAYQ5+qdB4fpuvDNIsDCBwaQGTAIi+VlscBaJZGMDiHAxMIAoAtjIF/QB4AWNgZGBgYQACOAkUQQWMAAGRABAAAAB4AWNgZGBgYGJgAdMMUJILJMQgAWICAAH3AC4AeAFjYGFhYJzAwMrAwDST6QwDA0M/hGZ8zWDMyMmAChgFkDgKQMBw4CXDSwYWEBdIYgAFBgYA/8sIdAAABAAAAAAAAAB4AWNgYGBkYAZiBgYeBhYGBSDNAoRA/kuG//8hpDgjWJ4BAFVMBiYAAAAAAAANAAAAAQAAAAAEAAQAAAMAABEhESEEAPwABAD8AAAAeAEtxgUNgAAAAMHHIQTShTlOAty9/4bf7AARCwlBNhBw4L/43qXjYGUmf19TMuLcj/BJL3XfBg54AWNgZsALAAB9AAR4AWNgYGAEYj4gFgGygGwICQACOwAoAAAAAAABAAEAAQAAAA4AAAAAyP8AAA=='; + const fontCOLR = + "d09GRgABAAAAAAKAAAwAAAAAAowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABDT0xSAAACVAAAABYAAAAYAAIAJUNQQUwAAAJsAAAAEgAAABLJAAAQT1MvMgAAAYAAAAA6AAAAYBfxJ0pjbWFwAAABxAAAACcAAAAsAAzpM2dseWYAAAH0AAAAGgAAABoNIh0kaGVhZAAAARwAAAAvAAAANgxLumdoaGVhAAABTAAAABUAAAAkCAEEAmhtdHgAAAG8AAAABgAAAAYEAAAAbG9jYQAAAewAAAAGAAAABgANAABtYXhwAAABZAAAABsAAAAgAg4AHW5hbWUAAAIQAAAAOAAAAD4C5wsecG9zdAAAAkgAAAAMAAAAIAADAAB4AWNgZGAAYQ5+qdB4fpuvDNIsDCBwaQGTAIi+VlscBaJZGMDiHAxMIAoAtjIF/QB4AWNgZGBgYQACOAkUQQWMAAGRABAAAAB4AWNgZGBgYGJgAdMMUJILJMQgAWICAAH3AC4AeAFjYGFhYJzAwMrAwDST6QwDA0M/hGZ8zWDMyMmAChgFkDgKQMBw4CXDSwYWEBdIYgAFBgYA/8sIdAAABAAAAAAAAAB4AWNgYGBkYAZiBgYeBhYGBSDNAoRA/kuG//8hpDgjWJ4BAFVMBiYAAAAAAAANAAAAAQAAAAAEAAQAAAMAABEhESEEAPwABAD8AAAAeAEtxgUNgAAAAMHHIQTShTlOAty9/4bf7AARCwlBNhBw4L/43qXjYGUmf19TMuLcj/BJL3XfBg54AWNgZsALAAB9AAR4AWNgYGAEYj4gFgGygGwICQACOwAoAAAAAAABAAEAAQAAAA4AAAAAyP8AAA=="; const svg = ` ; } diff --git a/src/utils/Mouse.ts b/src/utils/Mouse.ts index a85c6492c4..8788b4fe80 100644 --- a/src/utils/Mouse.ts +++ b/src/utils/Mouse.ts @@ -27,24 +27,22 @@ export function normalizeWheelEvent(event: WheelEvent): WheelEvent { let deltaY; let deltaZ; - if (event.deltaMode === 1) { // Units are lines - deltaX = (event.deltaX * LINE_HEIGHT); - deltaY = (event.deltaY * LINE_HEIGHT); - deltaZ = (event.deltaZ * LINE_HEIGHT); + if (event.deltaMode === 1) { + // Units are lines + deltaX = event.deltaX * LINE_HEIGHT; + deltaY = event.deltaY * LINE_HEIGHT; + deltaZ = event.deltaZ * LINE_HEIGHT; } else { deltaX = event.deltaX; deltaY = event.deltaY; deltaZ = event.deltaZ; } - return new WheelEvent( - "syntheticWheel", - { - deltaMode: 0, - deltaY: deltaY, - deltaX: deltaX, - deltaZ: deltaZ, - ...event, - }, - ); + return new WheelEvent("syntheticWheel", { + deltaMode: 0, + deltaY: deltaY, + deltaX: deltaX, + deltaZ: deltaZ, + ...event, + }); } diff --git a/src/utils/MultiInviter.ts b/src/utils/MultiInviter.ts index 7208326a2b..e1f0de6814 100644 --- a/src/utils/MultiInviter.ts +++ b/src/utils/MultiInviter.ts @@ -21,8 +21,8 @@ import { MatrixClient } from "matrix-js-sdk/src/client"; import { EventType } from "matrix-js-sdk/src/@types/event"; import { HistoryVisibility } from "matrix-js-sdk/src/@types/partials"; -import { MatrixClientPeg } from '../MatrixClientPeg'; -import { AddressType, getAddressType } from '../UserAddress'; +import { MatrixClientPeg } from "../MatrixClientPeg"; +import { AddressType, getAddressType } from "../UserAddress"; import { _t } from "../languageHandler"; import Modal from "../Modal"; import SettingsStore from "../settings/SettingsStore"; @@ -38,7 +38,7 @@ interface IError { errcode: string; } -const UNKNOWN_PROFILE_ERRORS = ['M_NOT_FOUND', 'M_USER_NOT_FOUND', 'M_PROFILE_UNDISCLOSED', 'M_PROFILE_NOT_FOUND']; +const UNKNOWN_PROFILE_ERRORS = ["M_NOT_FOUND", "M_USER_NOT_FOUND", "M_PROFILE_UNDISCLOSED", "M_PROFILE_NOT_FOUND"]; export type CompletionStates = Record; @@ -92,8 +92,8 @@ export default class MultiInviter { if (getAddressType(addr) === null) { this.completionStates[addr] = InviteState.Error; this.errors[addr] = { - errcode: 'M_INVALID', - errorText: _t('Unrecognised address'), + errcode: "M_INVALID", + errorText: _t("Unrecognised address"), }; } } @@ -112,7 +112,7 @@ export default class MultiInviter { return this.deferred.promise; } - return this.deferred.promise.then(async states => { + return this.deferred.promise.then(async (states) => { const invitedUsers = []; for (const [addr, state] of Object.entries(states)) { if (state === InviteState.Invited && getAddressType(addr) === AddressType.MatrixUserId) { @@ -134,7 +134,7 @@ export default class MultiInviter { if (!this.busy) return; this.canceled = true; - this.deferred.reject(new Error('canceled')); + this.deferred.reject(new Error("canceled")); } public getCompletionState(addr: string): InviteState { @@ -174,10 +174,10 @@ export default class MultiInviter { // The error handling during the invitation process covers any API. // Some errors must to me mapped from profile API errors to more specific ones to avoid collisions. switch (err.errcode) { - case 'M_FORBIDDEN': - throw new MatrixError({ errcode: 'M_PROFILE_UNDISCLOSED' }); - case 'M_NOT_FOUND': - throw new MatrixError({ errcode: 'M_USER_NOT_FOUND' }); + case "M_FORBIDDEN": + throw new MatrixError({ errcode: "M_PROFILE_UNDISCLOSED" }); + case "M_NOT_FOUND": + throw new MatrixError({ errcode: "M_USER_NOT_FOUND" }); default: throw err; } @@ -186,7 +186,7 @@ export default class MultiInviter { return this.matrixClient.invite(roomId, addr, this.reason); } else { - throw new Error('Unsupported address'); + throw new Error("Unsupported address"); } } @@ -195,99 +195,101 @@ export default class MultiInviter { logger.log(`Inviting ${address}`); const doInvite = this.inviteToRoom(this.roomId, address, ignoreProfile); - doInvite.then(() => { - if (this.canceled) { - return; - } - - this.completionStates[address] = InviteState.Invited; - delete this.errors[address]; - - resolve(); - this.progressCallback?.(); - }).catch((err) => { - if (this.canceled) { - return; - } - - logger.error(err); - - const isSpace = this.roomId && this.matrixClient.getRoom(this.roomId)?.isSpaceRoom(); - - let errorText: string; - let fatal = false; - switch (err.errcode) { - case "M_FORBIDDEN": - if (isSpace) { - errorText = _t('You do not have permission to invite people to this space.'); - } else { - errorText = _t('You do not have permission to invite people to this room.'); - } - fatal = true; - break; - case USER_ALREADY_INVITED: - if (isSpace) { - errorText = _t("User is already invited to the space"); - } else { - errorText = _t("User is already invited to the room"); - } - break; - case USER_ALREADY_JOINED: - if (isSpace) { - errorText = _t("User is already in the space"); - } else { - errorText = _t("User is already in the room"); - } - break; - case "M_LIMIT_EXCEEDED": - // we're being throttled so wait a bit & try again - window.setTimeout(() => { - this.doInvite(address, ignoreProfile).then(resolve, reject); - }, 5000); + doInvite + .then(() => { + if (this.canceled) { return; - case "M_NOT_FOUND": - case "M_USER_NOT_FOUND": - errorText = _t("User does not exist"); - break; - case "M_PROFILE_UNDISCLOSED": - errorText = _t("User may or may not exist"); - break; - case "M_PROFILE_NOT_FOUND": - if (!ignoreProfile) { - // Invite without the profile check - logger.warn(`User ${address} does not have a profile - inviting anyways automatically`); - this.doInvite(address, true).then(resolve, reject); - return; - } - break; - case "M_BAD_STATE": - errorText = _t("The user must be unbanned before they can be invited."); - break; - case "M_UNSUPPORTED_ROOM_VERSION": - if (isSpace) { - errorText = _t("The user's homeserver does not support the version of the space."); - } else { - errorText = _t("The user's homeserver does not support the version of the room."); - } - break; - } + } - if (!errorText) { - errorText = _t('Unknown server error'); - } + this.completionStates[address] = InviteState.Invited; + delete this.errors[address]; - this.completionStates[address] = InviteState.Error; - this.errors[address] = { errorText, errcode: err.errcode }; - - this.busy = !fatal; - this._fatal = fatal; - - if (fatal) { - reject(err); - } else { resolve(); - } - }); + this.progressCallback?.(); + }) + .catch((err) => { + if (this.canceled) { + return; + } + + logger.error(err); + + const isSpace = this.roomId && this.matrixClient.getRoom(this.roomId)?.isSpaceRoom(); + + let errorText: string; + let fatal = false; + switch (err.errcode) { + case "M_FORBIDDEN": + if (isSpace) { + errorText = _t("You do not have permission to invite people to this space."); + } else { + errorText = _t("You do not have permission to invite people to this room."); + } + fatal = true; + break; + case USER_ALREADY_INVITED: + if (isSpace) { + errorText = _t("User is already invited to the space"); + } else { + errorText = _t("User is already invited to the room"); + } + break; + case USER_ALREADY_JOINED: + if (isSpace) { + errorText = _t("User is already in the space"); + } else { + errorText = _t("User is already in the room"); + } + break; + case "M_LIMIT_EXCEEDED": + // we're being throttled so wait a bit & try again + window.setTimeout(() => { + this.doInvite(address, ignoreProfile).then(resolve, reject); + }, 5000); + return; + case "M_NOT_FOUND": + case "M_USER_NOT_FOUND": + errorText = _t("User does not exist"); + break; + case "M_PROFILE_UNDISCLOSED": + errorText = _t("User may or may not exist"); + break; + case "M_PROFILE_NOT_FOUND": + if (!ignoreProfile) { + // Invite without the profile check + logger.warn(`User ${address} does not have a profile - inviting anyways automatically`); + this.doInvite(address, true).then(resolve, reject); + return; + } + break; + case "M_BAD_STATE": + errorText = _t("The user must be unbanned before they can be invited."); + break; + case "M_UNSUPPORTED_ROOM_VERSION": + if (isSpace) { + errorText = _t("The user's homeserver does not support the version of the space."); + } else { + errorText = _t("The user's homeserver does not support the version of the room."); + } + break; + } + + if (!errorText) { + errorText = _t("Unknown server error"); + } + + this.completionStates[address] = InviteState.Error; + this.errors[address] = { errorText, errcode: err.errcode }; + + this.busy = !fatal; + this._fatal = fatal; + + if (fatal) { + reject(err); + } else { + resolve(); + } + }); }); } @@ -301,12 +303,13 @@ export default class MultiInviter { if (Object.keys(this.errors).length > 0) { // There were problems inviting some people - see if we can invite them // without caring if they exist or not. - const unknownProfileUsers = Object.keys(this.errors) - .filter(a => UNKNOWN_PROFILE_ERRORS.includes(this.errors[a].errcode)); + const unknownProfileUsers = Object.keys(this.errors).filter((a) => + UNKNOWN_PROFILE_ERRORS.includes(this.errors[a].errcode), + ); if (unknownProfileUsers.length > 0) { const inviteUnknowns = () => { - const promises = unknownProfileUsers.map(u => this.doInvite(u, true)); + const promises = unknownProfileUsers.map((u) => this.doInvite(u, true)); Promise.all(promises).then(() => this.deferred.resolve(this.completionStates)); }; @@ -317,7 +320,7 @@ export default class MultiInviter { logger.log("Showing failed to invite dialog..."); Modal.createDialog(AskInviteAnywayDialog, { - unknownProfileUsers: unknownProfileUsers.map(u => ({ + unknownProfileUsers: unknownProfileUsers.map((u) => ({ userId: u, errorText: this.errors[u].errorText, })), @@ -354,8 +357,10 @@ export default class MultiInviter { return; } - this.doInvite(addr, ignoreProfile).then(() => { - this.inviteMore(nextIndex + 1, ignoreProfile); - }).catch(() => this.deferred.resolve(this.completionStates)); + this.doInvite(addr, ignoreProfile) + .then(() => { + this.inviteMore(nextIndex + 1, ignoreProfile); + }) + .catch(() => this.deferred.resolve(this.completionStates)); } } diff --git a/src/utils/NativeEventUtils.ts b/src/utils/NativeEventUtils.ts index 3094b57bd4..3ed637c748 100644 --- a/src/utils/NativeEventUtils.ts +++ b/src/utils/NativeEventUtils.ts @@ -18,7 +18,8 @@ import React from "react"; // Wrap DOM event handlers with stopPropagation and preventDefault export const preventDefaultWrapper = - (callback: () => void) => (e?: T) => { + (callback: () => void) => + (e?: T) => { e?.stopPropagation(); e?.preventDefault(); callback(); diff --git a/src/utils/PasswordScorer.ts b/src/utils/PasswordScorer.ts index 9aae4039bd..8a0d611773 100644 --- a/src/utils/PasswordScorer.ts +++ b/src/utils/PasswordScorer.ts @@ -14,15 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -import zxcvbn, { ZXCVBNFeedbackWarning } from 'zxcvbn'; +import zxcvbn, { ZXCVBNFeedbackWarning } from "zxcvbn"; -import { MatrixClientPeg } from '../MatrixClientPeg'; -import { _t, _td } from '../languageHandler'; +import { MatrixClientPeg } from "../MatrixClientPeg"; +import { _t, _td } from "../languageHandler"; -const ZXCVBN_USER_INPUTS = [ - 'riot', - 'matrix', -]; +const ZXCVBN_USER_INPUTS = ["riot", "matrix"]; // Translations for zxcvbn's suggestion strings _td("Use a few words, avoid common phrases"); @@ -40,8 +37,8 @@ _td("Predictable substitutions like '@' instead of 'a' don't help very much"); _td("Add another word or two. Uncommon words are better."); // and warnings -_td("Repeats like \"aaa\" are easy to guess"); -_td("Repeats like \"abcabcabc\" are only slightly harder to guess than \"abc\""); +_td('Repeats like "aaa" are easy to guess'); +_td('Repeats like "abcabcabc" are only slightly harder to guess than "abc"'); _td("Sequences like abc or 6543 are easy to guess"); _td("Recent years are easy to guess"); _td("Dates are often easy to guess"); @@ -73,8 +70,8 @@ export function scorePassword(password: string) { let zxcvbnResult = zxcvbn(password, userInputs); // Work around https://github.com/dropbox/zxcvbn/issues/216 - if (password.includes(' ')) { - const resultNoSpaces = zxcvbn(password.replace(/ /g, ''), userInputs); + if (password.includes(" ")) { + const resultNoSpaces = zxcvbn(password.replace(/ /g, ""), userInputs); if (resultNoSpaces.score < zxcvbnResult.score) zxcvbnResult = resultNoSpaces; } diff --git a/src/utils/PreferredRoomVersions.ts b/src/utils/PreferredRoomVersions.ts index 2dc269da6c..b4944d4a00 100644 --- a/src/utils/PreferredRoomVersions.ts +++ b/src/utils/PreferredRoomVersions.ts @@ -52,4 +52,3 @@ export function doesRoomVersionSupport(roomVer: string, featureVer: string): boo // from a mile away and can course-correct this function if needed. return Number(roomVer) >= Number(featureVer); } - diff --git a/src/utils/ReactUtils.tsx b/src/utils/ReactUtils.tsx index 4cd2d750f3..59fa7a3b91 100644 --- a/src/utils/ReactUtils.tsx +++ b/src/utils/ReactUtils.tsx @@ -25,9 +25,7 @@ import React from "react"; export function jsxJoin(array: Array, joiner?: string | JSX.Element): JSX.Element { const newArray = []; array.forEach((element, index) => { - newArray.push(element, (index === array.length - 1) ? null : joiner); + newArray.push(element, index === array.length - 1 ? null : joiner); }); - return ( - { newArray } - ); + return {newArray}; } diff --git a/src/utils/Reply.ts b/src/utils/Reply.ts index 145753e041..c6f1778c04 100644 --- a/src/utils/Reply.ts +++ b/src/utils/Reply.ts @@ -36,11 +36,11 @@ export function getParentEventId(ev?: MatrixEvent): string | undefined { // Part of Replies fallback support export function stripPlainReply(body: string): string { // Removes lines beginning with `> ` until you reach one that doesn't. - const lines = body.split('\n'); - while (lines.length && lines[0].startsWith('> ')) lines.shift(); + const lines = body.split("\n"); + while (lines.length && lines[0].startsWith("> ")) lines.shift(); // Reply fallback has a blank line after it, so remove it to prevent leading newline - if (lines[0] === '') lines.shift(); - return lines.join('\n'); + if (lines[0] === "") lines.shift(); + return lines.join("\n"); } // Part of Replies fallback support @@ -52,24 +52,21 @@ export function stripHTMLReply(html: string): string { // anyways. However, we sanitize to 1) remove any mx-reply, so that we // don't generate a nested mx-reply, and 2) make sure that the HTML is // properly formatted (e.g. tags are closed where necessary) - return sanitizeHtml( - html, - { - allowedTags: false, // false means allow everything - allowedAttributes: false, - // we somehow can't allow all schemes, so we allow all that we - // know of and mxc (for img tags) - allowedSchemes: [...PERMITTED_URL_SCHEMES, 'mxc'], - exclusiveFilter: (frame) => frame.tag === "mx-reply", - }, - ); + return sanitizeHtml(html, { + allowedTags: false, // false means allow everything + allowedAttributes: false, + // we somehow can't allow all schemes, so we allow all that we + // know of and mxc (for img tags) + allowedSchemes: [...PERMITTED_URL_SCHEMES, "mxc"], + exclusiveFilter: (frame) => frame.tag === "mx-reply", + }); } // Part of Replies fallback support export function getNestedReplyText( ev: MatrixEvent, permalinkCreator: RoomPermalinkCreator, -): { body: string, html: string } | null { +): { body: string; html: string } | null { if (!ev) return null; let { body, formatted_body: html, msgtype } = ev.getContent(); @@ -86,7 +83,7 @@ export function getNestedReplyText( // Escape the body to use as HTML below. // We also run a nl2br over the result to fix the fallback representation. We do this // after converting the text to safe HTML to avoid user-provided BR's from being converted. - html = escapeHtml(body).replace(/\n/g, '
'); + html = escapeHtml(body).replace(/\n/g, "
"); } // dev note: do not rely on `body` being safe for HTML usage below. @@ -98,8 +95,9 @@ export function getNestedReplyText( if (M_BEACON_INFO.matches(ev.getType())) { const aTheir = isSelfLocation(ev.getContent()) ? "their" : "a"; return { - html: `
In reply to ${mxid}` - + `
shared ${aTheir} live location.
`, + html: + `
In reply to ${mxid}` + + `
shared ${aTheir} live location.
`, body: `> <${mxid}> shared ${aTheir} live location.\n\n`, }; } @@ -108,49 +106,56 @@ export function getNestedReplyText( switch (msgtype) { case MsgType.Text: case MsgType.Notice: { - html = `
In reply to ${mxid}` - + `
${html}
`; - const lines = body.trim().split('\n'); + html = + `
In reply to ${mxid}` + + `
${html}
`; + const lines = body.trim().split("\n"); if (lines.length > 0) { lines[0] = `<${mxid}> ${lines[0]}`; - body = lines.map((line) => `> ${line}`).join('\n') + '\n\n'; + body = lines.map((line) => `> ${line}`).join("\n") + "\n\n"; } break; } case MsgType.Image: - html = `
In reply to ${mxid}` - + `
sent an image.
`; + html = + `
In reply to ${mxid}` + + `
sent an image.
`; body = `> <${mxid}> sent an image.\n\n`; break; case MsgType.Video: - html = `
In reply to ${mxid}` - + `
sent a video.
`; + html = + `
In reply to ${mxid}` + + `
sent a video.
`; body = `> <${mxid}> sent a video.\n\n`; break; case MsgType.Audio: - html = `
In reply to ${mxid}` - + `
sent an audio file.
`; + html = + `
In reply to ${mxid}` + + `
sent an audio file.
`; body = `> <${mxid}> sent an audio file.\n\n`; break; case MsgType.File: - html = `
In reply to ${mxid}` - + `
sent a file.
`; + html = + `
In reply to ${mxid}` + + `
sent a file.
`; body = `> <${mxid}> sent a file.\n\n`; break; case MsgType.Location: { const aTheir = isSelfLocation(ev.getContent()) ? "their" : "a"; - html = `
In reply to ${mxid}` - + `
shared ${aTheir} location.
`; + html = + `
In reply to ${mxid}` + + `
shared ${aTheir} location.
`; body = `> <${mxid}> shared ${aTheir} location.\n\n`; break; } case MsgType.Emote: { - html = `
In reply to * ` - + `${mxid}
${html}
`; - const lines = body.trim().split('\n'); + html = + `
In reply to * ` + + `${mxid}
${html}
`; + const lines = body.trim().split("\n"); if (lines.length > 0) { lines[0] = `* <${mxid}> ${lines[0]}`; - body = lines.map((line) => `> ${line}`).join('\n') + '\n\n'; + body = lines.map((line) => `> ${line}`).join("\n") + "\n\n"; } break; } @@ -165,8 +170,8 @@ export function makeReplyMixIn(ev?: MatrixEvent): IEventRelation { if (!ev) return {}; const mixin: IEventRelation = { - 'm.in_reply_to': { - 'event_id': ev.getId(), + "m.in_reply_to": { + event_id: ev.getId(), }, }; @@ -197,7 +202,8 @@ export function shouldDisplayReply(event: MatrixEvent): boolean { } const relation = event.getRelation(); - if (SettingsStore.getValue("feature_thread") && + if ( + SettingsStore.getValue("feature_thread") && relation?.rel_type === THREAD_RELATION_TYPE.name && relation?.is_falling_back ) { diff --git a/src/utils/ResizeNotifier.ts b/src/utils/ResizeNotifier.ts index 8bb7f52e57..d957ee75bc 100644 --- a/src/utils/ResizeNotifier.ts +++ b/src/utils/ResizeNotifier.ts @@ -76,4 +76,3 @@ export default class ResizeNotifier extends EventEmitter { this.updateMiddlePanel(); } } - diff --git a/src/utils/RoomUpgrade.ts b/src/utils/RoomUpgrade.ts index 37aa61de30..6bb79642af 100644 --- a/src/utils/RoomUpgrade.ts +++ b/src/utils/RoomUpgrade.ts @@ -39,7 +39,7 @@ export async function awaitRoomDownSync(cli: MatrixClient, roomId: string): Prom const room = cli.getRoom(roomId); if (room) return room; // already have the room - return new Promise(resolve => { + return new Promise((resolve) => { // We have to wait for the js-sdk to give us the room back so // we can more effectively abuse the MultiInviter behaviour // which heavily relies on the Room object being available. @@ -69,22 +69,21 @@ export async function upgradeRoom( let toInvite: string[] = []; if (inviteUsers) { - toInvite = [ - ...room.getMembersWithMembership("join"), - ...room.getMembersWithMembership("invite"), - ].map(m => m.userId).filter(m => m !== cli.getUserId()); + toInvite = [...room.getMembersWithMembership("join"), ...room.getMembersWithMembership("invite")] + .map((m) => m.userId) + .filter((m) => m !== cli.getUserId()); } let parentsToRelink: Room[] = []; if (updateSpaces) { parentsToRelink = Array.from(SpaceStore.instance.getKnownParents(room.roomId)) - .map(roomId => cli.getRoom(roomId)) - .filter(parent => parent?.currentState.maySendStateEvent(EventType.SpaceChild, cli.getUserId())); + .map((roomId) => cli.getRoom(roomId)) + .filter((parent) => parent?.currentState.maySendStateEvent(EventType.SpaceChild, cli.getUserId())); } const progress: IProgress = { roomUpgraded: false, - roomSynced: (awaitRoom || inviteUsers) ? false : undefined, + roomSynced: awaitRoom || inviteUsers ? false : undefined, inviteUsersProgress: inviteUsers ? 0 : undefined, inviteUsersTotal: toInvite.length, updateSpacesProgress: updateSpaces ? 0 : undefined, @@ -100,8 +99,8 @@ export async function upgradeRoom( logger.error(e); Modal.createDialog(ErrorDialog, { - title: _t('Error upgrading room'), - description: _t('Double check that your server supports the room version chosen and try again.'), + title: _t("Error upgrading room"), + description: _t("Double check that your server supports the room version chosen and try again."), }); throw e; } @@ -127,10 +126,15 @@ export async function upgradeRoom( try { for (const parent of parentsToRelink) { const currentEv = parent.currentState.getStateEvents(EventType.SpaceChild, room.roomId); - await cli.sendStateEvent(parent.roomId, EventType.SpaceChild, { - ...(currentEv?.getContent() || {}), // copy existing attributes like suggested - via: [cli.getDomain()], - }, newRoomId); + await cli.sendStateEvent( + parent.roomId, + EventType.SpaceChild, + { + ...(currentEv?.getContent() || {}), // copy existing attributes like suggested + via: [cli.getDomain()], + }, + newRoomId, + ); await cli.sendStateEvent(parent.roomId, EventType.SpaceChild, {}, room.roomId); progress.updateSpacesProgress++; diff --git a/src/utils/ShieldUtils.ts b/src/utils/ShieldUtils.ts index 296c68fa09..6bf57801be 100644 --- a/src/utils/ShieldUtils.ts +++ b/src/utils/ShieldUtils.ts @@ -17,12 +17,12 @@ limitations under the License. import { MatrixClient } from "matrix-js-sdk/src/client"; import { Room } from "matrix-js-sdk/src/models/room"; -import DMRoomMap from './DMRoomMap'; +import DMRoomMap from "./DMRoomMap"; export enum E2EStatus { Warning = "warning", Verified = "verified", - Normal = "normal" + Normal = "normal", } export async function shieldStatusForRoom(client: MatrixClient, room: Room): Promise { @@ -31,10 +31,10 @@ export async function shieldStatusForRoom(client: MatrixClient, room: Room): Pro const verified: string[] = []; const unverified: string[] = []; - members.filter((userId) => userId !== client.getUserId()) + members + .filter((userId) => userId !== client.getUserId()) .forEach((userId) => { - (client.checkUserTrust(userId).isCrossSigningVerified() ? - verified : unverified).push(userId); + (client.checkUserTrust(userId).isCrossSigningVerified() ? verified : unverified).push(userId); }); /* Alarm if any unverified users were verified before. */ @@ -46,10 +46,11 @@ export async function shieldStatusForRoom(client: MatrixClient, room: Room): Pro /* Check all verified user devices. */ /* Don't alarm if no other users are verified */ - const includeUser = (verified.length > 0) && // Don't alarm for self in rooms where nobody else is verified - !inDMMap && // Don't alarm for self in DMs with other users - (members.length !== 2) || // Don't alarm for self in 1:1 chats with other users - (members.length === 1); // Do alarm for self if we're alone in a room + const includeUser = + (verified.length > 0 && // Don't alarm for self in rooms where nobody else is verified + !inDMMap && // Don't alarm for self in DMs with other users + members.length !== 2) || // Don't alarm for self in 1:1 chats with other users + members.length === 1; // Do alarm for self if we're alone in a room const targets = includeUser ? [...verified, client.getUserId()] : verified; for (const userId of targets) { const devices = client.getStoredDevicesForUser(userId); diff --git a/src/utils/Singleflight.ts b/src/utils/Singleflight.ts index 93822594a2..28d910b094 100644 --- a/src/utils/Singleflight.ts +++ b/src/utils/Singleflight.ts @@ -41,8 +41,7 @@ const keyMap = new EnhancedMap>(); * variables to strings to essentially namespace the field, for most cases. */ export class Singleflight { - private constructor() { - } + private constructor() {} /** * A void marker to help with returning a value in a singleflight context. @@ -80,8 +79,7 @@ export class Singleflight { } class SingleflightContext { - public constructor(private instance: Object, private key: string) { - } + public constructor(private instance: Object, private key: string) {} /** * Forget this particular instance and key combination, discarding the result. diff --git a/src/utils/SnakedObject.ts b/src/utils/SnakedObject.ts index bce02512c0..f19488795e 100644 --- a/src/utils/SnakedObject.ts +++ b/src/utils/SnakedObject.ts @@ -15,12 +15,11 @@ limitations under the License. */ export function snakeToCamel(s: string): string { - return s.replace(/._./g, v => `${v[0]}${v[2].toUpperCase()}`); + return s.replace(/._./g, (v) => `${v[0]}${v[2].toUpperCase()}`); } export class SnakedObject> { - public constructor(private obj: T) { - } + public constructor(private obj: T) {} public get(key: K, altCaseName?: string): T[K] { const val = this.obj[key]; diff --git a/src/utils/SortMembers.ts b/src/utils/SortMembers.ts index 18e2ce6680..4bc17be2c1 100644 --- a/src/utils/SortMembers.ts +++ b/src/utils/SortMembers.ts @@ -21,36 +21,38 @@ import { compare } from "matrix-js-sdk/src/utils"; import { Member } from "./direct-messages"; import DMRoomMap from "./DMRoomMap"; -export const compareMembers = ( - activityScores: Record, - memberScores: Record, -) => (a: Member | RoomMember, b: Member | RoomMember): number => { - const aActivityScore = activityScores[a.userId]?.score ?? 0; - const aMemberScore = memberScores[a.userId]?.score ?? 0; - const aScore = aActivityScore + aMemberScore; - const aNumRooms = memberScores[a.userId]?.numRooms ?? 0; +export const compareMembers = + (activityScores: Record, memberScores: Record) => + (a: Member | RoomMember, b: Member | RoomMember): number => { + const aActivityScore = activityScores[a.userId]?.score ?? 0; + const aMemberScore = memberScores[a.userId]?.score ?? 0; + const aScore = aActivityScore + aMemberScore; + const aNumRooms = memberScores[a.userId]?.numRooms ?? 0; - const bActivityScore = activityScores[b.userId]?.score ?? 0; - const bMemberScore = memberScores[b.userId]?.score ?? 0; - const bScore = bActivityScore + bMemberScore; - const bNumRooms = memberScores[b.userId]?.numRooms ?? 0; + const bActivityScore = activityScores[b.userId]?.score ?? 0; + const bMemberScore = memberScores[b.userId]?.score ?? 0; + const bScore = bActivityScore + bMemberScore; + const bNumRooms = memberScores[b.userId]?.numRooms ?? 0; - if (aScore === bScore) { - if (aNumRooms === bNumRooms) { - return compare(a.userId, b.userId); + if (aScore === bScore) { + if (aNumRooms === bNumRooms) { + return compare(a.userId, b.userId); + } + + return bNumRooms - aNumRooms; } - - return bNumRooms - aNumRooms; - } - return bScore - aScore; -}; + return bScore - aScore; + }; function joinedRooms(cli: MatrixClient): Room[] { - return cli.getRooms() - .filter(r => r.getMyMembership() === 'join') - // Skip low priority rooms and DMs - .filter(r => !DMRoomMap.shared().getUserIdForRoomId(r.roomId)) - .filter(r => !Object.keys(r.tags).includes("m.lowpriority")); + return ( + cli + .getRooms() + .filter((r) => r.getMyMembership() === "join") + // Skip low priority rooms and DMs + .filter((r) => !DMRoomMap.shared().getUserIdForRoomId(r.roomId)) + .filter((r) => !Object.keys(r.tags).includes("m.lowpriority")) + ); } interface IActivityScore { @@ -64,16 +66,16 @@ interface IActivityScore { // which are closer to "continue this conversation" rather than "this person exists". export function buildActivityScores(cli: MatrixClient): { [key: string]: IActivityScore } { const now = new Date().getTime(); - const earliestAgeConsidered = now - (60 * 60 * 1000); // 1 hour ago + const earliestAgeConsidered = now - 60 * 60 * 1000; // 1 hour ago const maxMessagesConsidered = 50; // so we don't iterate over a huge amount of traffic const events = joinedRooms(cli) - .flatMap(room => takeRight(room.getLiveTimeline().getEvents(), maxMessagesConsidered)) - .filter(ev => ev.getTs() > earliestAgeConsidered); - const senderEvents = groupBy(events, ev => ev.getSender()); - return mapValues(senderEvents, events => { - const lastEvent = maxBy(events, ev => ev.getTs()); + .flatMap((room) => takeRight(room.getLiveTimeline().getEvents(), maxMessagesConsidered)) + .filter((ev) => ev.getTs() > earliestAgeConsidered); + const senderEvents = groupBy(events, (ev) => ev.getSender()); + return mapValues(senderEvents, (events) => { + const lastEvent = maxBy(events, (ev) => ev.getTs()); const distanceFromNow = Math.abs(now - lastEvent.getTs()); // abs to account for slight future messages - const inverseTime = (now - earliestAgeConsidered) - distanceFromNow; + const inverseTime = now - earliestAgeConsidered - distanceFromNow; return { lastSpoke: lastEvent.getTs(), // Scores from being in a room give a 'good' score of about 1.0-1.5, so for our @@ -92,19 +94,18 @@ interface IMemberScore { export function buildMemberScores(cli: MatrixClient): { [key: string]: IMemberScore } { const maxConsideredMembers = 200; - const consideredRooms = joinedRooms(cli).filter(room => room.getJoinedMemberCount() < maxConsideredMembers); - const memberPeerEntries = consideredRooms - .flatMap(room => - room.getJoinedMembers().map(member => - ({ member, roomSize: room.getJoinedMemberCount() }))); + const consideredRooms = joinedRooms(cli).filter((room) => room.getJoinedMemberCount() < maxConsideredMembers); + const memberPeerEntries = consideredRooms.flatMap((room) => + room.getJoinedMembers().map((member) => ({ member, roomSize: room.getJoinedMemberCount() })), + ); const userMeta = groupBy(memberPeerEntries, ({ member }) => member.userId); - return mapValues(userMeta, roomMemberships => { + return mapValues(userMeta, (roomMemberships) => { const maximumPeers = maxConsideredMembers * roomMemberships.length; - const totalPeers = sumBy(roomMemberships, entry => entry.roomSize); + const totalPeers = sumBy(roomMemberships, (entry) => entry.roomSize); return { - member: minBy(roomMemberships, entry => entry.roomSize).member, + member: minBy(roomMemberships, (entry) => entry.roomSize).member, numRooms: roomMemberships.length, - score: Math.max(0, Math.pow(1 - (totalPeers / maximumPeers), 5)), + score: Math.max(0, Math.pow(1 - totalPeers / maximumPeers, 5)), }; }); } diff --git a/src/utils/StorageManager.ts b/src/utils/StorageManager.ts index 1145bcfb15..249c64acb4 100644 --- a/src/utils/StorageManager.ts +++ b/src/utils/StorageManager.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { LocalStorageCryptoStore } from 'matrix-js-sdk/src/crypto/store/localStorage-crypto-store'; +import { LocalStorageCryptoStore } from "matrix-js-sdk/src/crypto/store/localStorage-crypto-store"; import { IndexedDBStore } from "matrix-js-sdk/src/store/indexeddb"; import { IndexedDBCryptoStore } from "matrix-js-sdk/src/crypto/store/indexeddb-crypto-store"; import { logger } from "matrix-js-sdk/src/logger"; @@ -42,10 +42,11 @@ function error(msg: string, ...args: string[]) { export function tryPersistStorage() { if (navigator.storage && navigator.storage.persist) { - navigator.storage.persist().then(persistent => { + navigator.storage.persist().then((persistent) => { logger.log("StorageManager: Persistent?", persistent); }); - } else if (document.requestStorageAccess) { // Safari + } else if (document.requestStorageAccess) { + // Safari document.requestStorageAccess().then( () => logger.log("StorageManager: Persistent?", true), () => logger.log("StorageManager: Persistent?", false), @@ -101,8 +102,8 @@ export async function checkConsistency() { healthy = false; error( "Data exists in local storage and crypto is marked as initialised " + - " but no data found in crypto store. " + - "IndexedDB storage has likely been evicted by the browser!", + " but no data found in crypto store. " + + "IndexedDB storage has likely been evicted by the browser!", ); } @@ -123,9 +124,7 @@ export async function checkConsistency() { async function checkSyncStore() { let exists = false; try { - exists = await IndexedDBStore.exists( - indexedDB, SYNC_STORE_NAME, - ); + exists = await IndexedDBStore.exists(indexedDB, SYNC_STORE_NAME); log(`Sync store using IndexedDB contains data? ${exists}`); return { exists, healthy: true }; } catch (e) { @@ -138,9 +137,7 @@ async function checkSyncStore() { async function checkCryptoStore() { let exists = false; try { - exists = await IndexedDBCryptoStore.exists( - indexedDB, CRYPTO_STORE_NAME, - ); + exists = await IndexedDBCryptoStore.exists(indexedDB, CRYPTO_STORE_NAME); log(`Crypto store using IndexedDB contains data? ${exists}`); return { exists, healthy: true }; } catch (e) { @@ -183,7 +180,9 @@ async function idbInit(): Promise { idb = await new Promise((resolve, reject) => { const request = indexedDB.open("matrix-react-sdk", 1); request.onerror = reject; - request.onsuccess = () => { resolve(request.result); }; + request.onsuccess = () => { + resolve(request.result); + }; request.onupgradeneeded = () => { const db = request.result; db.createObjectStore("pickleKey"); @@ -192,10 +191,7 @@ async function idbInit(): Promise { }); } -export async function idbLoad( - table: string, - key: string | string[], -): Promise { +export async function idbLoad(table: string, key: string | string[]): Promise { if (!idb) { await idbInit(); } @@ -206,15 +202,13 @@ export async function idbLoad( const objectStore = txn.objectStore(table); const request = objectStore.get(key); request.onerror = reject; - request.onsuccess = (event) => { resolve(request.result); }; + request.onsuccess = (event) => { + resolve(request.result); + }; }); } -export async function idbSave( - table: string, - key: string | string[], - data: any, -): Promise { +export async function idbSave(table: string, key: string | string[], data: any): Promise { if (!idb) { await idbInit(); } @@ -225,14 +219,13 @@ export async function idbSave( const objectStore = txn.objectStore(table); const request = objectStore.put(data, key); request.onerror = reject; - request.onsuccess = (event) => { resolve(); }; + request.onsuccess = (event) => { + resolve(); + }; }); } -export async function idbDelete( - table: string, - key: string | string[], -): Promise { +export async function idbDelete(table: string, key: string | string[]): Promise { if (!idb) { await idbInit(); } @@ -243,6 +236,8 @@ export async function idbDelete( const objectStore = txn.objectStore(table); const request = objectStore.delete(key); request.onerror = reject; - request.onsuccess = () => { resolve(); }; + request.onsuccess = () => { + resolve(); + }; }); } diff --git a/src/utils/UrlUtils.ts b/src/utils/UrlUtils.ts index 6f441ff98e..bc185b2056 100644 --- a/src/utils/UrlUtils.ts +++ b/src/utils/UrlUtils.ts @@ -23,13 +23,13 @@ import url from "url"; * @returns {string} The abbreviated url */ export function abbreviateUrl(u: string): string { - if (!u) return ''; + if (!u) return ""; const parsedUrl = url.parse(u); // if it's something we can't parse as a url then just return it if (!parsedUrl) return u; - if (parsedUrl.path === '/') { + if (parsedUrl.path === "/") { // we ignore query / hash parts: these aren't relevant for IS server URLs return parsedUrl.host; } @@ -38,10 +38,10 @@ export function abbreviateUrl(u: string): string { } export function unabbreviateUrl(u: string): string { - if (!u) return ''; + if (!u) return ""; let longUrl = u; - if (!u.startsWith('https://')) longUrl = 'https://' + u; + if (!u.startsWith("https://")) longUrl = "https://" + u; const parsed = url.parse(longUrl); if (parsed.hostname === null) return u; diff --git a/src/utils/UserInteractiveAuth.ts b/src/utils/UserInteractiveAuth.ts index e3088fb3cb..5c7568ee6e 100644 --- a/src/utils/UserInteractiveAuth.ts +++ b/src/utils/UserInteractiveAuth.ts @@ -25,13 +25,13 @@ type FunctionWithUIA = (auth?: IAuthData, ...args: A[]) => Promise( requestFunction: FunctionWithUIA, opts: Omit, -): ((...args: A[]) => Promise) { - return async function(...args): Promise { +): (...args: A[]) => Promise { + return async function (...args): Promise { return new Promise((resolve, reject) => { const boundFunction = requestFunction.bind(opts.matrixClient) as FunctionWithUIA; boundFunction(undefined, ...args) .then((res) => resolve(res as R)) - .catch(error => { + .catch((error) => { if (error.httpStatus !== 401 || !error.data?.flows) { // doesn't look like an interactive-auth failure return reject(error); diff --git a/src/utils/WellKnownUtils.ts b/src/utils/WellKnownUtils.ts index 451f956f16..11137549a8 100644 --- a/src/utils/WellKnownUtils.ts +++ b/src/utils/WellKnownUtils.ts @@ -14,16 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { IClientWellKnown } from 'matrix-js-sdk/src/client'; -import { UnstableValue } from 'matrix-js-sdk/src/NamespacedValue'; +import { IClientWellKnown } from "matrix-js-sdk/src/client"; +import { UnstableValue } from "matrix-js-sdk/src/NamespacedValue"; -import { MatrixClientPeg } from '../MatrixClientPeg'; +import { MatrixClientPeg } from "../MatrixClientPeg"; const CALL_BEHAVIOUR_WK_KEY = "io.element.call_behaviour"; const E2EE_WK_KEY = "io.element.e2ee"; const E2EE_WK_KEY_DEPRECATED = "im.vector.riot.e2ee"; -export const TILE_SERVER_WK_KEY = new UnstableValue( - "m.tile_server", "org.matrix.msc3488.tile_server"); +export const TILE_SERVER_WK_KEY = new UnstableValue("m.tile_server", "org.matrix.msc3488.tile_server"); const EMBEDDED_PAGES_WK_PROPERTY = "io.element.embedded_pages"; /* eslint-disable camelcase */ @@ -66,23 +65,16 @@ export function getTileServerWellKnown(): ITileServerWellKnown | undefined { return tileServerFromWellKnown(MatrixClientPeg.get().getClientWellKnown()); } -export function tileServerFromWellKnown( - clientWellKnown?: IClientWellKnown | undefined, -): ITileServerWellKnown { - return ( - clientWellKnown?.[TILE_SERVER_WK_KEY.name] ?? - clientWellKnown?.[TILE_SERVER_WK_KEY.altName] - ); +export function tileServerFromWellKnown(clientWellKnown?: IClientWellKnown | undefined): ITileServerWellKnown { + return clientWellKnown?.[TILE_SERVER_WK_KEY.name] ?? clientWellKnown?.[TILE_SERVER_WK_KEY.altName]; } export function getEmbeddedPagesWellKnown(): IEmbeddedPagesWellKnown | undefined { return embeddedPagesFromWellKnown(MatrixClientPeg.get()?.getClientWellKnown()); } -export function embeddedPagesFromWellKnown( - clientWellKnown?: IClientWellKnown, -): IEmbeddedPagesWellKnown { - return (clientWellKnown?.[EMBEDDED_PAGES_WK_PROPERTY]); +export function embeddedPagesFromWellKnown(clientWellKnown?: IClientWellKnown): IEmbeddedPagesWellKnown { + return clientWellKnown?.[EMBEDDED_PAGES_WK_PROPERTY]; } export function isSecureBackupRequired(): boolean { @@ -106,10 +98,7 @@ export function getSecureBackupSetupMethods(): SecureBackupSetupMethod[] { wellKnown["secure_backup_setup_methods"].includes(SecureBackupSetupMethod.Passphrase) ) ) { - return [ - SecureBackupSetupMethod.Key, - SecureBackupSetupMethod.Passphrase, - ]; + return [SecureBackupSetupMethod.Key, SecureBackupSetupMethod.Passphrase]; } return wellKnown["secure_backup_setup_methods"]; } diff --git a/src/utils/Whenable.ts b/src/utils/Whenable.ts index 70bd45eb09..e421cb9fb6 100644 --- a/src/utils/Whenable.ts +++ b/src/utils/Whenable.ts @@ -28,7 +28,7 @@ export type WhenFn = (w: Whenable) => void; * the consumer needs to know *when* that happens. */ export abstract class Whenable implements IDestroyable { - private listeners: {condition: T | null, fn: WhenFn}[] = []; + private listeners: { condition: T | null; fn: WhenFn }[] = []; /** * Sets up a call to `fn` *when* the `condition` is met. diff --git a/src/utils/WidgetUtils.ts b/src/utils/WidgetUtils.ts index e6f75bb025..feb73e954d 100644 --- a/src/utils/WidgetUtils.ts +++ b/src/utils/WidgetUtils.ts @@ -25,11 +25,11 @@ import { ClientEvent, RoomStateEvent } from "matrix-js-sdk/src/matrix"; import { CallType } from "matrix-js-sdk/src/webrtc/call"; import { randomString, randomLowercaseString, randomUppercaseString } from "matrix-js-sdk/src/randomstring"; -import { MatrixClientPeg } from '../MatrixClientPeg'; -import PlatformPeg from '../PlatformPeg'; +import { MatrixClientPeg } from "../MatrixClientPeg"; +import PlatformPeg from "../PlatformPeg"; import SdkConfig from "../SdkConfig"; -import dis from '../dispatcher/dispatcher'; -import WidgetEchoStore from '../stores/WidgetEchoStore'; +import dis from "../dispatcher/dispatcher"; +import WidgetEchoStore from "../stores/WidgetEchoStore"; import { IntegrationManagers } from "../integrations/IntegrationManagers"; import { WidgetType } from "../widgets/WidgetType"; import { Jitsi } from "../widgets/Jitsi"; @@ -59,13 +59,13 @@ export default class WidgetUtils { */ static canUserModifyWidgets(roomId: string): boolean { if (!roomId) { - logger.warn('No room ID specified'); + logger.warn("No room ID specified"); return false; } const client = MatrixClientPeg.get(); if (!client) { - logger.warn('User must be be logged in'); + logger.warn("User must be be logged in"); return false; } @@ -77,7 +77,7 @@ export default class WidgetUtils { const me = client.credentials.userId; if (!me) { - logger.warn('Failed to get user ID'); + logger.warn("Failed to get user ID"); return false; } @@ -87,7 +87,7 @@ export default class WidgetUtils { } // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) - return room.currentState.maySendStateEvent('im.vector.modular.widgets', me); + return room.currentState.maySendStateEvent("im.vector.modular.widgets", me); } // TODO: Generify the name of this function. It's not just scalar. @@ -98,7 +98,7 @@ export default class WidgetUtils { */ static isScalarUrl(testUrlString: string): boolean { if (!testUrlString) { - logger.error('Scalar URL check failed. No URL specified'); + logger.error("Scalar URL check failed. No URL specified"); return false; } @@ -152,14 +152,14 @@ export default class WidgetUtils { } } - const startingAccountDataEvent = MatrixClientPeg.get().getAccountData('m.widgets'); + const startingAccountDataEvent = MatrixClientPeg.get().getAccountData("m.widgets"); if (eventInIntendedState(startingAccountDataEvent)) { resolve(); return; } function onAccountData(ev) { - const currentAccountDataEvent = MatrixClientPeg.get().getAccountData('m.widgets'); + const currentAccountDataEvent = MatrixClientPeg.get().getAccountData("m.widgets"); if (eventInIntendedState(currentAccountDataEvent)) { MatrixClientPeg.get().removeListener(ClientEvent.AccountData, onAccountData); clearTimeout(timerId); @@ -192,7 +192,7 @@ export default class WidgetUtils { // we're waiting for it to be in function eventsInIntendedState(evList) { const widgetPresent = evList.some((ev) => { - return ev.getContent() && ev.getContent()['id'] === widgetId; + return ev.getContent() && ev.getContent()["id"] === widgetId; }); if (add) { return widgetPresent; @@ -203,7 +203,7 @@ export default class WidgetUtils { const room = MatrixClientPeg.get().getRoom(roomId); // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) - const startingWidgetEvents = room.currentState.getStateEvents('im.vector.modular.widgets'); + const startingWidgetEvents = room.currentState.getStateEvents("im.vector.modular.widgets"); if (eventsInIntendedState(startingWidgetEvents)) { resolve(); return; @@ -213,7 +213,7 @@ export default class WidgetUtils { if (ev.getRoomId() !== roomId || ev.getType() !== "im.vector.modular.widgets") return; // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) - const currentWidgetEvents = room.currentState.getStateEvents('im.vector.modular.widgets'); + const currentWidgetEvents = room.currentState.getStateEvents("im.vector.modular.widgets"); if (eventsInIntendedState(currentWidgetEvents)) { MatrixClientPeg.get().removeListener(RoomStateEvent.Events, onRoomStateEvents); @@ -263,7 +263,7 @@ export default class WidgetUtils { content: content, sender: client.getUserId(), state_key: widgetId, - type: 'm.widget', + type: "m.widget", id: widgetId, }; } @@ -272,11 +272,14 @@ export default class WidgetUtils { // since the widget won't appear added until this happens. If we don't // wait for this, the action will complete but if the user is fast enough, // the widget still won't actually be there. - return client.setAccountData('m.widgets', userWidgets).then(() => { - return WidgetUtils.waitForUserWidget(widgetId, addingWidget); - }).then(() => { - dis.dispatch({ action: "user_widget_updated" }); - }); + return client + .setAccountData("m.widgets", userWidgets) + .then(() => { + return WidgetUtils.waitForUserWidget(widgetId, addingWidget); + }) + .then(() => { + dis.dispatch({ action: "user_widget_updated" }); + }); } static setRoomWidget( @@ -309,22 +312,21 @@ export default class WidgetUtils { return WidgetUtils.setRoomWidgetContent(roomId, widgetId, content); } - static setRoomWidgetContent( - roomId: string, - widgetId: string, - content: IWidget, - ) { + static setRoomWidgetContent(roomId: string, widgetId: string, content: IWidget) { const addingWidget = !!content.url; WidgetEchoStore.setRoomWidgetEcho(roomId, widgetId, content); const client = MatrixClientPeg.get(); // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) - return client.sendStateEvent(roomId, "im.vector.modular.widgets", content, widgetId).then(() => { - return WidgetUtils.waitForRoomWidget(widgetId, roomId, addingWidget); - }).finally(() => { - WidgetEchoStore.removeRoomWidgetEcho(roomId, widgetId); - }); + return client + .sendStateEvent(roomId, "im.vector.modular.widgets", content, widgetId) + .then(() => { + return WidgetUtils.waitForRoomWidget(widgetId, roomId, addingWidget); + }) + .finally(() => { + WidgetEchoStore.removeRoomWidgetEcho(roomId, widgetId); + }); } /** @@ -334,7 +336,7 @@ export default class WidgetUtils { */ static getRoomWidgets(room: Room) { // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) - const appsStateEvents = room.currentState.getStateEvents('im.vector.modular.widgets'); + const appsStateEvents = room.currentState.getStateEvents("im.vector.modular.widgets"); if (!appsStateEvents) { return []; } @@ -351,9 +353,9 @@ export default class WidgetUtils { static getUserWidgets(): Record { const client = MatrixClientPeg.get(); if (!client) { - throw new Error('User not logged in'); + throw new Error("User not logged in"); } - const userWidgets = client.getAccountData('m.widgets'); + const userWidgets = client.getAccountData("m.widgets"); if (userWidgets && userWidgets.getContent()) { return userWidgets.getContent(); } @@ -383,12 +385,12 @@ export default class WidgetUtils { */ static getIntegrationManagerWidgets(): IWidgetEvent[] { const widgets = WidgetUtils.getUserWidgetsArray(); - return widgets.filter(w => w.content && w.content.type === "m.integration_manager"); + return widgets.filter((w) => w.content && w.content.type === "m.integration_manager"); } static getRoomWidgetsOfType(room: Room, type: WidgetType): MatrixEvent[] { const widgets = WidgetUtils.getRoomWidgets(room) || []; - return widgets.filter(w => { + return widgets.filter((w) => { const content = w.getContent(); return content.url && type.matches(content.type); }); @@ -397,9 +399,9 @@ export default class WidgetUtils { static async removeIntegrationManagerWidgets(): Promise { const client = MatrixClientPeg.get(); if (!client) { - throw new Error('User not logged in'); + throw new Error("User not logged in"); } - const widgets = client.getAccountData('m.widgets'); + const widgets = client.getAccountData("m.widgets"); if (!widgets) return; const userWidgets: Record = widgets.getContent() || {}; Object.entries(userWidgets).forEach(([key, widget]) => { @@ -407,16 +409,16 @@ export default class WidgetUtils { delete userWidgets[key]; } }); - await client.setAccountData('m.widgets', userWidgets); + await client.setAccountData("m.widgets", userWidgets); } static addIntegrationManagerWidget(name: string, uiUrl: string, apiUrl: string): Promise { return WidgetUtils.setUserWidget( - "integration_manager_" + (new Date().getTime()), + "integration_manager_" + new Date().getTime(), WidgetType.INTEGRATION_MANAGER, uiUrl, "Integration manager: " + name, - { "api_url": apiUrl }, + { api_url: apiUrl }, ); } @@ -427,17 +429,17 @@ export default class WidgetUtils { static async removeStickerpickerWidgets(): Promise { const client = MatrixClientPeg.get(); if (!client) { - throw new Error('User not logged in'); + throw new Error("User not logged in"); } - const widgets = client.getAccountData('m.widgets'); + const widgets = client.getAccountData("m.widgets"); if (!widgets) return; const userWidgets: Record = widgets.getContent() || {}; Object.entries(userWidgets).forEach(([key, widget]) => { - if (widget.content && widget.content.type === 'm.stickerpicker') { + if (widget.content && widget.content.type === "m.stickerpicker") { delete userWidgets[key]; } }); - await client.setAccountData('m.widgets', userWidgets); + await client.setAccountData("m.widgets", userWidgets); } static async addJitsiWidget( @@ -452,7 +454,7 @@ export default class WidgetUtils { const widgetId = randomString(24); // Must be globally unique let confId; - if (auth === 'openidtoken-jwt') { + if (auth === "openidtoken-jwt") { // Create conference ID from room ID // For compatibility with Jitsi, use base32 without padding. // More details here: @@ -465,8 +467,8 @@ export default class WidgetUtils { // TODO: Remove URL hacks when the mobile clients eventually support v2 widgets const widgetUrl = new URL(WidgetUtils.getLocalJitsiWrapperUrl({ auth })); - widgetUrl.search = ''; // Causes the URL class use searchParams instead - widgetUrl.searchParams.set('confId', confId); + widgetUrl.search = ""; // Causes the URL class use searchParams instead + widgetUrl.searchParams.set("confId", confId); await WidgetUtils.setRoomWidget(roomId, widgetId, WidgetType.JITSI, widgetUrl.toString(), name, { conferenceId: confId, @@ -498,26 +500,26 @@ export default class WidgetUtils { return app as IApp; } - static getLocalJitsiWrapperUrl(opts: {forLocalRender?: boolean, auth?: string} = {}) { + static getLocalJitsiWrapperUrl(opts: { forLocalRender?: boolean; auth?: string } = {}) { // NB. we can't just encodeURIComponent all of these because the $ signs need to be there const queryStringParts = [ - 'conferenceDomain=$domain', - 'conferenceId=$conferenceId', - 'isAudioOnly=$isAudioOnly', - 'isVideoChannel=$isVideoChannel', - 'displayName=$matrix_display_name', - 'avatarUrl=$matrix_avatar_url', - 'userId=$matrix_user_id', - 'roomId=$matrix_room_id', - 'theme=$theme', - 'roomName=$roomName', + "conferenceDomain=$domain", + "conferenceId=$conferenceId", + "isAudioOnly=$isAudioOnly", + "isVideoChannel=$isVideoChannel", + "displayName=$matrix_display_name", + "avatarUrl=$matrix_avatar_url", + "userId=$matrix_user_id", + "roomId=$matrix_room_id", + "theme=$theme", + "roomName=$roomName", `supportsScreensharing=${PlatformPeg.get().supportsJitsiScreensharing()}`, - 'language=$org.matrix.msc2873.client_language', + "language=$org.matrix.msc2873.client_language", ]; if (opts.auth) { queryStringParts.push(`auth=${opts.auth}`); } - const queryString = queryStringParts.join('&'); + const queryString = queryStringParts.join("&"); let baseUrl = window.location.href; if (window.location.protocol !== "https:" && !opts.forLocalRender) { @@ -550,7 +552,9 @@ export default class WidgetUtils { static editWidget(room: Room, app: IApp): void { // noinspection JSIgnoredPromiseFromCall - IntegrationManagers.sharedInstance().getPrimaryManager().open(room, 'type_' + app.type, app.id); + IntegrationManagers.sharedInstance() + .getPrimaryManager() + .open(room, "type_" + app.type, app.id); } static isManagedByManager(app) { diff --git a/src/utils/arrays.ts b/src/utils/arrays.ts index b82be21443..cabfe3a83e 100644 --- a/src/utils/arrays.ts +++ b/src/utils/arrays.ts @@ -70,7 +70,7 @@ export function arraySmoothingResample(input: number[], points: number): number[ // never end, and we can over-average the data. Instead, we'll get as far as // we can and do a followup fast resample (the neighbouring points will be close // to the actual waveform, so we can get away with this safely). - while (samples.length > (points * 2) || samples.length === 0) { + while (samples.length > points * 2 || samples.length === 0) { samples = []; for (let i = 1; i < input.length - 1; i += 2) { const prevPoint = input[i - 1]; @@ -102,7 +102,7 @@ export function arraySmoothingResample(input: number[], points: number): number[ export function arrayRescale(input: number[], newMin: number, newMax: number): number[] { const min: number = Math.min(...input); const max: number = Math.max(...input); - return input.map(v => percentageWithin(percentageOf(v, min, max), newMin, newMax)); + return input.map((v) => percentageWithin(percentageOf(v, min, max), newMin, newMax)); } /** @@ -174,8 +174,8 @@ export function arrayHasDiff(a: any[], b: any[]): boolean { if (a.length === b.length) { // When the lengths are equal, check to see if either array is missing // an element from the other. - if (b.some(i => !a.includes(i))) return true; - if (a.some(i => !b.includes(i))) return true; + if (b.some((i) => !a.includes(i))) return true; + if (a.some((i) => !b.includes(i))) return true; // if all the keys are common, say so return false; @@ -184,7 +184,7 @@ export function arrayHasDiff(a: any[], b: any[]): boolean { } } -export type Diff = { added: T[], removed: T[] }; +export type Diff = { added: T[]; removed: T[] }; /** * Performs a diff on two arrays. The result is what is different with the @@ -196,8 +196,8 @@ export type Diff = { added: T[], removed: T[] }; */ export function arrayDiff(a: T[], b: T[]): Diff { return { - added: b.filter(i => !a.includes(i)), - removed: a.filter(i => !b.includes(i)), + added: b.filter((i) => !a.includes(i)), + removed: a.filter((i) => !b.includes(i)), }; } @@ -208,7 +208,7 @@ export function arrayDiff(a: T[], b: T[]): Diff { * @returns The intersection of the arrays. */ export function arrayIntersection(a: T[], b: T[]): T[] { - return a.filter(i => b.includes(i)); + return a.filter((i) => b.includes(i)); } /** @@ -217,10 +217,12 @@ export function arrayIntersection(a: T[], b: T[]): T[] { * @returns The union of all given arrays. */ export function arrayUnion(...a: T[][]): T[] { - return Array.from(a.reduce((c, v) => { - v.forEach(i => c.add(i)); - return c; - }, new Set())); + return Array.from( + a.reduce((c, v) => { + v.forEach((i) => c.add(i)); + return c; + }, new Set()), + ); } /** @@ -246,8 +248,7 @@ export class ArrayUtil { * Create a new array helper. * @param a The array to help. Can be modified in-place. */ - constructor(private a: T[]) { - } + constructor(private a: T[]) {} /** * The value of this array, after all appropriate alterations. @@ -280,8 +281,7 @@ export class GroupedArray { * Creates a new group helper. * @param val The group to help. Can be modified in-place. */ - constructor(private val: Map) { - } + constructor(private val: Map) {} /** * The value of this group, after all applicable alterations. diff --git a/src/utils/beacon/bounds.ts b/src/utils/beacon/bounds.ts index 43c063b1c5..c7942953e4 100644 --- a/src/utils/beacon/bounds.ts +++ b/src/utils/beacon/bounds.ts @@ -36,8 +36,9 @@ export type Bounds = { * west of Greenwich has a negative longitude, min -180 */ export const getBeaconBounds = (beacons: Beacon[]): Bounds | undefined => { - const coords = beacons.filter(beacon => !!beacon.latestLocationState) - .map(beacon => parseGeoUri(beacon.latestLocationState.uri)); + const coords = beacons + .filter((beacon) => !!beacon.latestLocationState) + .map((beacon) => parseGeoUri(beacon.latestLocationState.uri)); if (!coords.length) { return; @@ -51,6 +52,6 @@ export const getBeaconBounds = (beacons: Beacon[]): Bounds | undefined => { north: sortedByLat[0].latitude, south: sortedByLat[sortedByLat.length - 1].latitude, east: sortedByLong[0].longitude, - west: sortedByLong[sortedByLong.length -1].longitude, + west: sortedByLong[sortedByLong.length - 1].longitude, }; }; diff --git a/src/utils/beacon/duration.ts b/src/utils/beacon/duration.ts index bbd51c7b5d..136207b6f3 100644 --- a/src/utils/beacon/duration.ts +++ b/src/utils/beacon/duration.ts @@ -25,7 +25,7 @@ import { Beacon } from "matrix-js-sdk/src/matrix"; * @returns remainingMs */ export const msUntilExpiry = (startTimestamp: number, durationMs: number): number => - Math.max(0, (startTimestamp + durationMs) - Date.now()); + Math.max(0, startTimestamp + durationMs - Date.now()); export const getBeaconMsUntilExpiry = (beaconInfo: BeaconInfoState): number => msUntilExpiry(beaconInfo.timestamp, beaconInfo.timeout); diff --git a/src/utils/beacon/geolocation.ts b/src/utils/beacon/geolocation.ts index 6925ca73b5..5142984478 100644 --- a/src/utils/beacon/geolocation.ts +++ b/src/utils/beacon/geolocation.ts @@ -20,15 +20,15 @@ import { logger } from "matrix-js-sdk/src/logger"; // https://developer.mozilla.org/en-US/docs/Web/API/GeolocationPositionError export enum GeolocationError { // no navigator.geolocation - Unavailable = 'Unavailable', + Unavailable = "Unavailable", // The acquisition of the geolocation information failed because the page didn't have the permission to do it. - PermissionDenied = 'PermissionDenied', + PermissionDenied = "PermissionDenied", // The acquisition of the geolocation failed because at least one internal source of position returned an internal error. - PositionUnavailable = 'PositionUnavailable', + PositionUnavailable = "PositionUnavailable", // The time allowed to acquire the geolocation was reached before the information was obtained. - Timeout = 'Timeout', + Timeout = "Timeout", // other unexpected failure - Default = 'Default' + Default = "Default", } const GeolocationOptions = { @@ -37,12 +37,12 @@ const GeolocationOptions = { }; const isGeolocationPositionError = (error: unknown): error is GeolocationPositionError => - typeof error === 'object' && !!error['PERMISSION_DENIED']; + typeof error === "object" && !!error["PERMISSION_DENIED"]; /** * Maps GeolocationPositionError to our GeolocationError enum */ export const mapGeolocationError = (error: GeolocationPositionError | Error): GeolocationError => { - logger.error('Geolocation failed', error?.message ?? error); + logger.error("Geolocation failed", error?.message ?? error); if (isGeolocationPositionError(error)) { switch (error?.code) { @@ -83,9 +83,7 @@ export type TimedGeoUri = { }; export const genericPositionFromGeolocation = (geoPosition: GeolocationPosition): GenericPosition => { - const { - latitude, longitude, altitude, accuracy, - } = geoPosition.coords; + const { latitude, longitude, altitude, accuracy } = geoPosition.coords; return { // safari reports geolocation timestamps as Apple Cocoa Core Data timestamp @@ -93,23 +91,18 @@ export const genericPositionFromGeolocation = (geoPosition: GeolocationPosition) // they also use local time, not utc // to simplify, just use Date.now() timestamp: Date.now(), - latitude, longitude, altitude, accuracy, + latitude, + longitude, + altitude, + accuracy, }; }; export const getGeoUri = (position: GenericPosition): string => { const lat = position.latitude; const lon = position.longitude; - const alt = ( - Number.isFinite(position.altitude) - ? `,${position.altitude}` - : "" - ); - const acc = ( - Number.isFinite(position.accuracy) - ? `;u=${position.accuracy}` - : "" - ); + const alt = Number.isFinite(position.altitude) ? `,${position.altitude}` : ""; + const acc = Number.isFinite(position.accuracy) ? `;u=${position.accuracy}` : ""; return `geo:${lat},${lon}${alt}${acc}`; }; diff --git a/src/utils/beacon/getShareableLocation.ts b/src/utils/beacon/getShareableLocation.ts index b2a63db060..1b353f2b04 100644 --- a/src/utils/beacon/getShareableLocation.ts +++ b/src/utils/beacon/getShareableLocation.ts @@ -14,11 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { - MatrixClient, - MatrixEvent, - getBeaconInfoIdentifier, -} from "matrix-js-sdk/src/matrix"; +import { MatrixClient, MatrixEvent, getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix"; /** * Beacons should only have shareable locations (open in external mapping tool, forward) diff --git a/src/utils/beacon/index.ts b/src/utils/beacon/index.ts index 3da707b603..34be8c9f5e 100644 --- a/src/utils/beacon/index.ts +++ b/src/utils/beacon/index.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -export * from './duration'; -export * from './geolocation'; -export * from './useBeacon'; -export * from './useOwnLiveBeacons'; +export * from "./duration"; +export * from "./geolocation"; +export * from "./useBeacon"; +export * from "./useOwnLiveBeacons"; diff --git a/src/utils/beacon/timeline.ts b/src/utils/beacon/timeline.ts index 9c566e0d68..a04a61f364 100644 --- a/src/utils/beacon/timeline.ts +++ b/src/utils/beacon/timeline.ts @@ -21,11 +21,8 @@ import { M_BEACON_INFO } from "matrix-js-sdk/src/@types/beacon"; * beacon_info events without live property set to true * should be displayed in the timeline */ -export const shouldDisplayAsBeaconTile = (event: MatrixEvent): boolean => ( +export const shouldDisplayAsBeaconTile = (event: MatrixEvent): boolean => M_BEACON_INFO.matches(event.getType()) && - ( - event.getContent()?.live || + (event.getContent()?.live || // redacted beacons should show 'message deleted' tile - event.isRedacted() - ) -); + event.isRedacted()); diff --git a/src/utils/beacon/useBeacon.ts b/src/utils/beacon/useBeacon.ts index e1dcfc4975..2726262ec4 100644 --- a/src/utils/beacon/useBeacon.ts +++ b/src/utils/beacon/useBeacon.ts @@ -15,12 +15,7 @@ limitations under the License. */ import { useContext, useEffect, useState } from "react"; -import { - Beacon, - BeaconEvent, - MatrixEvent, - getBeaconInfoIdentifier, -} from "matrix-js-sdk/src/matrix"; +import { Beacon, BeaconEvent, MatrixEvent, getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix"; import MatrixClientContext from "../../contexts/MatrixClientContext"; import { useEventEmitterState } from "../../hooks/useEventEmitter"; @@ -56,11 +51,7 @@ export const useBeacon = (beaconInfoEvent: MatrixEvent): Beacon | undefined => { // beacon update will fire when this beacon is superseded // check the updated event id for equality to the matrix event - const beaconInstanceEventId = useEventEmitterState( - beacon, - BeaconEvent.Update, - () => beacon?.beaconInfoId, - ); + const beaconInstanceEventId = useEventEmitterState(beacon, BeaconEvent.Update, () => beacon?.beaconInfoId); useEffect(() => { if (beaconInstanceEventId && beaconInstanceEventId !== beaconInfoEvent.getId()) { diff --git a/src/utils/beacon/useLiveBeacons.ts b/src/utils/beacon/useLiveBeacons.ts index cbde1a40e7..fd6b2164d9 100644 --- a/src/utils/beacon/useLiveBeacons.ts +++ b/src/utils/beacon/useLiveBeacons.ts @@ -14,12 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { - Beacon, - Room, - RoomStateEvent, - MatrixClient, -} from "matrix-js-sdk/src/matrix"; +import { Beacon, Room, RoomStateEvent, MatrixClient } from "matrix-js-sdk/src/matrix"; import { useEventEmitterState } from "../../hooks/useEventEmitter"; @@ -28,13 +23,11 @@ import { useEventEmitterState } from "../../hooks/useEventEmitter"; * * Beacons are removed from array when they become inactive */ -export const useLiveBeacons = (roomId: Room['roomId'], matrixClient: MatrixClient): Beacon[] => { +export const useLiveBeacons = (roomId: Room["roomId"], matrixClient: MatrixClient): Beacon[] => { const room = matrixClient.getRoom(roomId); - const liveBeacons = useEventEmitterState( - room.currentState, - RoomStateEvent.BeaconLiveness, - () => room.currentState?.liveBeaconIds.map(beaconIdentifier => room.currentState.beacons.get(beaconIdentifier)), + const liveBeacons = useEventEmitterState(room.currentState, RoomStateEvent.BeaconLiveness, () => + room.currentState?.liveBeaconIds.map((beaconIdentifier) => room.currentState.beacons.get(beaconIdentifier)), ); return liveBeacons; diff --git a/src/utils/beacon/useOwnLiveBeacons.ts b/src/utils/beacon/useOwnLiveBeacons.ts index d83a66a1d4..a70eceb7df 100644 --- a/src/utils/beacon/useOwnLiveBeacons.ts +++ b/src/utils/beacon/useOwnLiveBeacons.ts @@ -43,15 +43,13 @@ export const useOwnLiveBeacons = (liveBeaconIds: BeaconIdentifier[]): LiveBeacon const hasLocationPublishError = useEventEmitterState( OwnBeaconStore.instance, OwnBeaconStoreEvent.LocationPublishError, - () => - liveBeaconIds.some(OwnBeaconStore.instance.beaconHasLocationPublishError), + () => liveBeaconIds.some(OwnBeaconStore.instance.beaconHasLocationPublishError), ); const hasStopSharingError = useEventEmitterState( OwnBeaconStore.instance, OwnBeaconStoreEvent.BeaconUpdateError, - () => - liveBeaconIds.some(id => OwnBeaconStore.instance.beaconUpdateErrors.has(id)), + () => liveBeaconIds.some((id) => OwnBeaconStore.instance.beaconUpdateErrors.has(id)), ); useEffect(() => { @@ -66,21 +64,22 @@ export const useOwnLiveBeacons = (liveBeaconIds: BeaconIdentifier[]): LiveBeacon }, [liveBeaconIds]); // select the beacon with latest expiry to display expiry time - const beacon = liveBeaconIds.map(beaconId => OwnBeaconStore.instance.getBeaconById(beaconId)) + const beacon = liveBeaconIds + .map((beaconId) => OwnBeaconStore.instance.getBeaconById(beaconId)) .sort(sortBeaconsByLatestExpiry) .shift(); const onStopSharing = async () => { setStoppingInProgress(true); try { - await Promise.all(liveBeaconIds.map(beaconId => OwnBeaconStore.instance.stopBeacon(beaconId))); + await Promise.all(liveBeaconIds.map((beaconId) => OwnBeaconStore.instance.stopBeacon(beaconId))); } catch (error) { setStoppingInProgress(false); } }; const onResetLocationPublishError = () => { - liveBeaconIds.forEach(beaconId => { + liveBeaconIds.forEach((beaconId) => { OwnBeaconStore.instance.resetLocationPublishError(beaconId); }); }; diff --git a/src/utils/blobs.ts b/src/utils/blobs.ts index 9dea3d226c..e1afe212a8 100644 --- a/src/utils/blobs.ts +++ b/src/utils/blobs.ts @@ -49,34 +49,34 @@ limitations under the License. // text/html, text/xhtml, image/svg, image/svg+xml, image/pdf, and similar. const ALLOWED_BLOB_MIMETYPES = [ - 'image/jpeg', - 'image/gif', - 'image/png', - 'image/apng', - 'image/webp', - 'image/avif', + "image/jpeg", + "image/gif", + "image/png", + "image/apng", + "image/webp", + "image/avif", - 'video/mp4', - 'video/webm', - 'video/ogg', - 'video/quicktime', + "video/mp4", + "video/webm", + "video/ogg", + "video/quicktime", - 'audio/mp4', - 'audio/webm', - 'audio/aac', - 'audio/mpeg', - 'audio/ogg', - 'audio/wave', - 'audio/wav', - 'audio/x-wav', - 'audio/x-pn-wav', - 'audio/flac', - 'audio/x-flac', + "audio/mp4", + "audio/webm", + "audio/aac", + "audio/mpeg", + "audio/ogg", + "audio/wave", + "audio/wav", + "audio/x-wav", + "audio/x-pn-wav", + "audio/flac", + "audio/x-flac", ]; export function getBlobSafeMimeType(mimetype: string): string { if (!ALLOWED_BLOB_MIMETYPES.includes(mimetype)) { - return 'application/octet-stream'; + return "application/octet-stream"; } return mimetype; } diff --git a/src/utils/colour.ts b/src/utils/colour.ts index 96eabd4eb4..518b11f835 100644 --- a/src/utils/colour.ts +++ b/src/utils/colour.ts @@ -14,12 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { split } from 'lodash'; +import { split } from "lodash"; export function textToHtmlRainbow(str: string): string { const frequency = (2 * Math.PI) / str.length; - return split(str, '') + return split(str, "") .map((c, i) => { if (c === " ") { return c; diff --git a/src/utils/createMatrixClient.ts b/src/utils/createMatrixClient.ts index e8b5276880..389d7269b4 100644 --- a/src/utils/createMatrixClient.ts +++ b/src/utils/createMatrixClient.ts @@ -62,9 +62,7 @@ export default function createMatrixClient(opts: ICreateClientOpts): MatrixClien } if (indexedDB) { - storeOpts.cryptoStore = new IndexedDBCryptoStore( - indexedDB, "matrix-js-sdk:crypto", - ); + storeOpts.cryptoStore = new IndexedDBCryptoStore(indexedDB, "matrix-js-sdk:crypto"); } else if (localStorage) { storeOpts.cryptoStore = new LocalStorageCryptoStore(localStorage); } else { diff --git a/src/utils/device/clientInformation.ts b/src/utils/device/clientInformation.ts index 5c9b65b54b..8b7b802239 100644 --- a/src/utils/device/clientInformation.ts +++ b/src/utils/device/clientInformation.ts @@ -70,9 +70,7 @@ export const recordClientInformation = async ( * @todo(kerrya) revisit after MSC3391: account data deletion is done * (PSBE-12) */ -export const removeClientInformation = async ( - matrixClient: MatrixClient, -): Promise => { +export const removeClientInformation = async (matrixClient: MatrixClient): Promise => { const deviceId = matrixClient.getDeviceId(); const type = getClientInformationEventType(deviceId); const clientInformation = getDeviceClientInformation(matrixClient, deviceId); @@ -84,7 +82,7 @@ export const removeClientInformation = async ( }; const sanitizeContentString = (value: unknown): string | undefined => - value && typeof value === 'string' ? value : undefined; + value && typeof value === "string" ? value : undefined; export const getDeviceClientInformation = (matrixClient: MatrixClient, deviceId: string): DeviceClientInformation => { const event = matrixClient.getAccountData(getClientInformationEventType(deviceId)); @@ -101,4 +99,3 @@ export const getDeviceClientInformation = (matrixClient: MatrixClient, deviceId: url: sanitizeContentString(url), }; }; - diff --git a/src/utils/device/parseUserAgent.ts b/src/utils/device/parseUserAgent.ts index 3eee617765..724ef617da 100644 --- a/src/utils/device/parseUserAgent.ts +++ b/src/utils/device/parseUserAgent.ts @@ -14,13 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -import UAParser from 'ua-parser-js'; +import UAParser from "ua-parser-js"; export enum DeviceType { - Desktop = 'Desktop', - Mobile = 'Mobile', - Web = 'Web', - Unknown = 'Unknown', + Desktop = "Desktop", + Mobile = "Mobile", + Web = "Web", + Unknown = "Unknown", } export type ExtendedDeviceInformation = { deviceType: DeviceType; @@ -42,17 +42,13 @@ const getDeviceType = ( browser: UAParser.IBrowser, operatingSystem: UAParser.IOS, ): DeviceType => { - if (browser.name === 'Electron') { + if (browser.name === "Electron") { return DeviceType.Desktop; } if (!!browser.name) { return DeviceType.Web; } - if ( - device.type === 'mobile' || - operatingSystem.name?.includes('Android') || - userAgent.indexOf(IOS_KEYWORD) > -1 - ) { + if (device.type === "mobile" || operatingSystem.name?.includes("Android") || userAgent.indexOf(IOS_KEYWORD) > -1) { return DeviceType.Mobile; } return DeviceType.Unknown; @@ -72,18 +68,18 @@ const checkForCustomValues = (userAgent: string): CustomValues => { return {}; } - const mightHaveDevice = userAgent.includes('('); + const mightHaveDevice = userAgent.includes("("); if (!mightHaveDevice) { return {}; } - const deviceInfoSegments = userAgent.substring(userAgent.indexOf('(') + 1).split('; '); + const deviceInfoSegments = userAgent.substring(userAgent.indexOf("(") + 1).split("; "); const customDeviceModel = deviceInfoSegments[0] || undefined; const customDeviceOS = deviceInfoSegments[1] || undefined; return { customDeviceModel, customDeviceOS }; }; const concatenateNameAndVersion = (name?: string, version?: string): string | undefined => - name && [name, version].filter(Boolean).join(' '); + name && [name, version].filter(Boolean).join(" "); export const parseUserAgent = (userAgent?: string): ExtendedDeviceInformation => { if (!userAgent) { @@ -111,9 +107,8 @@ export const parseUserAgent = (userAgent?: string): ExtendedDeviceInformation => const client = concatenateNameAndVersion(browser.name, browser.version); // only try to parse custom model and OS when device type is known - const { customDeviceModel, customDeviceOS } = deviceType !== DeviceType.Unknown - ? checkForCustomValues(userAgent) - : {} as CustomValues; + const { customDeviceModel, customDeviceOS } = + deviceType !== DeviceType.Unknown ? checkForCustomValues(userAgent) : ({} as CustomValues); return { deviceType, diff --git a/src/utils/device/snoozeBulkUnverifiedDeviceReminder.ts b/src/utils/device/snoozeBulkUnverifiedDeviceReminder.ts index 80f107b18a..ec70f49240 100644 --- a/src/utils/device/snoozeBulkUnverifiedDeviceReminder.ts +++ b/src/utils/device/snoozeBulkUnverifiedDeviceReminder.ts @@ -16,14 +16,14 @@ limitations under the License. import { logger } from "matrix-js-sdk/src/logger"; -const SNOOZE_KEY = 'mx_snooze_bulk_unverified_device_nag'; +const SNOOZE_KEY = "mx_snooze_bulk_unverified_device_nag"; // one week const snoozePeriod = 1000 * 60 * 60 * 24 * 7; export const snoozeBulkUnverifiedDeviceReminder = () => { try { localStorage.setItem(SNOOZE_KEY, String(Date.now())); } catch (error) { - logger.error('Failed to persist bulk unverified device nag snooze', error); + logger.error("Failed to persist bulk unverified device nag snooze", error); } }; @@ -31,9 +31,9 @@ export const isBulkUnverifiedDeviceReminderSnoozed = () => { try { const snoozedTimestamp = localStorage.getItem(SNOOZE_KEY); - const parsedTimestamp = Number.parseInt(snoozedTimestamp || '', 10); + const parsedTimestamp = Number.parseInt(snoozedTimestamp || "", 10); - return Number.isInteger(parsedTimestamp) && (parsedTimestamp + snoozePeriod) > Date.now(); + return Number.isInteger(parsedTimestamp) && parsedTimestamp + snoozePeriod > Date.now(); } catch (error) { return false; } diff --git a/src/utils/direct-messages.ts b/src/utils/direct-messages.ts index 2ed20b4f64..3e117c0717 100644 --- a/src/utils/direct-messages.ts +++ b/src/utils/direct-messages.ts @@ -29,10 +29,7 @@ import { privateShouldBeEncrypted } from "./rooms"; import { createDmLocalRoom } from "./dm/createDmLocalRoom"; import { startDm } from "./dm/startDm"; -export async function startDmOnFirstMessage( - client: MatrixClient, - targets: Member[], -): Promise { +export async function startDmOnFirstMessage(client: MatrixClient, targets: Member[]): Promise { const existingRoom = findDMRoom(client, targets); if (existingRoom) { dis.dispatch({ @@ -114,7 +111,7 @@ export class DirectoryMember extends Member { private readonly avatarUrl?: string; // eslint-disable-next-line camelcase - constructor(userDirResult: { user_id: string, display_name?: string, avatar_url?: string }) { + constructor(userDirResult: { user_id: string; display_name?: string; avatar_url?: string }) { super(); this._userId = userDirResult.user_id; this.displayName = userDirResult.display_name; @@ -147,7 +144,7 @@ export class ThreepidMember extends Member { // better type support in the react-sdk we can use this trick to determine the kind // of 3PID we're dealing with, if any. get isEmail(): boolean { - return this.id.includes('@'); + return this.id.includes("@"); } // These next class members are for the Member interface @@ -181,9 +178,9 @@ export async function determineCreateRoomEncryptionOption(client: MatrixClient, if (privateShouldBeEncrypted()) { // Check whether all users have uploaded device keys before. // If so, enable encryption in the new room. - const has3PidMembers = targets.some(t => t instanceof ThreepidMember); + const has3PidMembers = targets.some((t) => t instanceof ThreepidMember); if (!has3PidMembers) { - const targetIds = targets.map(t => t.userId); + const targetIds = targets.map((t) => t.userId); const allHaveDeviceKeys = await canEncryptToAllUsers(client, targetIds); if (allHaveDeviceKeys) { return true; diff --git a/src/utils/dm/createDmLocalRoom.ts b/src/utils/dm/createDmLocalRoom.ts index 9fe68986bc..822ae9700b 100644 --- a/src/utils/dm/createDmLocalRoom.ts +++ b/src/utils/dm/createDmLocalRoom.ts @@ -30,28 +30,27 @@ import { determineCreateRoomEncryptionOption, Member } from "../../../src/utils/ * @param {Member[]} targets DM partners * @returns {Promise} Resolves to the new local room */ -export async function createDmLocalRoom( - client: MatrixClient, - targets: Member[], -): Promise { +export async function createDmLocalRoom(client: MatrixClient, targets: Member[]): Promise { const userId = client.getUserId(); const localRoom = new LocalRoom(LOCAL_ROOM_ID_PREFIX + client.makeTxnId(), client, userId); const events = []; - events.push(new MatrixEvent({ - event_id: `~${localRoom.roomId}:${client.makeTxnId()}`, - type: EventType.RoomCreate, - content: { - creator: userId, - room_version: KNOWN_SAFE_ROOM_VERSION, - }, - state_key: "", - user_id: userId, - sender: userId, - room_id: localRoom.roomId, - origin_server_ts: Date.now(), - })); + events.push( + new MatrixEvent({ + event_id: `~${localRoom.roomId}:${client.makeTxnId()}`, + type: EventType.RoomCreate, + content: { + creator: userId, + room_version: KNOWN_SAFE_ROOM_VERSION, + }, + state_key: "", + user_id: userId, + sender: userId, + room_id: localRoom.roomId, + origin_server_ts: Date.now(), + }), + ); if (await determineCreateRoomEncryptionOption(client, targets)) { localRoom.encrypted = true; @@ -71,45 +70,51 @@ export async function createDmLocalRoom( ); } - events.push(new MatrixEvent({ - event_id: `~${localRoom.roomId}:${client.makeTxnId()}`, - type: EventType.RoomMember, - content: { - displayname: userId, - membership: "join", - }, - state_key: userId, - user_id: userId, - sender: userId, - room_id: localRoom.roomId, - })); - - targets.forEach((target: Member) => { - events.push(new MatrixEvent({ + events.push( + new MatrixEvent({ event_id: `~${localRoom.roomId}:${client.makeTxnId()}`, type: EventType.RoomMember, content: { - displayname: target.name, - avatar_url: target.getMxcAvatarUrl(), - membership: "invite", - isDirect: true, - }, - state_key: target.userId, - sender: userId, - room_id: localRoom.roomId, - })); - events.push(new MatrixEvent({ - event_id: `~${localRoom.roomId}:${client.makeTxnId()}`, - type: EventType.RoomMember, - content: { - displayname: target.name, - avatar_url: target.getMxcAvatarUrl(), + displayname: userId, membership: "join", }, - state_key: target.userId, - sender: target.userId, + state_key: userId, + user_id: userId, + sender: userId, room_id: localRoom.roomId, - })); + }), + ); + + targets.forEach((target: Member) => { + events.push( + new MatrixEvent({ + event_id: `~${localRoom.roomId}:${client.makeTxnId()}`, + type: EventType.RoomMember, + content: { + displayname: target.name, + avatar_url: target.getMxcAvatarUrl(), + membership: "invite", + isDirect: true, + }, + state_key: target.userId, + sender: userId, + room_id: localRoom.roomId, + }), + ); + events.push( + new MatrixEvent({ + event_id: `~${localRoom.roomId}:${client.makeTxnId()}`, + type: EventType.RoomMember, + content: { + displayname: target.name, + avatar_url: target.getMxcAvatarUrl(), + membership: "join", + }, + state_key: target.userId, + sender: target.userId, + room_id: localRoom.roomId, + }), + ); }); localRoom.targets = targets; diff --git a/src/utils/dm/findDMForUser.ts b/src/utils/dm/findDMForUser.ts index 47e3c87a74..babf8bd2af 100644 --- a/src/utils/dm/findDMForUser.ts +++ b/src/utils/dm/findDMForUser.ts @@ -30,29 +30,30 @@ import { getFunctionalMembers } from "../room/getFunctionalMembers"; */ export function findDMForUser(client: MatrixClient, userId: string): Room { const roomIds = DMRoomMap.shared().getDMRoomsForUserId(userId); - const rooms = roomIds.map(id => client.getRoom(id)); - const suitableDMRooms = rooms.filter(r => { - // Validate that we are joined and the other person is also joined. We'll also make sure - // that the room also looks like a DM (until we have canonical DMs to tell us). For now, - // a DM is a room of two people that contains those two people exactly. This does mean - // that bots, assistants, etc will ruin a room's DM-ness, though this is a problem for - // canonical DMs to solve. - if (r && r.getMyMembership() === "join") { - if (isLocalRoom(r)) return false; + const rooms = roomIds.map((id) => client.getRoom(id)); + const suitableDMRooms = rooms + .filter((r) => { + // Validate that we are joined and the other person is also joined. We'll also make sure + // that the room also looks like a DM (until we have canonical DMs to tell us). For now, + // a DM is a room of two people that contains those two people exactly. This does mean + // that bots, assistants, etc will ruin a room's DM-ness, though this is a problem for + // canonical DMs to solve. + if (r && r.getMyMembership() === "join") { + if (isLocalRoom(r)) return false; - const functionalUsers = getFunctionalMembers(r); - const members = r.currentState.getMembers(); - const joinedMembers = members.filter( - m => !functionalUsers.includes(m.userId) && isJoinedOrNearlyJoined(m.membership), - ); - const otherMember = joinedMembers.find(m => m.userId === userId); - return otherMember && joinedMembers.length === 2; - } - return false; - }).sort((r1, r2) => { - return r2.getLastActiveTimestamp() - - r1.getLastActiveTimestamp(); - }); + const functionalUsers = getFunctionalMembers(r); + const members = r.currentState.getMembers(); + const joinedMembers = members.filter( + (m) => !functionalUsers.includes(m.userId) && isJoinedOrNearlyJoined(m.membership), + ); + const otherMember = joinedMembers.find((m) => m.userId === userId); + return otherMember && joinedMembers.length === 2; + } + return false; + }) + .sort((r1, r2) => { + return r2.getLastActiveTimestamp() - r1.getLastActiveTimestamp(); + }); if (suitableDMRooms.length) { return suitableDMRooms[0]; } diff --git a/src/utils/dm/findDMRoom.ts b/src/utils/dm/findDMRoom.ts index 8cc6fa0d6d..d8cbb56d90 100644 --- a/src/utils/dm/findDMRoom.ts +++ b/src/utils/dm/findDMRoom.ts @@ -28,7 +28,7 @@ import { findDMForUser } from "./findDMForUser"; * @returns {Room | null} Resolved so the room if found, else null */ export function findDMRoom(client: MatrixClient, targets: Member[]): Room | null { - const targetIds = targets.map(t => t.userId); + const targetIds = targets.map((t) => t.userId); let existingRoom: Room; if (targetIds.length === 1) { existingRoom = findDMForUser(client, targetIds[0]); diff --git a/src/utils/dm/startDm.ts b/src/utils/dm/startDm.ts index c608a8b18d..ed5071bcf5 100644 --- a/src/utils/dm/startDm.ts +++ b/src/utils/dm/startDm.ts @@ -32,7 +32,7 @@ import createRoom from "../../createRoom"; * @returns {Promise { - const targetIds = targets.map(t => t.userId); + const targetIds = targets.map((t) => t.userId); // Check if there is already a DM with these people and reuse it if possible. let existingRoom: Room; @@ -69,14 +69,14 @@ export async function startDm(client: MatrixClient, targets: Member[], showSpinn createRoomOptions.createOpts = targetIds.reduce( (roomOptions, address) => { const type = getAddressType(address); - if (type === 'email') { + if (type === "email") { const invite: IInvite3PID = { id_server: client.getIdentityServerUrl(true), - medium: 'email', + medium: "email", address, }; roomOptions.invite_3pid.push(invite); - } else if (type === 'mx-user-id') { + } else if (type === "mx-user-id") { roomOptions.invite.push(address); } return roomOptions; diff --git a/src/utils/error.ts b/src/utils/error.ts index 8dec29e7f0..e52c0e4aeb 100644 --- a/src/utils/error.ts +++ b/src/utils/error.ts @@ -15,10 +15,7 @@ limitations under the License. */ export class GenericError extends Error { - constructor( - public readonly message: string, - public readonly description?: string | undefined, - ) { + constructor(public readonly message: string, public readonly description?: string | undefined) { super(message); } } diff --git a/src/utils/exportUtils/Exporter.ts b/src/utils/exportUtils/Exporter.ts index ec20f395e3..e855310ab6 100644 --- a/src/utils/exportUtils/Exporter.ts +++ b/src/utils/exportUtils/Exporter.ts @@ -48,9 +48,10 @@ export default abstract class Exporter { protected exportOptions: IExportOptions, protected setProgressText: React.Dispatch>, ) { - if (exportOptions.maxSize < 1 * 1024 * 1024|| // Less than 1 MB + if ( + exportOptions.maxSize < 1 * 1024 * 1024 || // Less than 1 MB exportOptions.maxSize > 8000 * 1024 * 1024 || // More than 8 GB - exportOptions.numberOfMessages > 10**8 + exportOptions.numberOfMessages > 10 ** 8 ) { throw new Error("Invalid export options"); } @@ -64,7 +65,7 @@ export default abstract class Exporter { protected onBeforeUnload(e: BeforeUnloadEvent): string { e.preventDefault(); - return e.returnValue = _t("Are you sure you want to exit during this export?"); + return (e.returnValue = _t("Are you sure you want to exit during this export?")); } protected updateProgress(progress: string, log = true, show = true): void { @@ -84,8 +85,7 @@ export default abstract class Exporter { // First try to use the real name of the room, then a translated copy of a generic name, // then finally hardcoded default to guarantee we'll have a name. const safeRoomName = sanitizeFilename(this.room.name ?? _t("Unnamed Room")).trim() || "Unnamed Room"; - const safeDate = formatFullDateNoDayISO(new Date()) - .replace(/:/g, '-'); // ISO format automatically removes a lot of stuff for us + const safeDate = formatFullDateNoDayISO(new Date()).replace(/:/g, "-"); // ISO format automatically removes a lot of stuff for us const safeBrand = sanitizeFilename(brand); return `${safeBrand} - ${safeRoomName} - Chat Export - ${safeDate}`; } @@ -93,7 +93,7 @@ export default abstract class Exporter { protected async downloadZIP(): Promise { const filename = this.destinationFileName; const filenameWithoutExt = filename.substring(0, filename.length - 4); // take off the .zip - const { default: JSZip } = await import('jszip'); + const { default: JSZip } = await import("jszip"); const zip = new JSZip(); // Create a writable stream to the directory @@ -125,13 +125,9 @@ export default abstract class Exporter { protected setEventMetadata(event: MatrixEvent): MatrixEvent { const roomState = this.client.getRoom(this.room.roomId).currentState; - event.sender = roomState.getSentinelMember( - event.getSender(), - ); + event.sender = roomState.getSentinelMember(event.getSender()); if (event.getType() === "m.room.member") { - event.target = roomState.getSentinelMember( - event.getStateKey(), - ); + event.target = roomState.getSentinelMember(event.getStateKey()); } return event; } @@ -146,7 +142,7 @@ export default abstract class Exporter { limit = 40; break; default: - limit = 10**8; + limit = 10 ** 8; } return limit; } @@ -154,7 +150,7 @@ export default abstract class Exporter { protected async getRequiredEvents(): Promise { const eventMapper = this.client.getEventMapper(); - let prevToken: string|null = null; + let prevToken: string | null = null; let limit = this.getLimit(); const events: MatrixEvent[] = []; @@ -188,26 +184,30 @@ export default abstract class Exporter { } if (this.exportType === ExportType.LastNMessages) { - this.updateProgress(_t("Fetched %(count)s events out of %(total)s", { - count: events.length, - total: this.exportOptions.numberOfMessages, - })); + this.updateProgress( + _t("Fetched %(count)s events out of %(total)s", { + count: events.length, + total: this.exportOptions.numberOfMessages, + }), + ); } else { - this.updateProgress(_t("Fetched %(count)s events so far", { - count: events.length, - })); + this.updateProgress( + _t("Fetched %(count)s events so far", { + count: events.length, + }), + ); } prevToken = res.end; } // Reverse the events so that we preserve the order - for (let i = 0; i < Math.floor(events.length/2); i++) { + for (let i = 0; i < Math.floor(events.length / 2); i++) { [events[i], events[events.length - i - 1]] = [events[events.length - i - 1], events[i]]; } const decryptionPromises = events - .filter(event => event.isEncrypted()) - .map(event => { + .filter((event) => event.isEncrypted()) + .map((event) => { return this.client.decryptEventIfNeeded(event, { isRetry: true, emit: false, @@ -242,11 +242,11 @@ export default abstract class Exporter { } public splitFileName(file: string): string[] { - const lastDot = file.lastIndexOf('.'); + const lastDot = file.lastIndexOf("."); if (lastDot === -1) return [file, ""]; const fileName = file.slice(0, lastDot); const ext = file.slice(lastDot + 1); - return [fileName, '.' + ext]; + return [fileName, "." + ext]; } public getFilePath(event: MatrixEvent): string { @@ -271,7 +271,7 @@ export default abstract class Exporter { if (event.getType() === "m.sticker") fileExt = ".png"; if (isVoiceMessage(event)) fileExt = ".ogg"; - return fileDirectory + "/" + fileName + '-' + fileDate + fileExt; + return fileDirectory + "/" + fileName + "-" + fileDate + fileExt; } protected isReply(event: MatrixEvent): boolean { diff --git a/src/utils/exportUtils/HtmlExport.tsx b/src/utils/exportUtils/HtmlExport.tsx index dcc7994ace..e732ec0efa 100644 --- a/src/utils/exportUtils/HtmlExport.tsx +++ b/src/utils/exportUtils/HtmlExport.tsx @@ -106,31 +106,27 @@ export default class HTMLExporter extends Exporter { const exportedText = renderToStaticMarkup(

- { _t( + {_t( "This is the start of export of . Exported by at %(exportDate)s.", { exportDate, }, { - roomName: () => { this.room.name }, + roomName: () => {this.room.name}, exporterDetails: () => ( - - { exporterName ? ( + + {exporterName ? ( <> - { exporterName } - { " (" + exporter + ")" } + {exporterName} + {" (" + exporter + ")"} ) : ( - { exporter } - ) } + {exporter} + )} ), }, - ) } + )}

, ); @@ -224,12 +220,7 @@ export default class HTMLExporter extends Exporter { protected getAvatarURL(event: MatrixEvent): string { const member = event.sender; return ( - member.getMxcAvatarUrl() && - mediaFromMxc(member.getMxcAvatarUrl()).getThumbnailOfSourceHttp( - 30, - 30, - "crop", - ) + member.getMxcAvatarUrl() && mediaFromMxc(member.getMxcAvatarUrl()).getThumbnailOfSourceHttp(30, 30, "crop") ); } @@ -241,7 +232,7 @@ export default class HTMLExporter extends Exporter { this.avatars.set(member.userId, true); const image = await fetch(avatarUrl); const blob = await image.blob(); - this.addFile(`users/${member.userId.replace(/:/g, '-')}.png`, blob); + this.addFile(`users/${member.userId.replace(/:/g, "-")}.png`, blob); } catch (err) { logger.log("Failed to fetch user's avatar" + err); } @@ -264,32 +255,34 @@ export default class HTMLExporter extends Exporter { } public getEventTile(mxEv: MatrixEvent, continuation: boolean) { - return
- - false} - isTwelveHour={false} - last={false} - lastInSection={false} - permalinkCreator={this.permalinkCreator} - lastSuccessful={false} - isSelectedEvent={false} - getRelationsForEvent={null} - showReactions={false} - layout={Layout.Group} - showReadReceipts={false} - /> - -
; + return ( +
+ + false} + isTwelveHour={false} + last={false} + lastInSection={false} + permalinkCreator={this.permalinkCreator} + lastSuccessful={false} + isSelectedEvent={false} + getRelationsForEvent={null} + showReactions={false} + layout={Layout.Group} + showReadReceipts={false} + /> + +
+ ); } protected async getEventTileMarkup(mxEv: MatrixEvent, continuation: boolean, filePath?: string) { @@ -305,11 +298,8 @@ export default class HTMLExporter extends Exporter { ) { // to linkify textual events, we'll need lifecycle methods which won't be invoked in renderToString // So, we'll have to render the component into a temporary root element - const tempRoot = document.createElement('div'); - ReactDOM.render( - EventTile, - tempRoot, - ); + const tempRoot = document.createElement("div"); + ReactDOM.render(EventTile, tempRoot); eventTileMarkup = tempRoot.innerHTML; } else { eventTileMarkup = renderToStaticMarkup(EventTile); @@ -319,17 +309,17 @@ export default class HTMLExporter extends Exporter { const mxc = mxEv.getContent().url ?? mxEv.getContent().file?.url; eventTileMarkup = eventTileMarkup.split(mxc).join(filePath); } - eventTileMarkup = eventTileMarkup.replace(/.*?<\/span>/, ''); + eventTileMarkup = eventTileMarkup.replace(/.*?<\/span>/, ""); if (hasAvatar) { eventTileMarkup = eventTileMarkup.replace( - encodeURI(this.getAvatarURL(mxEv)).replace(/&/g, '&'), + encodeURI(this.getAvatarURL(mxEv)).replace(/&/g, "&"), `users/${mxEv.sender.userId.replace(/:/g, "-")}.png`, ); } return eventTileMarkup; } - protected createModifiedEvent(text: string, mxEv: MatrixEvent, italic=true) { + protected createModifiedEvent(text: string, mxEv: MatrixEvent, italic = true) { const modifiedContent = { msgtype: "m.text", body: `${text}`, @@ -337,8 +327,8 @@ export default class HTMLExporter extends Exporter { formatted_body: `${text}`, }; if (italic) { - modifiedContent.formatted_body = '' + modifiedContent.formatted_body + ''; - modifiedContent.body = '*' + modifiedContent.body + '*'; + modifiedContent.formatted_body = "" + modifiedContent.formatted_body + ""; + modifiedContent.body = "*" + modifiedContent.body + "*"; } const modifiedEvent = new MatrixEvent(); modifiedEvent.event = mxEv.event; @@ -402,15 +392,20 @@ export default class HTMLExporter extends Exporter { let prevEvent = null; for (let i = start; i < Math.min(start + 1000, events.length); i++) { const event = events[i]; - this.updateProgress(_t("Processing event %(number)s out of %(total)s", { - number: i + 1, - total: events.length, - }), false, true); + this.updateProgress( + _t("Processing event %(number)s out of %(total)s", { + number: i + 1, + total: events.length, + }), + false, + true, + ); if (this.cancelled) return this.cleanUp(); if (!haveRendererForEvent(event, false)) continue; content += this.needsDateSeparator(event, prevEvent) ? this.getDateSeparator(event) : ""; - const shouldBeJoined = !this.needsDateSeparator(event, prevEvent) && + const shouldBeJoined = + !this.needsDateSeparator(event, prevEvent) && shouldFormContinuation(prevEvent, event, false, this.threadsEnabled); const body = await this.createMessageBody(event, shouldBeJoined); this.totalSize += Buffer.byteLength(body); @@ -427,10 +422,14 @@ export default class HTMLExporter extends Exporter { const res = await this.getRequiredEvents(); const fetchEnd = performance.now(); - this.updateProgress(_t("Fetched %(count)s events in %(seconds)ss", { - count: res.length, - seconds: (fetchEnd - fetchStart) / 1000, - }), true, false); + this.updateProgress( + _t("Fetched %(count)s events in %(seconds)ss", { + count: res.length, + seconds: (fetchEnd - fetchStart) / 1000, + }), + true, + false, + ); this.updateProgress(_t("Creating HTML...")); @@ -438,8 +437,8 @@ export default class HTMLExporter extends Exporter { for (let page = 0; page < res.length / 1000; page++) { const html = await this.createHTML(res, page * 1000); const document = new DOMParser().parseFromString(html, "text/html"); - document.querySelectorAll("*").forEach(element => { - element.classList.forEach(c => usedClasses.add(c)); + document.querySelectorAll("*").forEach((element) => { + element.classList.forEach((c) => usedClasses.add(c)); }); this.addFile(`messages${page ? page + 1 : ""}.html`, new Blob([html])); } @@ -456,10 +455,12 @@ export default class HTMLExporter extends Exporter { logger.info("Export cancelled successfully"); } else { this.updateProgress(_t("Export successful!")); - this.updateProgress(_t("Exported %(count)s events in %(seconds)s seconds", { - count: res.length, - seconds: (exportEnd - fetchStart) / 1000, - })); + this.updateProgress( + _t("Exported %(count)s events in %(seconds)s seconds", { + count: res.length, + seconds: (exportEnd - fetchStart) / 1000, + }), + ); } this.cleanUp(); diff --git a/src/utils/exportUtils/JSONExport.ts b/src/utils/exportUtils/JSONExport.ts index a0dc5e036e..a050e32ef1 100644 --- a/src/utils/exportUtils/JSONExport.ts +++ b/src/utils/exportUtils/JSONExport.ts @@ -84,10 +84,14 @@ export default class JSONExporter extends Exporter { protected async createOutput(events: MatrixEvent[]) { for (let i = 0; i < events.length; i++) { const event = events[i]; - this.updateProgress(_t("Processing event %(number)s out of %(total)s", { - number: i + 1, - total: events.length, - }), false, true); + this.updateProgress( + _t("Processing event %(number)s out of %(total)s", { + number: i + 1, + total: events.length, + }), + false, + true, + ); if (this.cancelled) return this.cleanUp(); if (!haveRendererForEvent(event, false)) continue; this.messages.push(await this.getJSONString(event)); @@ -103,7 +107,7 @@ export default class JSONExporter extends Exporter { const res = await this.getRequiredEvents(); const fetchEnd = performance.now(); - logger.log(`Fetched ${res.length} events in ${(fetchEnd - fetchStart)/1000}s`); + logger.log(`Fetched ${res.length} events in ${(fetchEnd - fetchStart) / 1000}s`); logger.info("Creating output..."); const text = await this.createOutput(res); @@ -122,10 +126,9 @@ export default class JSONExporter extends Exporter { logger.info("Export cancelled successfully"); } else { logger.info("Export successful!"); - logger.log(`Exported ${res.length} events in ${(exportEnd - fetchStart)/1000} seconds`); + logger.log(`Exported ${res.length} events in ${(exportEnd - fetchStart) / 1000} seconds`); } this.cleanUp(); } } - diff --git a/src/utils/exportUtils/PlainTextExport.ts b/src/utils/exportUtils/PlainTextExport.ts index 3150b15c64..d097d842a5 100644 --- a/src/utils/exportUtils/PlainTextExport.ts +++ b/src/utils/exportUtils/PlainTextExport.ts @@ -61,7 +61,7 @@ export default class PlainTextExporter extends Exporter { rplSource = match[2].substring(1); // Get the first non-blank line from the source. - const lines = rplSource.split('\n').filter((line) => !/^\s*$/.test(line)); + const lines = rplSource.split("\n").filter((line) => !/^\s*$/.test(line)); if (lines.length > 0) { // Cut to a maximum length. rplSource = lines[0].substring(0, REPLY_SOURCE_MAX_LENGTH); @@ -111,10 +111,14 @@ export default class PlainTextExporter extends Exporter { let content = ""; for (let i = 0; i < events.length; i++) { const event = events[i]; - this.updateProgress(_t("Processing event %(number)s out of %(total)s", { - number: i + 1, - total: events.length, - }), false, true); + this.updateProgress( + _t("Processing event %(number)s out of %(total)s", { + number: i + 1, + total: events.length, + }), + false, + true, + ); if (this.cancelled) return this.cleanUp(); if (!haveRendererForEvent(event, false)) continue; const textForEvent = await this.plainTextForEvent(event); @@ -131,7 +135,7 @@ export default class PlainTextExporter extends Exporter { const res = await this.getRequiredEvents(); const fetchEnd = performance.now(); - logger.log(`Fetched ${res.length} events in ${(fetchEnd - fetchStart)/1000}s`); + logger.log(`Fetched ${res.length} events in ${(fetchEnd - fetchStart) / 1000}s`); this.updateProgress(_t("Creating output...")); const text = await this.createOutput(res); @@ -150,10 +154,9 @@ export default class PlainTextExporter extends Exporter { logger.info("Export cancelled successfully"); } else { logger.info("Export successful!"); - logger.log(`Exported ${res.length} events in ${(exportEnd - fetchStart)/1000} seconds`); + logger.log(`Exported ${res.length} events in ${(exportEnd - fetchStart) / 1000} seconds`); } this.cleanUp(); } } - diff --git a/src/utils/exportUtils/exportCSS.ts b/src/utils/exportUtils/exportCSS.ts index b85c2a9431..f92e339b02 100644 --- a/src/utils/exportUtils/exportCSS.ts +++ b/src/utils/exportUtils/exportCSS.ts @@ -52,7 +52,7 @@ async function getRulesFromCssFile(path: string): Promise { // doesn't cull rules which won't apply due to the full selector not matching but gets rid of a LOT of cruft anyway. const getExportCSS = async (usedClasses: Set): Promise => { // only include bundle.css and the data-mx-theme=light styling - const stylesheets = Array.from(document.styleSheets).filter(s => { + const stylesheets = Array.from(document.styleSheets).filter((s) => { return s.href?.endsWith("bundle.css") || isLightTheme(s); }); @@ -70,12 +70,14 @@ const getExportCSS = async (usedClasses: Set): Promise => { const selectorText = (rule as CSSStyleRule).selectorText; // only skip the rule if all branches (,) of the selector are redundant - if (selectorText?.split(",").every(selector => { - const classes = selector.match(cssSelectorTextClassesRegex); - if (classes && !classes.every(c => usedClasses.has(c.substring(1)))) { - return true; // signal as a redundant selector - } - })) { + if ( + selectorText?.split(",").every((selector) => { + const classes = selector.match(cssSelectorTextClassesRegex); + if (classes && !classes.every((c) => usedClasses.has(c.substring(1)))) { + return true; // signal as a redundant selector + } + }) + ) { continue; // skip this rule as it is redundant } diff --git a/src/utils/exportUtils/exportCustomCSS.css b/src/utils/exportUtils/exportCustomCSS.css index a62f890649..47dd4e5ec5 100644 --- a/src/utils/exportUtils/exportCustomCSS.css +++ b/src/utils/exportUtils/exportCustomCSS.css @@ -32,9 +32,8 @@ limitations under the License. bottom: 30px; font-size: 17px; padding: 6px 16px; - font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir, - segoe ui, helvetica neue, helvetica, Ubuntu, roboto, noto, arial, - sans-serif; + font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Ubuntu, + roboto, noto, arial, sans-serif; font-weight: 400; line-height: 1.43; border-radius: 4px; @@ -126,7 +125,6 @@ a.mx_reply_anchor:hover { .mx_RedactedBody, .mx_HiddenBody { - padding-left: unset; } diff --git a/src/utils/exportUtils/exportJS.js b/src/utils/exportUtils/exportJS.js index 4b2e29005d..f4d5df322b 100644 --- a/src/utils/exportUtils/exportJS.js +++ b/src/utils/exportUtils/exportJS.js @@ -33,10 +33,9 @@ function showToast(text) { } window.onload = () => { - document.querySelectorAll('.mx_reply_anchor').forEach(element => { - element.addEventListener('click', event => { + document.querySelectorAll(".mx_reply_anchor").forEach((element) => { + element.addEventListener("click", (event) => { showToastIfNeeded(event.target.dataset.scrollTo); }); }); }; - diff --git a/src/utils/humanize.ts b/src/utils/humanize.ts index 47e2d83e8a..9ae5b17589 100644 --- a/src/utils/humanize.ts +++ b/src/utils/humanize.ts @@ -36,7 +36,8 @@ export function humanizeTime(timeMillis: number): string { const hours = Math.ceil(minutes / 60); const days = Math.ceil(hours / 24); - if (msAgo >= 0) { // Past + if (msAgo >= 0) { + // Past if (msAgo <= MILLISECONDS_RECENT) return _t("a few seconds ago"); if (msAgo <= MILLISECONDS_1_MIN) return _t("about a minute ago"); if (minutes <= MINUTES_UNDER_1_HOUR) return _t("%(num)s minutes ago", { num: minutes }); @@ -44,7 +45,8 @@ export function humanizeTime(timeMillis: number): string { if (hours <= HOURS_UNDER_1_DAY) return _t("%(num)s hours ago", { num: hours }); if (hours <= HOURS_1_DAY) return _t("about a day ago"); return _t("%(num)s days ago", { num: days }); - } else { // Future + } else { + // Future msAgo = Math.abs(msAgo); if (msAgo <= MILLISECONDS_RECENT) return _t("a few seconds from now"); if (msAgo <= MILLISECONDS_1_MIN) return _t("about a minute from now"); diff --git a/src/utils/image-media.ts b/src/utils/image-media.ts index 58558f7a25..02de079928 100644 --- a/src/utils/image-media.ts +++ b/src/utils/image-media.ts @@ -95,7 +95,7 @@ export async function createThumbnail( if (window.OffscreenCanvas && canvas instanceof OffscreenCanvas) { thumbnailPromise = canvas.convertToBlob({ type: mimeType }); } else { - thumbnailPromise = new Promise(resolve => (canvas as HTMLCanvasElement).toBlob(resolve, mimeType)); + thumbnailPromise = new Promise((resolve) => (canvas as HTMLCanvasElement).toBlob(resolve, mimeType)); } const imageData = context.getImageData(0, 0, targetWidth, targetHeight); diff --git a/src/utils/iterables.ts b/src/utils/iterables.ts index 5fb8967a34..9ee29448d8 100644 --- a/src/utils/iterables.ts +++ b/src/utils/iterables.ts @@ -20,6 +20,6 @@ export function iterableIntersection(a: Iterable, b: Iterable): Iterabl return arrayIntersection(Array.from(a), Array.from(b)); } -export function iterableDiff(a: Iterable, b: Iterable): { added: Iterable, removed: Iterable } { +export function iterableDiff(a: Iterable, b: Iterable): { added: Iterable; removed: Iterable } { return arrayDiff(Array.from(a), Array.from(b)); } diff --git a/src/utils/leave-behaviour.ts b/src/utils/leave-behaviour.ts index f330e9b873..8b1a900f8c 100644 --- a/src/utils/leave-behaviour.ts +++ b/src/utils/leave-behaviour.ts @@ -39,7 +39,7 @@ import { SdkContextClass } from "../contexts/SDKContext"; export async function leaveRoomBehaviour(roomId: string, retry = true, spinner = true) { let spinnerModal: IHandle; if (spinner) { - spinnerModal = Modal.createDialog(Spinner, null, 'mx_Dialog_spinner'); + spinnerModal = Modal.createDialog(Spinner, null, "mx_Dialog_spinner"); } const cli = MatrixClientPeg.get(); @@ -56,25 +56,33 @@ export async function leaveRoomBehaviour(roomId: string, retry = true, spinner = const room = cli.getRoom(roomId); // await any queued messages being sent so that they do not fail - await Promise.all(room.getPendingEvents().filter(ev => { - return [EventStatus.QUEUED, EventStatus.ENCRYPTING, EventStatus.SENDING].includes(ev.status); - }).map(ev => new Promise((resolve, reject) => { - const handler = () => { - if (ev.status === EventStatus.NOT_SENT) { - spinnerModal?.close(); - reject(ev.error); - } + await Promise.all( + room + .getPendingEvents() + .filter((ev) => { + return [EventStatus.QUEUED, EventStatus.ENCRYPTING, EventStatus.SENDING].includes(ev.status); + }) + .map( + (ev) => + new Promise((resolve, reject) => { + const handler = () => { + if (ev.status === EventStatus.NOT_SENT) { + spinnerModal?.close(); + reject(ev.error); + } - if (!ev.status || ev.status === EventStatus.SENT) { - ev.off(MatrixEventEvent.Status, handler); - resolve(); - } - }; + if (!ev.status || ev.status === EventStatus.SENT) { + ev.off(MatrixEventEvent.Status, handler); + resolve(); + } + }; - ev.on(MatrixEventEvent.Status, handler); - }))); + ev.on(MatrixEventEvent.Status, handler); + }), + ), + ); - let results: { [roomId: string]: Error & { errcode?: string, message: string, data?: Record } } = {}; + let results: { [roomId: string]: Error & { errcode?: string; message: string; data?: Record } } = {}; if (!leavingAllVersions) { try { await cli.leave(roomId); @@ -91,7 +99,7 @@ export async function leaveRoomBehaviour(roomId: string, retry = true, spinner = } if (retry) { - const limitExceededError = Object.values(results).find(e => e?.errcode === "M_LIMIT_EXCEEDED"); + const limitExceededError = Object.values(results).find((e) => e?.errcode === "M_LIMIT_EXCEEDED"); if (limitExceededError) { await sleep(limitExceededError.data.retry_after_ms ?? 100); return leaveRoomBehaviour(roomId, false, false); @@ -100,26 +108,26 @@ export async function leaveRoomBehaviour(roomId: string, retry = true, spinner = spinnerModal?.close(); - const errors = Object.entries(results).filter(r => !!r[1]); + const errors = Object.entries(results).filter((r) => !!r[1]); if (errors.length > 0) { const messages = []; for (const roomErr of errors) { const err = roomErr[1]; // [0] is the roomId let message = _t("Unexpected server error trying to leave the room"); if (err.errcode && err.message) { - if (err.errcode === 'M_CANNOT_LEAVE_SERVER_NOTICE_ROOM') { + if (err.errcode === "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM") { Modal.createDialog(ErrorDialog, { title: _t("Can't leave Server Notices room"), description: _t( "This room is used for important messages from the Homeserver, " + - "so you cannot leave it.", + "so you cannot leave it.", ), }); return; } message = results[roomId].message; } - messages.push(message, React.createElement('BR')); // createElement to avoid using a tsx file in utils + messages.push(message, React.createElement("BR")); // createElement to avoid using a tsx file in utils } Modal.createDialog(ErrorDialog, { title: _t("Error leaving room"), @@ -158,16 +166,20 @@ export async function leaveRoomBehaviour(roomId: string, retry = true, spinner = } export const leaveSpace = (space: Room) => { - Modal.createDialog(LeaveSpaceDialog, { - space, - onFinished: async (leave: boolean, rooms: Room[]) => { - if (!leave) return; - await bulkSpaceBehaviour(space, rooms, room => leaveRoomBehaviour(room.roomId)); + Modal.createDialog( + LeaveSpaceDialog, + { + space, + onFinished: async (leave: boolean, rooms: Room[]) => { + if (!leave) return; + await bulkSpaceBehaviour(space, rooms, (room) => leaveRoomBehaviour(room.roomId)); - dis.dispatch({ - action: Action.AfterLeaveRoom, - room_id: space.roomId, - }); + dis.dispatch({ + action: Action.AfterLeaveRoom, + room_id: space.roomId, + }); + }, }, - }, "mx_LeaveSpaceDialog_wrapper"); + "mx_LeaveSpaceDialog_wrapper", + ); }; diff --git a/src/utils/localRoom/isLocalRoom.ts b/src/utils/localRoom/isLocalRoom.ts index a31774ea5e..f2d7e3acfd 100644 --- a/src/utils/localRoom/isLocalRoom.ts +++ b/src/utils/localRoom/isLocalRoom.ts @@ -18,7 +18,7 @@ import { Room } from "matrix-js-sdk/src/matrix"; import { LocalRoom, LOCAL_ROOM_ID_PREFIX } from "../../models/LocalRoom"; -export function isLocalRoom(roomOrID: Room|string): boolean { +export function isLocalRoom(roomOrID: Room | string): boolean { if (typeof roomOrID === "string") { return roomOrID.startsWith(LOCAL_ROOM_ID_PREFIX); } diff --git a/src/utils/localRoom/isRoomReady.ts b/src/utils/localRoom/isRoomReady.ts index c26839236d..32d7106b87 100644 --- a/src/utils/localRoom/isRoomReady.ts +++ b/src/utils/localRoom/isRoomReady.ts @@ -21,10 +21,7 @@ import { LocalRoom } from "../../models/LocalRoom"; /** * Tests whether a room created based on a local room is ready. */ -export function isRoomReady( - client: MatrixClient, - localRoom: LocalRoom, -): boolean { +export function isRoomReady(client: MatrixClient, localRoom: LocalRoom): boolean { // not ready if no actual room id exists if (!localRoom.actualRoomId) return false; diff --git a/src/utils/location/LocationShareErrors.ts b/src/utils/location/LocationShareErrors.ts index 81d4e50d31..a7f34b4221 100644 --- a/src/utils/location/LocationShareErrors.ts +++ b/src/utils/location/LocationShareErrors.ts @@ -17,18 +17,20 @@ limitations under the License. import { _t } from "../../languageHandler"; export enum LocationShareError { - MapStyleUrlNotConfigured = 'MapStyleUrlNotConfigured', - MapStyleUrlNotReachable = 'MapStyleUrlNotReachable', - Default = 'Default' + MapStyleUrlNotConfigured = "MapStyleUrlNotConfigured", + MapStyleUrlNotReachable = "MapStyleUrlNotReachable", + Default = "Default", } export const getLocationShareErrorMessage = (errorType?: LocationShareError): string => { switch (errorType) { case LocationShareError.MapStyleUrlNotConfigured: - return _t('This homeserver is not configured to display maps.'); + return _t("This homeserver is not configured to display maps."); case LocationShareError.MapStyleUrlNotReachable: default: - return _t(`This homeserver is not configured correctly to display maps, ` - + `or the configured map server may be unreachable.`); + return _t( + `This homeserver is not configured correctly to display maps, ` + + `or the configured map server may be unreachable.`, + ); } }; diff --git a/src/utils/location/findMapStyleUrl.ts b/src/utils/location/findMapStyleUrl.ts index 9eb9a6d307..0653d65cf2 100644 --- a/src/utils/location/findMapStyleUrl.ts +++ b/src/utils/location/findMapStyleUrl.ts @@ -26,14 +26,12 @@ import { LocationShareError } from "./LocationShareErrors"; * that, defaults to the same tile server listed by matrix.org. */ export function findMapStyleUrl(): string { - const mapStyleUrl = ( - getTileServerWellKnown()?.map_style_url ?? - SdkConfig.get().map_style_url - ); + const mapStyleUrl = getTileServerWellKnown()?.map_style_url ?? SdkConfig.get().map_style_url; if (!mapStyleUrl) { - logger.error("'map_style_url' missing from homeserver .well-known area, and " + - "missing from from config.json."); + logger.error( + "'map_style_url' missing from homeserver .well-known area, and " + "missing from from config.json.", + ); throw new Error(LocationShareError.MapStyleUrlNotConfigured); } diff --git a/src/utils/location/index.ts b/src/utils/location/index.ts index a6aeaa65d6..f94c6a12dd 100644 --- a/src/utils/location/index.ts +++ b/src/utils/location/index.ts @@ -14,9 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -export * from './findMapStyleUrl'; -export * from './isSelfLocation'; -export * from './locationEventGeoUri'; -export * from './LocationShareErrors'; -export * from './map'; -export * from './parseGeoUri'; +export * from "./findMapStyleUrl"; +export * from "./isSelfLocation"; +export * from "./locationEventGeoUri"; +export * from "./LocationShareErrors"; +export * from "./map"; +export * from "./parseGeoUri"; diff --git a/src/utils/location/locationEventGeoUri.ts b/src/utils/location/locationEventGeoUri.ts index eb81ac87c0..2009edc325 100644 --- a/src/utils/location/locationEventGeoUri.ts +++ b/src/utils/location/locationEventGeoUri.ts @@ -27,5 +27,5 @@ export const locationEventGeoUri = (mxEvent: MatrixEvent): string => { // https://github.com/matrix-org/matrix-doc/issues/3516 const content = mxEvent.getContent(); const loc = M_LOCATION.findIn(content) as { uri?: string }; - return loc ? loc.uri : content['geo_uri']; + return loc ? loc.uri : content["geo_uri"]; }; diff --git a/src/utils/location/map.ts b/src/utils/location/map.ts index 7dc8522271..861515eb77 100644 --- a/src/utils/location/map.ts +++ b/src/utils/location/map.ts @@ -24,11 +24,7 @@ import { parseGeoUri } from "./parseGeoUri"; import { findMapStyleUrl } from "./findMapStyleUrl"; import { LocationShareError } from "./LocationShareErrors"; -export const createMap = ( - interactive: boolean, - bodyId: string, - onError: (error: Error) => void, -): maplibregl.Map => { +export const createMap = (interactive: boolean, bodyId: string, onError: (error: Error) => void): maplibregl.Map => { try { const styleUrl = findMapStyleUrl(); @@ -39,24 +35,23 @@ export const createMap = ( interactive, attributionControl: false, locale: { - 'AttributionControl.ToggleAttribution': _t('Toggle attribution'), - 'AttributionControl.MapFeedback': _t('Map feedback'), - 'FullscreenControl.Enter': _t('Enter fullscreen'), - 'FullscreenControl.Exit': _t('Exit fullscreen'), - 'GeolocateControl.FindMyLocation': _t('Find my location'), - 'GeolocateControl.LocationNotAvailable': _t('Location not available'), - 'LogoControl.Title': _t('Mapbox logo'), - 'NavigationControl.ResetBearing': _t('Reset bearing to north'), - 'NavigationControl.ZoomIn': _t('Zoom in'), - 'NavigationControl.ZoomOut': _t('Zoom out'), + "AttributionControl.ToggleAttribution": _t("Toggle attribution"), + "AttributionControl.MapFeedback": _t("Map feedback"), + "FullscreenControl.Enter": _t("Enter fullscreen"), + "FullscreenControl.Exit": _t("Exit fullscreen"), + "GeolocateControl.FindMyLocation": _t("Find my location"), + "GeolocateControl.LocationNotAvailable": _t("Location not available"), + "LogoControl.Title": _t("Mapbox logo"), + "NavigationControl.ResetBearing": _t("Reset bearing to north"), + "NavigationControl.ZoomIn": _t("Zoom in"), + "NavigationControl.ZoomOut": _t("Zoom out"), }, }); - map.addControl(new maplibregl.AttributionControl(), 'top-right'); + map.addControl(new maplibregl.AttributionControl(), "top-right"); - map.on('error', (e) => { + map.on("error", (e) => { logger.error( - "Failed to load map: check map_style_url in config.json has a " - + "valid URL and API key", + "Failed to load map: check map_style_url in config.json has a " + "valid URL and API key", e.error, ); onError(new Error(LocationShareError.MapStyleUrlNotReachable)); @@ -72,7 +67,7 @@ export const createMap = ( export const createMarker = (coords: GeolocationCoordinates, element: HTMLElement): maplibregl.Marker => { const marker = new maplibregl.Marker({ element, - anchor: 'bottom', + anchor: "bottom", offset: [0, -1], }).setLngLat({ lon: coords.longitude, lat: coords.latitude }); return marker; diff --git a/src/utils/location/parseGeoUri.ts b/src/utils/location/parseGeoUri.ts index 4c7291cd3e..080ff5359b 100644 --- a/src/utils/location/parseGeoUri.ts +++ b/src/utils/location/parseGeoUri.ts @@ -26,8 +26,8 @@ export const parseGeoUri = (uri: string): GeolocationCoordinates => { const m = uri.match(/^\s*geo:(.*?)\s*$/); if (!m) return; - const parts = m[1].split(';'); - const coords = parts[0].split(','); + const parts = m[1].split(";"); + const coords = parts[0].split(","); let uncertainty: number; for (const param of parts.slice(1)) { const m = param.match(/u=(.*)/); diff --git a/src/utils/location/useMap.ts b/src/utils/location/useMap.ts index 55770cc5e2..c4637a9a36 100644 --- a/src/utils/location/useMap.ts +++ b/src/utils/location/useMap.ts @@ -14,8 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { useEffect, useState } from 'react'; -import { Map as MapLibreMap } from 'maplibre-gl'; +import { useEffect, useState } from "react"; +import { Map as MapLibreMap } from "maplibre-gl"; import { createMap } from "./map"; @@ -31,11 +31,7 @@ interface UseMapProps { * Make sure `onError` has a stable reference * As map is recreated on changes to it */ -export const useMap = ({ - interactive, - bodyId, - onError, -}: UseMapProps): MapLibreMap | undefined => { +export const useMap = ({ interactive, bodyId, onError }: UseMapProps): MapLibreMap | undefined => { const [map, setMap] = useState(); useEffect( @@ -59,4 +55,3 @@ export const useMap = ({ return map; }; - diff --git a/src/utils/maps.ts b/src/utils/maps.ts index 2afbc16bc5..2a82b0a238 100644 --- a/src/utils/maps.ts +++ b/src/utils/maps.ts @@ -23,12 +23,12 @@ import { arrayDiff, arrayIntersection } from "./arrays"; * @param b The second Map. Must be defined. * @returns The difference between the keys of each Map. */ -export function mapDiff(a: Map, b: Map): { changed: K[], added: K[], removed: K[] } { +export function mapDiff(a: Map, b: Map): { changed: K[]; added: K[]; removed: K[] } { const aKeys = [...a.keys()]; const bKeys = [...b.keys()]; const keyDiff = arrayDiff(aKeys, bKeys); const possibleChanges = arrayIntersection(aKeys, bKeys); - const changes = possibleChanges.filter(k => a.get(k) !== b.get(k)); + const changes = possibleChanges.filter((k) => a.get(k) !== b.get(k)); return { changed: changes, added: keyDiff.added, removed: keyDiff.removed }; } diff --git a/src/utils/media/requestMediaPermissions.tsx b/src/utils/media/requestMediaPermissions.tsx index 7740fb8da4..c7720fffeb 100644 --- a/src/utils/media/requestMediaPermissions.tsx +++ b/src/utils/media/requestMediaPermissions.tsx @@ -47,11 +47,8 @@ export const requestMediaPermissions = async (video = true): Promise { - handler = function(_, __, member: RoomMember) { // eslint-disable-line @typescript-eslint/naming-convention + // eslint-disable-next-line @typescript-eslint/naming-convention + handler = function (_, __, member: RoomMember) { if (member.userId !== userId) return; if (member.roomId !== roomId) return; resolve(true); diff --git a/src/utils/notifications.ts b/src/utils/notifications.ts index 2b08f406dc..8929240e6f 100644 --- a/src/utils/notifications.ts +++ b/src/utils/notifications.ts @@ -45,7 +45,7 @@ export async function createLocalNotificationSettingsIfNeeded(cli: MatrixClient) if (!event) { // If any of the above is true, we fall in the "backwards compat" case, // and `is_silenced` will be set to `false` - const isSilenced = !deviceNotificationSettingsKeys.some(key => SettingsStore.getValue(key)); + const isSilenced = !deviceNotificationSettingsKeys.some((key) => SettingsStore.getValue(key)); await cli.setAccountData(eventType, { is_silenced: isSilenced, @@ -68,9 +68,10 @@ export function clearAllNotifications(client: MatrixClient): Promise> const lastRoomEvent = roomEvents?.[roomEvents?.length - 1]; const lastThreadLastEvent = lastThreadEvents?.[lastThreadEvents?.length - 1]; - const lastEvent = (lastRoomEvent?.getTs() ?? 0) > (lastThreadLastEvent?.getTs() ?? 0) - ? lastRoomEvent - : lastThreadLastEvent; + const lastEvent = + (lastRoomEvent?.getTs() ?? 0) > (lastThreadLastEvent?.getTs() ?? 0) + ? lastRoomEvent + : lastThreadLastEvent; if (lastEvent) { const receiptType = SettingsStore.getValue("sendReadReceipts", room.roomId) diff --git a/src/utils/numbers.ts b/src/utils/numbers.ts index 6ba19d0bef..180e4f7950 100644 --- a/src/utils/numbers.ts +++ b/src/utils/numbers.ts @@ -34,7 +34,7 @@ export function sum(...i: number[]): number { } export function percentageWithin(pct: number, min: number, max: number): number { - return (pct * (max - min)) + min; + return pct * (max - min) + min; } export function percentageOf(val: number, min: number, max: number): number { diff --git a/src/utils/objects.ts b/src/utils/objects.ts index 87fb4dd8e6..f3bc8e93f1 100644 --- a/src/utils/objects.ts +++ b/src/utils/objects.ts @@ -16,7 +16,7 @@ limitations under the License. import { arrayDiff, arrayUnion, arrayIntersection } from "./arrays"; -type ObjectExcluding = {[k in Exclude]: O[k]}; +type ObjectExcluding = { [k in Exclude]: O[k] }; /** * Gets a new object which represents the provided object, excluding some properties. @@ -45,13 +45,13 @@ export function objectExcluding>(a: O, pr * @param props The property names to keep. * @returns The new object with only the provided properties. */ -export function objectWithOnly>(a: O, props: P): {[k in P[number]]: O[k]} { +export function objectWithOnly>(a: O, props: P): { [k in P[number]]: O[k] } { const existingProps = Object.keys(a) as (keyof O)[]; const diff = arrayDiff(existingProps, props); if (diff.removed.length === 0) { return objectShallowClone(a); } else { - return objectExcluding(a, diff.removed) as {[k in P[number]]: O[k]}; + return objectExcluding(a, diff.removed) as { [k in P[number]]: O[k] }; } } @@ -94,10 +94,10 @@ export function objectHasDiff(a: O, b: O): boolean { // if the amalgamation of both sets of keys has the a different length to the inputs then there must be a change if (possibleChanges.length !== aKeys.length) return true; - return possibleChanges.some(k => a[k] !== b[k]); + return possibleChanges.some((k) => a[k] !== b[k]); } -type Diff = { changed: K[], added: K[], removed: K[] }; +type Diff = { changed: K[]; added: K[]; removed: K[] }; /** * Determines the keys added, changed, and removed between two objects. @@ -112,7 +112,7 @@ export function objectDiff(a: O, b: O): Diff { const bKeys = Object.keys(b) as (keyof O)[]; const keyDiff = arrayDiff(aKeys, bKeys); const possibleChanges = arrayIntersection(aKeys, bKeys); - const changes = possibleChanges.filter(k => a[k] !== b[k]); + const changes = possibleChanges.filter((k) => a[k] !== b[k]); return { changed: changes, added: keyDiff.added, removed: keyDiff.removed }; } diff --git a/src/utils/pages.ts b/src/utils/pages.ts index 75e4fef9bf..3b51d79691 100644 --- a/src/utils/pages.ts +++ b/src/utils/pages.ts @@ -17,14 +17,14 @@ limitations under the License. import { logger } from "matrix-js-sdk/src/logger"; import { IConfigOptions } from "../IConfigOptions"; -import { getEmbeddedPagesWellKnown } from '../utils/WellKnownUtils'; +import { getEmbeddedPagesWellKnown } from "../utils/WellKnownUtils"; import { SnakedObject } from "./SnakedObject"; export function getHomePageUrl(appConfig: IConfigOptions): string | null { const config = new SnakedObject(appConfig); const pagesConfig = config.get("embedded_pages"); - let pageUrl = pagesConfig ? (new SnakedObject(pagesConfig).get("home_url")) : null; + let pageUrl = pagesConfig ? new SnakedObject(pagesConfig).get("home_url") : null; if (!pageUrl) { // This is a deprecated config option for the home page @@ -34,7 +34,7 @@ export function getHomePageUrl(appConfig: IConfigOptions): string | null { if (pageUrl) { logger.warn( "You are using a deprecated config option: `welcomePageUrl`. Please use " + - "`embedded_pages.home_url` instead, per https://github.com/vector-im/element-web/issues/21428", + "`embedded_pages.home_url` instead, per https://github.com/vector-im/element-web/issues/21428", ); } } @@ -49,7 +49,5 @@ export function getHomePageUrl(appConfig: IConfigOptions): string | null { export function shouldUseLoginForWelcome(appConfig: IConfigOptions): boolean { const config = new SnakedObject(appConfig); const pagesConfig = config.get("embedded_pages"); - return pagesConfig - ? ((new SnakedObject(pagesConfig).get("login_for_welcome")) === true) - : false; + return pagesConfig ? new SnakedObject(pagesConfig).get("login_for_welcome") === true : false; } diff --git a/src/utils/permalinks/ElementPermalinkConstructor.ts b/src/utils/permalinks/ElementPermalinkConstructor.ts index d66c3ae031..79ec0ed566 100644 --- a/src/utils/permalinks/ElementPermalinkConstructor.ts +++ b/src/utils/permalinks/ElementPermalinkConstructor.ts @@ -44,9 +44,9 @@ export default class ElementPermalinkConstructor extends PermalinkConstructor { } forEntity(entityId: string): string { - if (entityId[0] === '!' || entityId[0] === '#') { + if (entityId[0] === "!" || entityId[0] === "#") { return this.forRoom(entityId); - } else if (entityId[0] === '@') { + } else if (entityId[0] === "@") { return this.forUser(entityId); } else throw new Error("Unrecognized entity"); } @@ -57,8 +57,8 @@ export default class ElementPermalinkConstructor extends PermalinkConstructor { } encodeServerCandidates(candidates?: string[]) { - if (!candidates || candidates.length === 0) return ''; - return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`; + if (!candidates || candidates.length === 0) return ""; + return `?via=${candidates.map((c) => encodeURIComponent(c)).join("&via=")}`; } // Heavily inspired by/borrowed from the matrix-bot-sdk (with permission): @@ -82,7 +82,8 @@ export default class ElementPermalinkConstructor extends PermalinkConstructor { static parseAppRoute(route: string): PermalinkParts { const parts = route.split("/"); - if (parts.length < 2) { // we're expecting an entity and an ID of some kind at least + if (parts.length < 2) { + // we're expecting an entity and an ID of some kind at least throw new Error("URL is missing parts"); } @@ -93,13 +94,13 @@ export default class ElementPermalinkConstructor extends PermalinkConstructor { const entityType = parts[0]; const entity = parts[1]; - if (entityType === 'user') { + if (entityType === "user") { // Probably a user, no further parsing needed. return PermalinkParts.forUser(entity); - } else if (entityType === 'room') { + } else if (entityType === "room") { // Rejoin the rest because v3 events can have slashes (annoyingly) - const eventId = parts.length > 2 ? parts.slice(2).join('/') : ""; - const via = query.split(/&?via=/).filter(p => !!p); + const eventId = parts.length > 2 ? parts.slice(2).join("/") : ""; + const via = query.split(/&?via=/).filter((p) => !!p); return PermalinkParts.forEvent(entity, eventId, via); } else { throw new Error("Unknown entity type in permalink"); diff --git a/src/utils/permalinks/MatrixSchemePermalinkConstructor.ts b/src/utils/permalinks/MatrixSchemePermalinkConstructor.ts index 904fbb8939..080a666fbd 100644 --- a/src/utils/permalinks/MatrixSchemePermalinkConstructor.ts +++ b/src/utils/permalinks/MatrixSchemePermalinkConstructor.ts @@ -39,8 +39,10 @@ export default class MatrixSchemePermalinkConstructor extends PermalinkConstruct } forEvent(roomId: string, eventId: string, serverCandidates: string[]): string { - return `matrix:${this.encodeEntity(roomId)}` + - `/${this.encodeEntity(eventId)}${this.encodeServerCandidates(serverCandidates)}`; + return ( + `matrix:${this.encodeEntity(roomId)}` + + `/${this.encodeEntity(eventId)}${this.encodeServerCandidates(serverCandidates)}` + ); } forRoom(roomIdOrAlias: string, serverCandidates: string[]): string { @@ -61,8 +63,8 @@ export default class MatrixSchemePermalinkConstructor extends PermalinkConstruct } encodeServerCandidates(candidates: string[]) { - if (!candidates || candidates.length === 0) return ''; - return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`; + if (!candidates || candidates.length === 0) return ""; + return `?via=${candidates.map((c) => encodeURIComponent(c)).join("&via=")}`; } parsePermalink(fullUrl: string): PermalinkParts { @@ -70,26 +72,28 @@ export default class MatrixSchemePermalinkConstructor extends PermalinkConstruct throw new Error("Does not appear to be a permalink"); } - const parts = fullUrl.substring("matrix:".length).split('/'); + const parts = fullUrl.substring("matrix:".length).split("/"); const identifier = parts[0]; const entityNoSigil = parts[1]; - if (identifier === 'u') { + if (identifier === "u") { // Probably a user, no further parsing needed. return PermalinkParts.forUser(`@${entityNoSigil}`); - } else if (identifier === 'r' || identifier === 'roomid') { - const sigil = identifier === 'r' ? '#' : '!'; + } else if (identifier === "r" || identifier === "roomid") { + const sigil = identifier === "r" ? "#" : "!"; - if (parts.length === 2) { // room without event permalink + if (parts.length === 2) { + // room without event permalink const [roomId, query = ""] = entityNoSigil.split("?"); - const via = query.split(/&?via=/g).filter(p => !!p); + const via = query.split(/&?via=/g).filter((p) => !!p); return PermalinkParts.forRoom(`${sigil}${roomId}`, via); } - if (parts[2] === 'e') { // event permalink - const eventIdAndQuery = parts.length > 3 ? parts.slice(3).join('/') : ""; + if (parts[2] === "e") { + // event permalink + const eventIdAndQuery = parts.length > 3 ? parts.slice(3).join("/") : ""; const [eventId, query = ""] = eventIdAndQuery.split("?"); - const via = query.split(/&?via=/g).filter(p => !!p); + const via = query.split(/&?via=/g).filter((p) => !!p); return PermalinkParts.forEvent(`${sigil}${entityNoSigil}`, `$${eventId}`, via); } diff --git a/src/utils/permalinks/MatrixToPermalinkConstructor.ts b/src/utils/permalinks/MatrixToPermalinkConstructor.ts index 3a57fc443f..a451d82606 100644 --- a/src/utils/permalinks/MatrixToPermalinkConstructor.ts +++ b/src/utils/permalinks/MatrixToPermalinkConstructor.ts @@ -48,8 +48,8 @@ export default class MatrixToPermalinkConstructor extends PermalinkConstructor { } encodeServerCandidates(candidates: string[]) { - if (!candidates || candidates.length === 0) return ''; - return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`; + if (!candidates || candidates.length === 0) return ""; + return `?via=${candidates.map((c) => encodeURIComponent(c)).join("&via=")}`; } // Heavily inspired by/borrowed from the matrix-bot-sdk (with permission): @@ -62,20 +62,21 @@ export default class MatrixToPermalinkConstructor extends PermalinkConstructor { const parts = fullUrl.substring(`${baseUrl}/#/`.length).split("/"); const entity = parts[0]; - if (entity[0] === '@') { + if (entity[0] === "@") { // Probably a user, no further parsing needed. return PermalinkParts.forUser(entity); - } else if (entity[0] === '#' || entity[0] === '!') { - if (parts.length === 1) { // room without event permalink - const [roomId, query=""] = entity.split("?"); - const via = query.split(/&?via=/g).filter(p => !!p); + } else if (entity[0] === "#" || entity[0] === "!") { + if (parts.length === 1) { + // room without event permalink + const [roomId, query = ""] = entity.split("?"); + const via = query.split(/&?via=/g).filter((p) => !!p); return PermalinkParts.forRoom(roomId, via); } // rejoin the rest because v3 events can have slashes (annoyingly) - const eventIdAndQuery = parts.length > 1 ? parts.slice(1).join('/') : ""; - const [eventId, query=""] = eventIdAndQuery.split("?"); - const via = query.split(/&?via=/g).filter(p => !!p); + const eventIdAndQuery = parts.length > 1 ? parts.slice(1).join("/") : ""; + const [eventId, query = ""] = eventIdAndQuery.split("?"); + const via = query.split(/&?via=/g).filter((p) => !!p); return PermalinkParts.forEvent(entity, eventId, via); } else { diff --git a/src/utils/permalinks/Permalinks.ts b/src/utils/permalinks/Permalinks.ts index ce2f8aeb1d..4133509f6f 100644 --- a/src/utils/permalinks/Permalinks.ts +++ b/src/utils/permalinks/Permalinks.ts @@ -191,13 +191,18 @@ export class RoomPermalinkCreator { const serverName = getServerName(userId); const domain = getHostnameFromMatrixServerName(serverName) ?? serverName; - return !isHostnameIpAddress(domain) && + return ( + !isHostnameIpAddress(domain) && !isHostInRegex(domain, this.bannedHostsRegexps) && - isHostInRegex(domain, this.allowedHostsRegexps); + isHostInRegex(domain, this.allowedHostsRegexps) + ); }); - const maxEntry = allowedEntries.reduce((max, entry) => { - return (entry[1] > max[1]) ? entry : max; - }, [null, 0]); + const maxEntry = allowedEntries.reduce( + (max, entry) => { + return entry[1] > max[1] ? entry : max; + }, + [null, 0], + ); const [userId, powerLevel] = maxEntry; // object wasn't empty, and max entry wasn't a demotion from the default if (userId !== null && powerLevel >= 50) { @@ -219,11 +224,11 @@ export class RoomPermalinkCreator { const getRegex = (hostname) => new RegExp("^" + utils.globToRegexp(hostname, false) + "$"); const denied = aclEvent.getContent().deny || []; - denied.forEach(h => bannedHostsRegexps.push(getRegex(h))); + denied.forEach((h) => bannedHostsRegexps.push(getRegex(h))); const allowed = aclEvent.getContent().allow || []; allowedHostsRegexps = []; // we don't want to use the default rule here - allowed.forEach(h => allowedHostsRegexps.push(getRegex(h))); + allowed.forEach((h) => allowedHostsRegexps.push(getRegex(h))); } } this.bannedHostsRegexps = bannedHostsRegexps; @@ -248,8 +253,9 @@ export class RoomPermalinkCreator { candidates.add(getServerName(this.highestPlUserId)); } - const serversByPopulation = Object.keys(this.populationMap) - .sort((a, b) => this.populationMap[b] - this.populationMap[a]); + const serversByPopulation = Object.keys(this.populationMap).sort( + (a, b) => this.populationMap[b] - this.populationMap[a], + ); for (let i = 0; i < serversByPopulation.length && candidates.size < MAX_SERVER_CANDIDATES; i++) { const serverName = serversByPopulation[i]; @@ -283,7 +289,7 @@ export function makeRoomPermalink(roomId: string): string { // If the roomId isn't actually a room ID, don't try to list the servers. // Aliases are already routable, and don't need extra information. - if (roomId[0] !== '!') return getPermalinkConstructor().forRoom(roomId, []); + if (roomId[0] !== "!") return getPermalinkConstructor().forRoom(roomId, []); const client = MatrixClientPeg.get(); const room = client.getRoom(roomId); @@ -313,15 +319,15 @@ export function tryTransformEntityToPermalink(entity: string): string { if (!entity) return null; // Check to see if it is a bare entity for starters - if (entity[0] === '#' || entity[0] === '!') return makeRoomPermalink(entity); - if (entity[0] === '@') return makeUserPermalink(entity); + if (entity[0] === "#" || entity[0] === "!") return makeRoomPermalink(entity); + if (entity[0] === "@") return makeUserPermalink(entity); if (entity.slice(0, 7) === "matrix:") { try { const permalinkParts = parsePermalink(entity); if (permalinkParts) { if (permalinkParts.roomIdOrAlias) { - const eventIdPart = permalinkParts.eventId ? `/${permalinkParts.eventId}` : ''; + const eventIdPart = permalinkParts.eventId ? `/${permalinkParts.eventId}` : ""; let pl = matrixtoBaseUrl + `/#/${permalinkParts.roomIdOrAlias}${eventIdPart}`; if (permalinkParts.viaServers.length > 0) { pl += new MatrixToPermalinkConstructor().encodeServerCandidates(permalinkParts.viaServers); @@ -344,7 +350,8 @@ export function tryTransformEntityToPermalink(entity: string): string { * @returns {string} The transformed permalink or original URL if unable. */ export function tryTransformPermalinkToLocalHref(permalink: string): string { - if (!permalink.startsWith("http:") && + if ( + !permalink.startsWith("http:") && !permalink.startsWith("https:") && !permalink.startsWith("matrix:") && !permalink.startsWith("vector:") // Element Desktop @@ -367,7 +374,7 @@ export function tryTransformPermalinkToLocalHref(permalink: string): string { const permalinkParts = parsePermalink(permalink); if (permalinkParts) { if (permalinkParts.roomIdOrAlias) { - const eventIdPart = permalinkParts.eventId ? `/${permalinkParts.eventId}` : ''; + const eventIdPart = permalinkParts.eventId ? `/${permalinkParts.eventId}` : ""; permalink = `#/room/${permalinkParts.roomIdOrAlias}${eventIdPart}`; if (permalinkParts.viaServers.length > 0) { permalink += new MatrixToPermalinkConstructor().encodeServerCandidates(permalinkParts.viaServers); @@ -393,7 +400,7 @@ export function getPrimaryPermalinkEntity(permalink: string): string { if (m) { // A bit of a hack, but it gets the job done const handler = new ElementPermalinkConstructor("http://localhost"); - const entityInfo = m[1].split('#').slice(1).join('#'); + const entityInfo = m[1].split("#").slice(1).join("#"); permalinkParts = handler.parsePermalink(`http://localhost/#${entityInfo}`); } } @@ -452,7 +459,7 @@ function isHostInRegex(hostname: string, regexps: RegExp[]): boolean { if (!hostname) return true; // assumed if (regexps.length > 0 && !regexps[0].test) throw new Error(regexps[0].toString()); - return regexps.some(h => h.test(hostname)); + return regexps.some((h) => h.test(hostname)); } function isHostnameIpAddress(hostname: string): boolean { diff --git a/src/utils/permalinks/navigator.ts b/src/utils/permalinks/navigator.ts index ffa4678dbe..640bdabcb7 100644 --- a/src/utils/permalinks/navigator.ts +++ b/src/utils/permalinks/navigator.ts @@ -23,7 +23,8 @@ import { tryTransformPermalinkToLocalHref } from "./Permalinks"; */ export function navigateToPermalink(uri: string): void { const localUri = tryTransformPermalinkToLocalHref(uri); - if (!localUri || localUri === uri) { // parse failure can lead to an unmodified URL + if (!localUri || localUri === uri) { + // parse failure can lead to an unmodified URL throw new Error("Failed to transform URI"); } window.location.hash = localUri; // it'll just be a fragment diff --git a/src/utils/pillify.tsx b/src/utils/pillify.tsx index b7a1b4e558..ecc208e732 100644 --- a/src/utils/pillify.tsx +++ b/src/utils/pillify.tsx @@ -15,11 +15,11 @@ limitations under the License. */ import React from "react"; -import ReactDOM from 'react-dom'; -import { PushProcessor } from 'matrix-js-sdk/src/pushprocessor'; +import ReactDOM from "react-dom"; +import { PushProcessor } from "matrix-js-sdk/src/pushprocessor"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; -import { MatrixClientPeg } from '../MatrixClientPeg'; +import { MatrixClientPeg } from "../MatrixClientPeg"; import SettingsStore from "../settings/SettingsStore"; import Pill, { PillType } from "../components/views/elements/Pill"; import { parsePermalink } from "./permalinks/Permalinks"; @@ -54,14 +54,11 @@ export function pillifyLinks(nodes: ArrayLike, mxEvent: MatrixEvent, pi // If the link is a (localised) matrix.to link, replace it with a pill // We don't want to pill event permalinks, so those are ignored. if (parts && !parts.eventId) { - const pillContainer = document.createElement('span'); + const pillContainer = document.createElement("span"); - const pill = ; + const pill = ( + + ); ReactDOM.render(pill, pillContainer); node.parentNode.replaceChild(pillContainer, node); @@ -111,13 +108,15 @@ export function pillifyLinks(nodes: ArrayLike, mxEvent: MatrixEvent, pi // Note we've checked roomNotifTextNodes.length > 0 so we'll do this at least once node = roomNotifTextNode.nextSibling; - const pillContainer = document.createElement('span'); - const pill = ; + const pillContainer = document.createElement("span"); + const pill = ( + + ); ReactDOM.render(pill, pillContainer); roomNotifTextNode.parentNode.replaceChild(pillContainer, roomNotifTextNode); diff --git a/src/utils/read-receipts.ts b/src/utils/read-receipts.ts index 35eda2e338..fc389f54a7 100644 --- a/src/utils/read-receipts.ts +++ b/src/utils/read-receipts.ts @@ -30,7 +30,7 @@ export function readReceiptChangeIsFor(event: MatrixEvent, client: MatrixClient) for (const [receiptType, receipt] of Object.entries(event.getContent()[eventId])) { if (!isSupportedReceiptType(receiptType)) continue; - if (Object.keys((receipt || {})).includes(myUserId)) return true; + if (Object.keys(receipt || {}).includes(myUserId)) return true; } } } diff --git a/src/utils/room/getJoinedNonFunctionalMembers.ts b/src/utils/room/getJoinedNonFunctionalMembers.ts index 912c4bf1f1..20a1b37eb8 100644 --- a/src/utils/room/getJoinedNonFunctionalMembers.ts +++ b/src/utils/room/getJoinedNonFunctionalMembers.ts @@ -23,5 +23,5 @@ import { getFunctionalMembers } from "./getFunctionalMembers"; */ export const getJoinedNonFunctionalMembers = (room: Room): RoomMember[] => { const functionalMembers = getFunctionalMembers(room); - return room.getJoinedMembers().filter(m => !functionalMembers.includes(m.userId)); + return room.getJoinedMembers().filter((m) => !functionalMembers.includes(m.userId)); }; diff --git a/src/utils/room/htmlToPlaintext.ts b/src/utils/room/htmlToPlaintext.ts index 883db8d360..4b0272b4e1 100644 --- a/src/utils/room/htmlToPlaintext.ts +++ b/src/utils/room/htmlToPlaintext.ts @@ -15,5 +15,5 @@ limitations under the License. */ export function htmlToPlainText(html: string) { - return new DOMParser().parseFromString(html, 'text/html').documentElement.textContent; + return new DOMParser().parseFromString(html, "text/html").documentElement.textContent; } diff --git a/src/utils/sets.ts b/src/utils/sets.ts index da856af2b5..68c226de39 100644 --- a/src/utils/sets.ts +++ b/src/utils/sets.ts @@ -25,8 +25,8 @@ import { arrayDiff, Diff } from "./arrays"; export function setHasDiff(a: Set, b: Set): boolean { if (a.size === b.size) { // When the lengths are equal, check to see if either set is missing an element from the other. - if (Array.from(b).some(i => !a.has(i))) return true; - if (Array.from(a).some(i => !b.has(i))) return true; + if (Array.from(b).some((i) => !a.has(i))) return true; + if (Array.from(a).some((i) => !b.has(i))) return true; // if all the keys are common, say so return false; diff --git a/src/utils/space.tsx b/src/utils/space.tsx index 1e30b7235a..0153edbd43 100644 --- a/src/utils/space.tsx +++ b/src/utils/space.tsx @@ -41,18 +41,20 @@ import { SdkContextClass } from "../contexts/SDKContext"; export const shouldShowSpaceSettings = (space: Room) => { const userId = space.client.getUserId(); - return space.getMyMembership() === "join" - && (space.currentState.maySendStateEvent(EventType.RoomAvatar, userId) - || space.currentState.maySendStateEvent(EventType.RoomName, userId) - || space.currentState.maySendStateEvent(EventType.RoomTopic, userId) - || space.currentState.maySendStateEvent(EventType.RoomJoinRules, userId)); + return ( + space.getMyMembership() === "join" && + (space.currentState.maySendStateEvent(EventType.RoomAvatar, userId) || + space.currentState.maySendStateEvent(EventType.RoomName, userId) || + space.currentState.maySendStateEvent(EventType.RoomTopic, userId) || + space.currentState.maySendStateEvent(EventType.RoomJoinRules, userId)) + ); }; export const makeSpaceParentEvent = (room: Room, canonical = false) => ({ type: EventType.SpaceParent, content: { - "via": calculateRoomVia(room), - "canonical": canonical, + via: calculateRoomVia(room), + canonical: canonical, }, state_key: room.roomId, }); @@ -85,19 +87,20 @@ export const showCreateNewRoom = async (space: Room, type?: RoomType): Promise - ( - (space?.getMyMembership() === "join" && space.canInvite(space.client.getUserId())) || - space.getJoinRule() === JoinRule.Public - ) && shouldShowComponent(UIComponent.InviteUsers); + ((space?.getMyMembership() === "join" && space.canInvite(space.client.getUserId())) || + space.getJoinRule() === JoinRule.Public) && + shouldShowComponent(UIComponent.InviteUsers); export const showSpaceInvite = (space: Room, initialText = ""): void => { if (space.getJoinRule() === "public") { const modal = Modal.createDialog(InfoDialog, { title: _t("Invite to %(spaceName)s", { spaceName: space.name }), - description: - { _t("Share your public space") } - modal.close()} /> - , + description: ( + + {_t("Share your public space")} + modal.close()} /> + + ), fixedWidth: false, button: false, className: "mx_SpacePanel_sharePublicSpace", @@ -109,27 +112,35 @@ export const showSpaceInvite = (space: Room, initialText = ""): void => { }; export const showAddExistingSubspace = (space: Room): void => { - Modal.createDialog(AddExistingSubspaceDialog, { - space, - onCreateSubspaceClick: () => showCreateNewSubspace(space), - onFinished: (added: boolean) => { - if (added && SdkContextClass.instance.roomViewStore.getRoomId() === space.roomId) { - defaultDispatcher.fire(Action.UpdateSpaceHierarchy); - } + Modal.createDialog( + AddExistingSubspaceDialog, + { + space, + onCreateSubspaceClick: () => showCreateNewSubspace(space), + onFinished: (added: boolean) => { + if (added && SdkContextClass.instance.roomViewStore.getRoomId() === space.roomId) { + defaultDispatcher.fire(Action.UpdateSpaceHierarchy); + } + }, }, - }, "mx_AddExistingToSpaceDialog_wrapper"); + "mx_AddExistingToSpaceDialog_wrapper", + ); }; export const showCreateNewSubspace = (space: Room): void => { - Modal.createDialog(CreateSubspaceDialog, { - space, - onAddExistingSpaceClick: () => showAddExistingSubspace(space), - onFinished: (added: boolean) => { - if (added && SdkContextClass.instance.roomViewStore.getRoomId() === space.roomId) { - defaultDispatcher.fire(Action.UpdateSpaceHierarchy); - } + Modal.createDialog( + CreateSubspaceDialog, + { + space, + onAddExistingSpaceClick: () => showAddExistingSubspace(space), + onFinished: (added: boolean) => { + if (added && SdkContextClass.instance.roomViewStore.getRoomId() === space.roomId) { + defaultDispatcher.fire(Action.UpdateSpaceHierarchy); + } + }, }, - }, "mx_CreateSubspaceDialog_wrapper"); + "mx_CreateSubspaceDialog_wrapper", + ); }; export const bulkSpaceBehaviour = async ( diff --git a/src/utils/stringOrderField.ts b/src/utils/stringOrderField.ts index da840792ee..7c950e75fe 100644 --- a/src/utils/stringOrderField.ts +++ b/src/utils/stringOrderField.ts @@ -47,7 +47,9 @@ export function midPointsBetweenStrings( const step = (baseB - baseA) / BigInt(count + 1); const start = BigInt(baseA + step); - return Array(count).fill(undefined).map((_, i) => baseToString(start + (BigInt(i) * step), alphabet)); + return Array(count) + .fill(undefined) + .map((_, i) => baseToString(start + BigInt(i) * step, alphabet)); } interface IEntry { @@ -62,11 +64,7 @@ export const reorderLexicographically = ( maxLen = 50, ): IEntry[] => { // sanity check inputs - if ( - fromIndex < 0 || toIndex < 0 || - fromIndex > orders.length || toIndex > orders.length || - fromIndex === toIndex - ) { + if (fromIndex < 0 || toIndex < 0 || fromIndex > orders.length || toIndex > orders.length || fromIndex === toIndex) { return []; } @@ -82,9 +80,10 @@ export const reorderLexicographically = ( let rightBoundIdx = toIndex; let canMoveLeft = true; - const nextBase = newOrder[toIndex + 1]?.order !== undefined - ? stringToBase(newOrder[toIndex + 1].order) - : BigInt(Number.MAX_VALUE); + const nextBase = + newOrder[toIndex + 1]?.order !== undefined + ? stringToBase(newOrder[toIndex + 1].order) + : BigInt(Number.MAX_VALUE); // check how far left we would have to mutate to fit in that direction for (let i = toIndex - 1, j = 1; i >= 0; i--, j++) { @@ -95,7 +94,8 @@ export const reorderLexicographically = ( // verify the left move would be sufficient const firstOrderBase = newOrder[0].order === undefined ? undefined : stringToBase(newOrder[0].order); const bigToIndex = BigInt(toIndex); - if (leftBoundIdx === 0 && + if ( + leftBoundIdx === 0 && firstOrderBase !== undefined && nextBase - firstOrderBase <= bigToIndex && firstOrderBase <= bigToIndex @@ -106,9 +106,10 @@ export const reorderLexicographically = ( const canDisplaceRight = !orderToLeftUndefined; let canMoveRight = canDisplaceRight; if (canDisplaceRight) { - const prevBase = newOrder[toIndex - 1]?.order !== undefined - ? stringToBase(newOrder[toIndex - 1]?.order) - : BigInt(Number.MIN_VALUE); + const prevBase = + newOrder[toIndex - 1]?.order !== undefined + ? stringToBase(newOrder[toIndex - 1]?.order) + : BigInt(Number.MIN_VALUE); // check how far right we would have to mutate to fit in that direction for (let i = toIndex + 1, j = 1; i < newOrder.length; i++, j++) { @@ -117,10 +118,11 @@ export const reorderLexicographically = ( } // verify the right move would be sufficient - if (rightBoundIdx === newOrder.length - 1 && - (newOrder[rightBoundIdx] - ? stringToBase(newOrder[rightBoundIdx].order) - : BigInt(Number.MAX_VALUE)) - prevBase <= (rightBoundIdx - toIndex) + if ( + rightBoundIdx === newOrder.length - 1 && + (newOrder[rightBoundIdx] ? stringToBase(newOrder[rightBoundIdx].order) : BigInt(Number.MAX_VALUE)) - + prevBase <= + rightBoundIdx - toIndex ) { canMoveRight = false; } @@ -136,8 +138,9 @@ export const reorderLexicographically = ( } const prevOrder = newOrder[leftBoundIdx - 1]?.order ?? ""; - const nextOrder = newOrder[rightBoundIdx + 1]?.order - ?? DEFAULT_ALPHABET.charAt(DEFAULT_ALPHABET.length - 1).repeat(prevOrder.length || 1); + const nextOrder = + newOrder[rightBoundIdx + 1]?.order ?? + DEFAULT_ALPHABET.charAt(DEFAULT_ALPHABET.length - 1).repeat(prevOrder.length || 1); const changes = midPointsBetweenStrings(prevOrder, nextOrder, 1 + rightBoundIdx - leftBoundIdx, maxLen); diff --git a/src/utils/strings.ts b/src/utils/strings.ts index 1758cb5ff8..b2dfe4d1bf 100644 --- a/src/utils/strings.ts +++ b/src/utils/strings.ts @@ -72,7 +72,7 @@ export function selectText(target: Element) { */ export function copyNode(ref: Element): boolean { selectText(ref); - return document.execCommand('copy'); + return document.execCommand("copy"); } /** diff --git a/src/utils/tooltipify.tsx b/src/utils/tooltipify.tsx index 843ee326ab..8e9ccf4fc8 100644 --- a/src/utils/tooltipify.tsx +++ b/src/utils/tooltipify.tsx @@ -15,7 +15,7 @@ limitations under the License. */ import React from "react"; -import ReactDOM from 'react-dom'; +import ReactDOM from "react-dom"; import PlatformPeg from "../PlatformPeg"; import LinkWithTooltip from "../components/views/elements/LinkWithTooltip"; @@ -44,8 +44,10 @@ export function tooltipifyLinks(rootNodes: ArrayLike, ignoredNodes: Ele continue; } - if (node.tagName === "A" && node.getAttribute("href") - && node.getAttribute("href") !== node.textContent.trim() + if ( + node.tagName === "A" && + node.getAttribute("href") && + node.getAttribute("href") !== node.textContent.trim() ) { let href = node.getAttribute("href"); try { @@ -57,9 +59,11 @@ export function tooltipifyLinks(rootNodes: ArrayLike, ignoredNodes: Ele // The node's innerHTML was already sanitized before being rendered in the first place, here we are just // wrapping the link with the LinkWithTooltip component, keeping the same children. Ideally we'd do this // without the superfluous span but this is not something React trivially supports at this time. - const tooltip = - - ; + const tooltip = ( + + + + ); ReactDOM.render(tooltip, node); containers.push(node); diff --git a/src/utils/useTooltip.tsx b/src/utils/useTooltip.tsx index 98b6ffa1bd..303bea17db 100644 --- a/src/utils/useTooltip.tsx +++ b/src/utils/useTooltip.tsx @@ -31,10 +31,7 @@ export function useTooltip(props: ComponentProps): [TooltipEvent // No need to fill up the DOM with hidden tooltip elements. Only add the // tooltip when we're hovering over the item (performance) - const tooltip = ; + const tooltip = ; return [{ showTooltip, hideTooltip }, tooltip]; } diff --git a/src/utils/validate/numberInRange.ts b/src/utils/validate/numberInRange.ts index 181641c935..97d3e5fabb 100644 --- a/src/utils/validate/numberInRange.ts +++ b/src/utils/validate/numberInRange.ts @@ -20,5 +20,5 @@ limitations under the License. * - in a provided range (inclusive) */ export const validateNumberInRange = (min: number, max: number) => (value?: number) => { - return typeof value === 'number' && !(isNaN(value) || min > value || value > max); + return typeof value === "number" && !(isNaN(value) || min > value || value > max); }; diff --git a/src/utils/video-rooms.ts b/src/utils/video-rooms.ts index 7177e0c5e0..f45fb8f45c 100644 --- a/src/utils/video-rooms.ts +++ b/src/utils/video-rooms.ts @@ -17,5 +17,5 @@ limitations under the License. import type { Room } from "matrix-js-sdk/src/models/room"; import SettingsStore from "../settings/SettingsStore"; -export const isVideoRoom = (room: Room) => room.isElementVideoRoom() - || (SettingsStore.getValue("feature_element_call_video_rooms") && room.isCallRoom()); +export const isVideoRoom = (room: Room) => + room.isElementVideoRoom() || (SettingsStore.getValue("feature_element_call_video_rooms") && room.isCallRoom()); diff --git a/src/verification.ts b/src/verification.ts index 940de1cbaf..61cead0f22 100644 --- a/src/verification.ts +++ b/src/verification.ts @@ -15,14 +15,14 @@ limitations under the License. */ import { User } from "matrix-js-sdk/src/models/user"; -import { verificationMethods as VerificationMethods } from 'matrix-js-sdk/src/crypto'; +import { verificationMethods as VerificationMethods } from "matrix-js-sdk/src/crypto"; import { RoomMember } from "matrix-js-sdk/src/matrix"; -import { MatrixClientPeg } from './MatrixClientPeg'; +import { MatrixClientPeg } from "./MatrixClientPeg"; import dis from "./dispatcher/dispatcher"; -import Modal from './Modal'; +import Modal from "./Modal"; import { RightPanelPhases } from "./stores/right-panel/RightPanelStorePhases"; -import { accessSecretStorage } from './SecurityManager'; +import { accessSecretStorage } from "./SecurityManager"; import UntrustedDeviceDialog from "./components/views/dialogs/UntrustedDeviceDialog"; import { IDevice } from "./components/views/right_panel/UserInfo"; import ManualDeviceKeyVerificationDialog from "./components/views/dialogs/ManualDeviceKeyVerificationDialog"; @@ -47,7 +47,7 @@ async function enable4SIfNeeded() { export async function verifyDevice(user: User, device: IDevice) { const cli = MatrixClientPeg.get(); if (cli.isGuest()) { - dis.dispatch({ action: 'require_registration' }); + dis.dispatch({ action: "require_registration" }); return; } // if cross-signing is not explicitly disabled, check if it should be enabled first. @@ -81,7 +81,7 @@ export async function verifyDevice(user: User, device: IDevice) { export async function legacyVerifyUser(user: User) { const cli = MatrixClientPeg.get(); if (cli.isGuest()) { - dis.dispatch({ action: 'require_registration' }); + dis.dispatch({ action: "require_registration" }); return; } // if cross-signing is not explicitly disabled, check if it should be enabled first. @@ -97,7 +97,7 @@ export async function legacyVerifyUser(user: User) { export async function verifyUser(user: User) { const cli = MatrixClientPeg.get(); if (cli.isGuest()) { - dis.dispatch({ action: 'require_registration' }); + dis.dispatch({ action: "require_registration" }); return; } if (!(await enable4SIfNeeded())) { @@ -108,10 +108,8 @@ export async function verifyUser(user: User) { } function setRightPanel(state: IRightPanelCardState) { - if (RightPanelStore.instance.roomPhaseHistory.some((card) => (card.phase == RightPanelPhases.RoomSummary))) { - RightPanelStore.instance.pushCard( - { phase: RightPanelPhases.EncryptionPanel, state }, - ); + if (RightPanelStore.instance.roomPhaseHistory.some((card) => card.phase == RightPanelPhases.RoomSummary)) { + RightPanelStore.instance.pushCard({ phase: RightPanelPhases.EncryptionPanel, state }); } else { RightPanelStore.instance.setCards([ { phase: RightPanelPhases.RoomSummary }, diff --git a/src/voice-broadcast/audio/VoiceBroadcastRecorder.ts b/src/voice-broadcast/audio/VoiceBroadcastRecorder.ts index ebd9bfc737..e8771b94fb 100644 --- a/src/voice-broadcast/audio/VoiceBroadcastRecorder.ts +++ b/src/voice-broadcast/audio/VoiceBroadcastRecorder.ts @@ -45,7 +45,8 @@ export interface ChunkRecordedPayload { */ export class VoiceBroadcastRecorder extends TypedEventEmitter - implements IDestroyable { + implements IDestroyable +{ private headers = new Uint8Array(0); private chunkBuffer = new Uint8Array(0); // position of the previous chunk in seconds @@ -54,10 +55,7 @@ export class VoiceBroadcastRecorder // current chunk length in seconds private currentChunkLength = 0; - public constructor( - private voiceRecording: VoiceRecording, - public readonly targetChunkLength: number, - ) { + public constructor(private voiceRecording: VoiceRecording, public readonly targetChunkLength: number) { super(); this.voiceRecording.onDataAvailable = this.onDataAvailable; } @@ -148,10 +146,7 @@ export class VoiceBroadcastRecorder return; } - this.emit( - VoiceBroadcastRecorderEvent.ChunkRecorded, - this.extractChunk(), - ); + this.emit(VoiceBroadcastRecorderEvent.ChunkRecorded, this.extractChunk()); } public destroy(): void { diff --git a/src/voice-broadcast/components/VoiceBroadcastBody.tsx b/src/voice-broadcast/components/VoiceBroadcastBody.tsx index 95bc9fde06..b5a3a7f471 100644 --- a/src/voice-broadcast/components/VoiceBroadcastBody.tsx +++ b/src/voice-broadcast/components/VoiceBroadcastBody.tsx @@ -58,13 +58,9 @@ export const VoiceBroadcastBody: React.FC = ({ mxEvent }) => { if (shouldDisplayAsVoiceBroadcastRecordingTile(infoState, client, mxEvent)) { const recording = VoiceBroadcastRecordingsStore.instance().getByInfoEvent(mxEvent, client); - return ; + return ; } const playback = VoiceBroadcastPlaybacksStore.instance().getByInfoEvent(mxEvent, client); - return ; + return ; }; diff --git a/src/voice-broadcast/components/atoms/LiveBadge.tsx b/src/voice-broadcast/components/atoms/LiveBadge.tsx index 23a80a6a27..a5a96f460b 100644 --- a/src/voice-broadcast/components/atoms/LiveBadge.tsx +++ b/src/voice-broadcast/components/atoms/LiveBadge.tsx @@ -24,18 +24,15 @@ interface Props { grey?: boolean; } -export const LiveBadge: React.FC = ({ - grey = false, -}) => { - const liveBadgeClasses = classNames( - "mx_LiveBadge", - { - "mx_LiveBadge--grey": grey, - }, - ); +export const LiveBadge: React.FC = ({ grey = false }) => { + const liveBadgeClasses = classNames("mx_LiveBadge", { + "mx_LiveBadge--grey": grey, + }); - return
- - { _t("Live") } -
; + return ( +
+ + {_t("Live")} +
+ ); }; diff --git a/src/voice-broadcast/components/atoms/SeekButton.tsx b/src/voice-broadcast/components/atoms/SeekButton.tsx index 11bf99123a..0abb986595 100644 --- a/src/voice-broadcast/components/atoms/SeekButton.tsx +++ b/src/voice-broadcast/components/atoms/SeekButton.tsx @@ -24,16 +24,10 @@ interface Props { onClick: () => void; } -export const SeekButton: React.FC = ({ - onClick, - icon: Icon, - label, -}) => { - return - - ; +export const SeekButton: React.FC = ({ onClick, icon: Icon, label }) => { + return ( + + + + ); }; diff --git a/src/voice-broadcast/components/atoms/VoiceBroadcastControl.tsx b/src/voice-broadcast/components/atoms/VoiceBroadcastControl.tsx index 276282d198..684a5ea365 100644 --- a/src/voice-broadcast/components/atoms/VoiceBroadcastControl.tsx +++ b/src/voice-broadcast/components/atoms/VoiceBroadcastControl.tsx @@ -26,17 +26,14 @@ interface Props { onClick: () => void; } -export const VoiceBroadcastControl: React.FC = ({ - className = "", - icon: Icon, - label, - onClick, -}) => { - return - - ; +export const VoiceBroadcastControl: React.FC = ({ className = "", icon: Icon, label, onClick }) => { + return ( + + + + ); }; diff --git a/src/voice-broadcast/components/atoms/VoiceBroadcastHeader.tsx b/src/voice-broadcast/components/atoms/VoiceBroadcastHeader.tsx index 7984316459..e1ee393f81 100644 --- a/src/voice-broadcast/components/atoms/VoiceBroadcastHeader.tsx +++ b/src/voice-broadcast/components/atoms/VoiceBroadcastHeader.tsx @@ -54,13 +54,11 @@ export const VoiceBroadcastHeader: React.FC = ({ const broadcast = showBroadcast && (
- { _t("Voice broadcast") } + {_t("Voice broadcast")}
); - const liveBadge = live !== "not-live" && ( - - ); + const liveBadge = live !== "not-live" && ; const closeButton = showClose && ( @@ -78,7 +76,7 @@ export const VoiceBroadcastHeader: React.FC = ({ const buffering = showBuffering && (
- { _t("Buffering…") } + {_t("Buffering…")}
); @@ -94,22 +92,22 @@ export const VoiceBroadcastHeader: React.FC = ({ title={_t("Change input device")} > - { microphoneLabel } + {microphoneLabel} ); - return
- -
-
- { room.name } + return ( +
+ +
+
{room.name}
+ {microphoneLine} + {timeLeftLine} + {broadcast} + {buffering}
- { microphoneLine } - { timeLeftLine } - { broadcast } - { buffering } + {liveBadge} + {closeButton}
- { liveBadge } - { closeButton } -
; + ); }; diff --git a/src/voice-broadcast/components/atoms/VoiceBroadcastRoomSubtitle.tsx b/src/voice-broadcast/components/atoms/VoiceBroadcastRoomSubtitle.tsx index 4c6356ba2b..17a94a1bd1 100644 --- a/src/voice-broadcast/components/atoms/VoiceBroadcastRoomSubtitle.tsx +++ b/src/voice-broadcast/components/atoms/VoiceBroadcastRoomSubtitle.tsx @@ -20,8 +20,10 @@ import { Icon as LiveIcon } from "../../../../res/img/element-icons/live.svg"; import { _t } from "../../../languageHandler"; export const VoiceBroadcastRoomSubtitle = () => { - return
- - { _t("Live") } -
; + return ( +
+ + {_t("Live")} +
+ ); }; diff --git a/src/voice-broadcast/components/molecules/VoiceBroadcastPlaybackBody.tsx b/src/voice-broadcast/components/molecules/VoiceBroadcastPlaybackBody.tsx index cc86e3304d..8c93975b01 100644 --- a/src/voice-broadcast/components/molecules/VoiceBroadcastPlaybackBody.tsx +++ b/src/voice-broadcast/components/molecules/VoiceBroadcastPlaybackBody.tsx @@ -40,18 +40,8 @@ interface VoiceBroadcastPlaybackBodyProps { playback: VoiceBroadcastPlayback; } -export const VoiceBroadcastPlaybackBody: React.FC = ({ - pip = false, - playback, -}) => { - const { - times, - liveness, - playbackState, - room, - sender, - toggle, - } = useVoiceBroadcastPlayback(playback); +export const VoiceBroadcastPlaybackBody: React.FC = ({ pip = false, playback }) => { + const { times, liveness, playbackState, room, sender, toggle } = useVoiceBroadcastPlayback(playback); let controlIcon: React.FC>; let controlLabel: string; @@ -75,12 +65,9 @@ export const VoiceBroadcastPlaybackBody: React.FC; + const control = ( + + ); let seekBackwardButton: ReactElement | null = null; let seekForwardButton: ReactElement | null = null; @@ -90,21 +77,17 @@ export const VoiceBroadcastPlaybackBody: React.FC; + seekBackwardButton = ( + + ); const onSeekForwardButtonClick = () => { playback.skipTo(Math.min(times.duration, times.position + SEEK_TIME)); }; - seekForwardButton = ; + seekForwardButton = ( + + ); } const classes = classNames({ @@ -122,9 +105,9 @@ export const VoiceBroadcastPlaybackBody: React.FC
- { seekBackwardButton } - { control } - { seekForwardButton } + {seekBackwardButton} + {control} + {seekForwardButton}
diff --git a/src/voice-broadcast/components/molecules/VoiceBroadcastPreRecordingPip.tsx b/src/voice-broadcast/components/molecules/VoiceBroadcastPreRecordingPip.tsx index 3353093282..041852baeb 100644 --- a/src/voice-broadcast/components/molecules/VoiceBroadcastPreRecordingPip.tsx +++ b/src/voice-broadcast/components/molecules/VoiceBroadcastPreRecordingPip.tsx @@ -28,9 +28,7 @@ interface Props { voiceBroadcastPreRecording: VoiceBroadcastPreRecording; } -export const VoiceBroadcastPreRecordingPip: React.FC = ({ - voiceBroadcastPreRecording, -}) => { +export const VoiceBroadcastPreRecordingPip: React.FC = ({ voiceBroadcastPreRecording }) => { const pipRef = useRef(null); const { currentDevice, currentDeviceLabel, devices, setDevice } = useAudioDeviceSelection(); const [showDeviceSelect, setShowDeviceSelect] = useState(false); @@ -40,32 +38,31 @@ export const VoiceBroadcastPreRecordingPip: React.FC = ({ setDevice(device); }; - return
- setShowDeviceSelect(true)} - room={voiceBroadcastPreRecording.room} - microphoneLabel={currentDeviceLabel} - showClose={true} - /> - - - { _t("Go live") } - - { - showDeviceSelect && + setShowDeviceSelect(true)} + room={voiceBroadcastPreRecording.room} + microphoneLabel={currentDeviceLabel} + showClose={true} /> - } -
; + + + {_t("Go live")} + + {showDeviceSelect && ( + + )} +
+ ); }; diff --git a/src/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody.tsx b/src/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody.tsx index f54e04b8f1..0d7b82474c 100644 --- a/src/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody.tsx +++ b/src/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody.tsx @@ -20,19 +20,11 @@ interface VoiceBroadcastRecordingBodyProps { } export const VoiceBroadcastRecordingBody: React.FC = ({ recording }) => { - const { - live, - room, - sender, - } = useVoiceBroadcastRecording(recording); + const { live, room, sender } = useVoiceBroadcastRecording(recording); return (
- +
); }; diff --git a/src/voice-broadcast/components/molecules/VoiceBroadcastRecordingPip.tsx b/src/voice-broadcast/components/molecules/VoiceBroadcastRecordingPip.tsx index 7946cf0262..1ae05b086e 100644 --- a/src/voice-broadcast/components/molecules/VoiceBroadcastRecordingPip.tsx +++ b/src/voice-broadcast/components/molecules/VoiceBroadcastRecordingPip.tsx @@ -16,11 +16,7 @@ limitations under the License. import React, { useRef, useState } from "react"; -import { - VoiceBroadcastControl, - VoiceBroadcastInfoState, - VoiceBroadcastRecording, -} from "../.."; +import { VoiceBroadcastControl, VoiceBroadcastInfoState, VoiceBroadcastRecording } from "../.."; import { useVoiceBroadcastRecording } from "../../hooks/useVoiceBroadcastRecording"; import { VoiceBroadcastHeader } from "../atoms/VoiceBroadcastHeader"; import { Icon as StopIcon } from "../../../../res/img/element-icons/Stop.svg"; @@ -38,14 +34,8 @@ interface VoiceBroadcastRecordingPipProps { export const VoiceBroadcastRecordingPip: React.FC = ({ recording }) => { const pipRef = useRef(null); - const { - live, - timeLeft, - recordingState, - room, - stopRecording, - toggleRecording, - } = useVoiceBroadcastRecording(recording); + const { live, timeLeft, recordingState, room, stopRecording, toggleRecording } = + useVoiceBroadcastRecording(recording); const { currentDevice, devices, setDevice } = useAudioDeviceSelection(); const onDeviceSelect = async (device: MediaDeviceInfo) => { @@ -70,46 +60,37 @@ export const VoiceBroadcastRecordingPip: React.FC(false); - const toggleControl = recordingState === VoiceBroadcastInfoState.Paused - ? - : ; - - return
- -
-
- { toggleControl } - setShowDeviceSelect(true)} - title={_t("Change input device")} - > - - + const toggleControl = + recordingState === VoiceBroadcastInfoState.Paused ? ( + ) : ( + + ); + + return ( +
+ +
+
+ {toggleControl} + setShowDeviceSelect(true)} title={_t("Change input device")}> + + + +
+ {showDeviceSelect && ( + + )}
- { - showDeviceSelect && - } -
; + ); }; diff --git a/src/voice-broadcast/hooks/useCurrentVoiceBroadcastPlayback.ts b/src/voice-broadcast/hooks/useCurrentVoiceBroadcastPlayback.ts index d169b9bd55..7ed15d06f2 100644 --- a/src/voice-broadcast/hooks/useCurrentVoiceBroadcastPlayback.ts +++ b/src/voice-broadcast/hooks/useCurrentVoiceBroadcastPlayback.ts @@ -23,9 +23,7 @@ import { VoiceBroadcastPlaybacksStoreEvent, } from "../stores/VoiceBroadcastPlaybacksStore"; -export const useCurrentVoiceBroadcastPlayback = ( - voiceBroadcastPlaybackStore: VoiceBroadcastPlaybacksStore, -) => { +export const useCurrentVoiceBroadcastPlayback = (voiceBroadcastPlaybackStore: VoiceBroadcastPlaybacksStore) => { const [currentVoiceBroadcastPlayback, setVoiceBroadcastPlayback] = useState( voiceBroadcastPlaybackStore.getCurrent(), ); diff --git a/src/voice-broadcast/hooks/useCurrentVoiceBroadcastPreRecording.ts b/src/voice-broadcast/hooks/useCurrentVoiceBroadcastPreRecording.ts index ca9a5769eb..e78b9656a8 100644 --- a/src/voice-broadcast/hooks/useCurrentVoiceBroadcastPreRecording.ts +++ b/src/voice-broadcast/hooks/useCurrentVoiceBroadcastPreRecording.ts @@ -26,11 +26,7 @@ export const useCurrentVoiceBroadcastPreRecording = ( voiceBroadcastPreRecordingStore.getCurrent(), ); - useTypedEventEmitter( - voiceBroadcastPreRecordingStore, - "changed", - setCurrentVoiceBroadcastPreRecording, - ); + useTypedEventEmitter(voiceBroadcastPreRecordingStore, "changed", setCurrentVoiceBroadcastPreRecording); return { currentVoiceBroadcastPreRecording, diff --git a/src/voice-broadcast/hooks/useCurrentVoiceBroadcastRecording.ts b/src/voice-broadcast/hooks/useCurrentVoiceBroadcastRecording.ts index 7b5c597a18..4ba397082e 100644 --- a/src/voice-broadcast/hooks/useCurrentVoiceBroadcastRecording.ts +++ b/src/voice-broadcast/hooks/useCurrentVoiceBroadcastRecording.ts @@ -19,9 +19,7 @@ import { useState } from "react"; import { VoiceBroadcastRecordingsStore, VoiceBroadcastRecordingsStoreEvent } from ".."; import { useTypedEventEmitter } from "../../hooks/useEventEmitter"; -export const useCurrentVoiceBroadcastRecording = ( - voiceBroadcastRecordingsStore: VoiceBroadcastRecordingsStore, -) => { +export const useCurrentVoiceBroadcastRecording = (voiceBroadcastRecordingsStore: VoiceBroadcastRecordingsStore) => { const [currentVoiceBroadcastRecording, setCurrentVoiceBroadcastRecording] = useState( voiceBroadcastRecordingsStore.getCurrent(), ); diff --git a/src/voice-broadcast/hooks/useHasRoomLiveVoiceBroadcast.ts b/src/voice-broadcast/hooks/useHasRoomLiveVoiceBroadcast.ts index 6db5ed789e..c50f17d7fe 100644 --- a/src/voice-broadcast/hooks/useHasRoomLiveVoiceBroadcast.ts +++ b/src/voice-broadcast/hooks/useHasRoomLiveVoiceBroadcast.ts @@ -23,13 +23,9 @@ import { useTypedEventEmitter } from "../../hooks/useEventEmitter"; export const useHasRoomLiveVoiceBroadcast = (room: Room) => { const [hasLiveVoiceBroadcast, setHasLiveVoiceBroadcast] = useState(hasRoomLiveVoiceBroadcast(room).hasBroadcast); - useTypedEventEmitter( - room.currentState, - RoomStateEvent.Update, - () => { - setHasLiveVoiceBroadcast(hasRoomLiveVoiceBroadcast(room).hasBroadcast); - }, - ); + useTypedEventEmitter(room.currentState, RoomStateEvent.Update, () => { + setHasLiveVoiceBroadcast(hasRoomLiveVoiceBroadcast(room).hasBroadcast); + }); return hasLiveVoiceBroadcast; }; diff --git a/src/voice-broadcast/hooks/useVoiceBroadcastPlayback.ts b/src/voice-broadcast/hooks/useVoiceBroadcastPlayback.ts index adeb19c231..b594c307f6 100644 --- a/src/voice-broadcast/hooks/useVoiceBroadcastPlayback.ts +++ b/src/voice-broadcast/hooks/useVoiceBroadcastPlayback.ts @@ -18,20 +18,14 @@ import { useState } from "react"; import { useTypedEventEmitter } from "../../hooks/useEventEmitter"; import { MatrixClientPeg } from "../../MatrixClientPeg"; -import { - VoiceBroadcastPlayback, - VoiceBroadcastPlaybackEvent, - VoiceBroadcastPlaybackState, -} from ".."; +import { VoiceBroadcastPlayback, VoiceBroadcastPlaybackEvent, VoiceBroadcastPlaybackState } from ".."; export const useVoiceBroadcastPlayback = (playback: VoiceBroadcastPlayback) => { const client = MatrixClientPeg.get(); const room = client.getRoom(playback.infoEvent.getRoomId()); if (!room) { - throw new Error( - `Voice Broadcast room not found (event ${playback.infoEvent.getId()})`, - ); + throw new Error(`Voice Broadcast room not found (event ${playback.infoEvent.getId()})`); } const playbackToggle = () => { @@ -52,18 +46,10 @@ export const useVoiceBroadcastPlayback = (playback: VoiceBroadcastPlayback) => { position: playback.timeSeconds, timeLeft: playback.timeLeftSeconds, }); - useTypedEventEmitter( - playback, - VoiceBroadcastPlaybackEvent.TimesChanged, - t => setTimes(t), - ); + useTypedEventEmitter(playback, VoiceBroadcastPlaybackEvent.TimesChanged, (t) => setTimes(t)); const [liveness, setLiveness] = useState(playback.getLiveness()); - useTypedEventEmitter( - playback, - VoiceBroadcastPlaybackEvent.LivenessChanged, - l => setLiveness(l), - ); + useTypedEventEmitter(playback, VoiceBroadcastPlaybackEvent.LivenessChanged, (l) => setLiveness(l)); return { times, diff --git a/src/voice-broadcast/hooks/useVoiceBroadcastRecording.tsx b/src/voice-broadcast/hooks/useVoiceBroadcastRecording.tsx index d718e274f2..e63c2bfbad 100644 --- a/src/voice-broadcast/hooks/useVoiceBroadcastRecording.tsx +++ b/src/voice-broadcast/hooks/useVoiceBroadcastRecording.tsx @@ -16,11 +16,7 @@ limitations under the License. import React, { useState } from "react"; -import { - VoiceBroadcastInfoState, - VoiceBroadcastRecording, - VoiceBroadcastRecordingEvent, -} from ".."; +import { VoiceBroadcastInfoState, VoiceBroadcastRecording, VoiceBroadcastRecordingEvent } from ".."; import QuestionDialog from "../../components/views/dialogs/QuestionDialog"; import { useTypedEventEmitter } from "../../hooks/useEventEmitter"; import { _t } from "../../languageHandler"; @@ -28,19 +24,18 @@ import { MatrixClientPeg } from "../../MatrixClientPeg"; import Modal from "../../Modal"; const showStopBroadcastingDialog = async (): Promise => { - const { finished } = Modal.createDialog( - QuestionDialog, - { - title: _t("Stop live broadcasting?"), - description: ( -

- { _t("Are you sure you want to stop your live broadcast?" - + "This will end the broadcast and the full recording will be available in the room.") } -

- ), - button: _t("Yes, stop broadcast"), - }, - ); + const { finished } = Modal.createDialog(QuestionDialog, { + title: _t("Stop live broadcasting?"), + description: ( +

+ {_t( + "Are you sure you want to stop your live broadcast?" + + "This will end the broadcast and the full recording will be available in the room.", + )} +

+ ), + button: _t("Yes, stop broadcast"), + }); const [confirmed] = await finished; return confirmed; }; @@ -72,16 +67,9 @@ export const useVoiceBroadcastRecording = (recording: VoiceBroadcastRecording) = ); const [timeLeft, setTimeLeft] = useState(recording.getTimeLeft()); - useTypedEventEmitter( - recording, - VoiceBroadcastRecordingEvent.TimeLeftChanged, - setTimeLeft, - ); + useTypedEventEmitter(recording, VoiceBroadcastRecordingEvent.TimeLeftChanged, setTimeLeft); - const live = [ - VoiceBroadcastInfoState.Started, - VoiceBroadcastInfoState.Resumed, - ].includes(recordingState); + const live = [VoiceBroadcastInfoState.Started, VoiceBroadcastInfoState.Resumed].includes(recordingState); return { live, diff --git a/src/voice-broadcast/models/VoiceBroadcastPlayback.ts b/src/voice-broadcast/models/VoiceBroadcastPlayback.ts index 62ad35628c..57a39b492e 100644 --- a/src/voice-broadcast/models/VoiceBroadcastPlayback.ts +++ b/src/voice-broadcast/models/VoiceBroadcastPlayback.ts @@ -60,14 +60,15 @@ interface EventMap { [VoiceBroadcastPlaybackEvent.LivenessChanged]: (liveness: VoiceBroadcastLiveness) => void; [VoiceBroadcastPlaybackEvent.StateChanged]: ( state: VoiceBroadcastPlaybackState, - playback: VoiceBroadcastPlayback + playback: VoiceBroadcastPlayback, ) => void; [VoiceBroadcastPlaybackEvent.InfoStateChanged]: (state: VoiceBroadcastInfoState) => void; } export class VoiceBroadcastPlayback extends TypedEventEmitter - implements IDestroyable, PlaybackInterface { + implements IDestroyable, PlaybackInterface +{ private state = VoiceBroadcastPlaybackState.Stopped; private chunkEvents = new VoiceBroadcastChunkEvents(); private playbacks = new Map(); @@ -87,10 +88,7 @@ export class VoiceBroadcastPlayback private chunkRelationHelper!: RelationsHelper; private infoRelationHelper!: RelationsHelper; - public constructor( - public readonly infoEvent: MatrixEvent, - private client: MatrixClient, - ) { + public constructor(public readonly infoEvent: MatrixEvent, private client: MatrixClient) { super(); this.addInfoEvent(this.infoEvent); this.infoEvent.on(MatrixEventEvent.BeforeRedaction, this.onBeforeRedaction); @@ -213,13 +211,10 @@ export class VoiceBroadcastPlayback this.playbacks.delete(event.getId()!); } - private onPlaybackPositionUpdate = ( - event: MatrixEvent, - position: number, - ): void => { + private onPlaybackPositionUpdate = (event: MatrixEvent, position: number): void => { if (event !== this.currentlyPlaying) return; - const newPosition = this.chunkEvents.getLengthTo(event) + (position * 1000); // observable sends seconds + const newPosition = this.chunkEvents.getLengthTo(event) + position * 1000; // observable sends seconds // do not jump backwards - this can happen when transiting from one to another chunk if (newPosition < this.position) return; @@ -244,14 +239,11 @@ export class VoiceBroadcastPlayback } private emitTimesChanged(): void { - this.emit( - VoiceBroadcastPlaybackEvent.TimesChanged, - { - duration: this.durationSeconds, - position: this.timeSeconds, - timeLeft: this.timeLeftSeconds, - }, - ); + this.emit(VoiceBroadcastPlaybackEvent.TimesChanged, { + duration: this.durationSeconds, + position: this.timeSeconds, + timeLeft: this.timeLeftSeconds, + }); } private onPlaybackStateChange = async (event: MatrixEvent, newState: PlaybackState): Promise => { @@ -408,9 +400,10 @@ export class VoiceBroadcastPlayback public async start(): Promise { const chunkEvents = this.chunkEvents.getEvents(); - const toPlay = this.getInfoState() === VoiceBroadcastInfoState.Stopped - ? chunkEvents[0] // start at the beginning for an ended voice broadcast - : chunkEvents[chunkEvents.length - 1]; // start at the current chunk for an ongoing voice broadcast + const toPlay = + this.getInfoState() === VoiceBroadcastInfoState.Stopped + ? chunkEvents[0] // start at the beginning for an ended voice broadcast + : chunkEvents[chunkEvents.length - 1]; // start at the current chunk for an ongoing voice broadcast if (toPlay) { return this.playEvent(toPlay); @@ -499,7 +492,7 @@ export class VoiceBroadcastPlayback this.removeAllListeners(); this.chunkEvents = new VoiceBroadcastChunkEvents(); - this.playbacks.forEach(p => p.destroy()); + this.playbacks.forEach((p) => p.destroy()); this.playbacks = new Map(); } } diff --git a/src/voice-broadcast/models/VoiceBroadcastPreRecording.ts b/src/voice-broadcast/models/VoiceBroadcastPreRecording.ts index 10995e5d49..700bda8b8a 100644 --- a/src/voice-broadcast/models/VoiceBroadcastPreRecording.ts +++ b/src/voice-broadcast/models/VoiceBroadcastPreRecording.ts @@ -25,12 +25,13 @@ import { startNewVoiceBroadcastRecording } from "../utils/startNewVoiceBroadcast type VoiceBroadcastPreRecordingEvent = "dismiss"; interface EventMap { - "dismiss": (voiceBroadcastPreRecording: VoiceBroadcastPreRecording) => void; + dismiss: (voiceBroadcastPreRecording: VoiceBroadcastPreRecording) => void; } export class VoiceBroadcastPreRecording extends TypedEventEmitter - implements IDestroyable { + implements IDestroyable +{ public constructor( public room: Room, public sender: RoomMember, @@ -42,12 +43,7 @@ export class VoiceBroadcastPreRecording } public start = async (): Promise => { - await startNewVoiceBroadcastRecording( - this.room, - this.client, - this.playbacksStore, - this.recordingsStore, - ); + await startNewVoiceBroadcastRecording(this.room, this.client, this.playbacksStore, this.recordingsStore); this.emit("dismiss", this); }; diff --git a/src/voice-broadcast/models/VoiceBroadcastRecording.ts b/src/voice-broadcast/models/VoiceBroadcastRecording.ts index bd5d2e5c0d..e0627731eb 100644 --- a/src/voice-broadcast/models/VoiceBroadcastRecording.ts +++ b/src/voice-broadcast/models/VoiceBroadcastRecording.ts @@ -56,7 +56,8 @@ interface EventMap { export class VoiceBroadcastRecording extends TypedEventEmitter - implements IDestroyable { + implements IDestroyable +{ private state: VoiceBroadcastInfoState; private recorder: VoiceBroadcastRecorder; private sequence = 1; @@ -108,8 +109,8 @@ export class VoiceBroadcastRecording private onChunkEvent = (event: MatrixEvent): void => { if ( - (!event.getId() && !event.getTxnId()) - || event.getContent()?.msgtype !== MsgType.Audio // don't add non-audio event + (!event.getId() && !event.getTxnId()) || + event.getContent()?.msgtype !== MsgType.Audio // don't add non-audio event ) { return; } @@ -119,15 +120,19 @@ export class VoiceBroadcastRecording private setInitialStateFromInfoEvent(): void { const room = this.client.getRoom(this.infoEvent.getRoomId()); - const relations = room?.getUnfilteredTimelineSet()?.relations?.getChildEventsForEvent( - this.infoEvent.getId(), - RelationType.Reference, - VoiceBroadcastInfoEventType, - ); + const relations = room + ?.getUnfilteredTimelineSet() + ?.relations?.getChildEventsForEvent( + this.infoEvent.getId(), + RelationType.Reference, + VoiceBroadcastInfoEventType, + ); const relatedEvents = relations?.getRelations(); this.state = !relatedEvents?.find((event: MatrixEvent) => { return event.getContent()?.state === VoiceBroadcastInfoState.Stopped; - }) ? VoiceBroadcastInfoState.Started : VoiceBroadcastInfoState.Stopped; + }) + ? VoiceBroadcastInfoState.Started + : VoiceBroadcastInfoState.Stopped; } public getTimeLeft(): number { @@ -244,12 +249,9 @@ export class VoiceBroadcastRecording return uploadFile( this.client, this.infoEvent.getRoomId(), - new Blob( - [chunk.buffer], - { - type: this.getRecorder().contentType, - }, - ), + new Blob([chunk.buffer], { + type: this.getRecorder().contentType, + }), ); } diff --git a/src/voice-broadcast/stores/VoiceBroadcastPlaybacksStore.ts b/src/voice-broadcast/stores/VoiceBroadcastPlaybacksStore.ts index e34a259379..b49673c4fe 100644 --- a/src/voice-broadcast/stores/VoiceBroadcastPlaybacksStore.ts +++ b/src/voice-broadcast/stores/VoiceBroadcastPlaybacksStore.ts @@ -35,7 +35,8 @@ interface EventMap { */ export class VoiceBroadcastPlaybacksStore extends TypedEventEmitter - implements IDestroyable { + implements IDestroyable +{ private current: VoiceBroadcastPlayback | null; /** Playbacks indexed by their info event id. */ @@ -83,10 +84,7 @@ export class VoiceBroadcastPlaybacksStore playback.on(VoiceBroadcastPlaybackEvent.StateChanged, this.onPlaybackStateChanged); } - private onPlaybackStateChanged = ( - state: VoiceBroadcastPlaybackState, - playback: VoiceBroadcastPlayback, - ): void => { + private onPlaybackStateChanged = (state: VoiceBroadcastPlaybackState, playback: VoiceBroadcastPlayback): void => { switch (state) { case VoiceBroadcastPlaybackState.Buffering: case VoiceBroadcastPlaybackState.Playing: diff --git a/src/voice-broadcast/stores/VoiceBroadcastPreRecordingStore.ts b/src/voice-broadcast/stores/VoiceBroadcastPreRecordingStore.ts index faefea3ddf..13fcd831d1 100644 --- a/src/voice-broadcast/stores/VoiceBroadcastPreRecordingStore.ts +++ b/src/voice-broadcast/stores/VoiceBroadcastPreRecordingStore.ts @@ -27,7 +27,8 @@ interface EventMap { export class VoiceBroadcastPreRecordingStore extends TypedEventEmitter - implements IDestroyable { + implements IDestroyable +{ private current: VoiceBroadcastPreRecording | null = null; public setCurrent(current: VoiceBroadcastPreRecording): void { diff --git a/src/voice-broadcast/utils/VoiceBroadcastChunkEvents.ts b/src/voice-broadcast/utils/VoiceBroadcastChunkEvents.ts index 681166beed..562fb831f1 100644 --- a/src/voice-broadcast/utils/VoiceBroadcastChunkEvents.ts +++ b/src/voice-broadcast/utils/VoiceBroadcastChunkEvents.ts @@ -50,7 +50,7 @@ export class VoiceBroadcastChunkEvents { } public includes(event: MatrixEvent): boolean { - return !!this.events.find(e => this.equalByTxnIdOrId(event, e)); + return !!this.events.find((e) => this.equalByTxnIdOrId(event, e)); } /** @@ -98,20 +98,20 @@ export class VoiceBroadcastChunkEvents { } private calculateChunkLength(event: MatrixEvent): number { - return event.getContent()?.["org.matrix.msc1767.audio"]?.duration - || event.getContent()?.info?.duration - || 0; + return event.getContent()?.["org.matrix.msc1767.audio"]?.duration || event.getContent()?.info?.duration || 0; } private addOrReplaceEvent = (event: MatrixEvent): boolean => { - this.events = this.events.filter(e => !this.equalByTxnIdOrId(event, e)); + this.events = this.events.filter((e) => !this.equalByTxnIdOrId(event, e)); this.events.push(event); return true; }; private equalByTxnIdOrId(eventA: MatrixEvent, eventB: MatrixEvent): boolean { - return eventA.getTxnId() && eventB.getTxnId() && eventA.getTxnId() === eventB.getTxnId() - || eventA.getId() === eventB.getId(); + return ( + (eventA.getTxnId() && eventB.getTxnId() && eventA.getTxnId() === eventB.getTxnId()) || + eventA.getId() === eventB.getId() + ); } /** diff --git a/src/voice-broadcast/utils/VoiceBroadcastResumer.ts b/src/voice-broadcast/utils/VoiceBroadcastResumer.ts index be949d0eab..a46953d815 100644 --- a/src/voice-broadcast/utils/VoiceBroadcastResumer.ts +++ b/src/voice-broadcast/utils/VoiceBroadcastResumer.ts @@ -25,9 +25,7 @@ import { findRoomLiveVoiceBroadcastFromUserAndDevice } from "./findRoomLiveVoice * Handles voice broadcasts on app resume (after logging in, reload, crash…). */ export class VoiceBroadcastResumer implements IDestroyable { - public constructor( - private client: MatrixClient, - ) { + public constructor(private client: MatrixClient) { if (client.isInitialSyncComplete()) { this.resume(); } else { @@ -80,9 +78,10 @@ export class VoiceBroadcastResumer implements IDestroyable { }; // all events should reference the started event - const referencedEventId = infoEvent.getContent()?.state === VoiceBroadcastInfoState.Started - ? infoEvent.getId() - : infoEvent.getContent()?.["m.relates_to"]?.event_id; + const referencedEventId = + infoEvent.getContent()?.state === VoiceBroadcastInfoState.Started + ? infoEvent.getId() + : infoEvent.getContent()?.["m.relates_to"]?.event_id; if (referencedEventId) { content["m.relates_to"] = { diff --git a/src/voice-broadcast/utils/checkVoiceBroadcastPreConditions.tsx b/src/voice-broadcast/utils/checkVoiceBroadcastPreConditions.tsx index a76e6faa31..3fdf8c2440 100644 --- a/src/voice-broadcast/utils/checkVoiceBroadcastPreConditions.tsx +++ b/src/voice-broadcast/utils/checkVoiceBroadcastPreConditions.tsx @@ -25,8 +25,14 @@ import Modal from "../../Modal"; const showAlreadyRecordingDialog = () => { Modal.createDialog(InfoDialog, { title: _t("Can't start a new voice broadcast"), - description:

{ _t("You are already recording a voice broadcast. " - + "Please end your current voice broadcast to start a new one.") }

, + description: ( +

+ {_t( + "You are already recording a voice broadcast. " + + "Please end your current voice broadcast to start a new one.", + )} +

+ ), hasCloseButton: true, }); }; @@ -34,8 +40,14 @@ const showAlreadyRecordingDialog = () => { const showInsufficientPermissionsDialog = () => { Modal.createDialog(InfoDialog, { title: _t("Can't start a new voice broadcast"), - description:

{ _t("You don't have the required permissions to start a voice broadcast in this room. " - + "Contact a room administrator to upgrade your permissions.") }

, + description: ( +

+ {_t( + "You don't have the required permissions to start a voice broadcast in this room. " + + "Contact a room administrator to upgrade your permissions.", + )} +

+ ), hasCloseButton: true, }); }; @@ -43,8 +55,14 @@ const showInsufficientPermissionsDialog = () => { const showOthersAlreadyRecordingDialog = () => { Modal.createDialog(InfoDialog, { title: _t("Can't start a new voice broadcast"), - description:

{ _t("Someone else is already recording a voice broadcast. " - + "Wait for their voice broadcast to end to start a new one.") }

, + description: ( +

+ {_t( + "Someone else is already recording a voice broadcast. " + + "Wait for their voice broadcast to end to start a new one.", + )} +

+ ), hasCloseButton: true, }); }; diff --git a/src/voice-broadcast/utils/getChunkLength.ts b/src/voice-broadcast/utils/getChunkLength.ts index 9eebfe4979..5c8f75a5ce 100644 --- a/src/voice-broadcast/utils/getChunkLength.ts +++ b/src/voice-broadcast/utils/getChunkLength.ts @@ -23,7 +23,5 @@ import SdkConfig, { DEFAULTS } from "../../SdkConfig"; * - If that fails fall back to 120 (two minutes) */ export const getChunkLength = (): number => { - return SdkConfig.get("voice_broadcast")?.chunk_length - || DEFAULTS.voice_broadcast?.chunk_length - || 120; + return SdkConfig.get("voice_broadcast")?.chunk_length || DEFAULTS.voice_broadcast?.chunk_length || 120; }; diff --git a/src/voice-broadcast/utils/getMaxBroadcastLength.ts b/src/voice-broadcast/utils/getMaxBroadcastLength.ts index 15eb83b4a9..2ac930f80a 100644 --- a/src/voice-broadcast/utils/getMaxBroadcastLength.ts +++ b/src/voice-broadcast/utils/getMaxBroadcastLength.ts @@ -23,7 +23,5 @@ import SdkConfig, { DEFAULTS } from "../../SdkConfig"; * - If that fails fall back to four hours */ export const getMaxBroadcastLength = (): number => { - return SdkConfig.get("voice_broadcast")?.max_length - || DEFAULTS.voice_broadcast?.max_length - || 4 * 60 * 60; + return SdkConfig.get("voice_broadcast")?.max_length || DEFAULTS.voice_broadcast?.max_length || 4 * 60 * 60; }; diff --git a/src/voice-broadcast/utils/shouldDisplayAsVoiceBroadcastRecordingTile.ts b/src/voice-broadcast/utils/shouldDisplayAsVoiceBroadcastRecordingTile.ts index b9964b6f2a..c6891f3b77 100644 --- a/src/voice-broadcast/utils/shouldDisplayAsVoiceBroadcastRecordingTile.ts +++ b/src/voice-broadcast/utils/shouldDisplayAsVoiceBroadcastRecordingTile.ts @@ -24,7 +24,5 @@ export const shouldDisplayAsVoiceBroadcastRecordingTile = ( event: MatrixEvent, ): boolean => { const userId = client.getUserId(); - return !!userId - && userId === event.getSender() - && state !== VoiceBroadcastInfoState.Stopped; + return !!userId && userId === event.getSender() && state !== VoiceBroadcastInfoState.Stopped; }; diff --git a/src/voice-broadcast/utils/shouldDisplayAsVoiceBroadcastTile.ts b/src/voice-broadcast/utils/shouldDisplayAsVoiceBroadcastTile.ts index ef55eed3bb..57a0f2f3c6 100644 --- a/src/voice-broadcast/utils/shouldDisplayAsVoiceBroadcastTile.ts +++ b/src/voice-broadcast/utils/shouldDisplayAsVoiceBroadcastTile.ts @@ -18,10 +18,6 @@ import { MatrixEvent } from "matrix-js-sdk/src/matrix"; import { VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from ".."; -export const shouldDisplayAsVoiceBroadcastTile = (event: MatrixEvent) => ( - event.getType?.() === VoiceBroadcastInfoEventType - && ( - event.getContent?.()?.state === VoiceBroadcastInfoState.Started - || event.isRedacted() - ) -); +export const shouldDisplayAsVoiceBroadcastTile = (event: MatrixEvent) => + event.getType?.() === VoiceBroadcastInfoEventType && + (event.getContent?.()?.state === VoiceBroadcastInfoState.Started || event.isRedacted()); diff --git a/src/voice-broadcast/utils/startNewVoiceBroadcastRecording.ts b/src/voice-broadcast/utils/startNewVoiceBroadcastRecording.ts index 5306a9d605..a9cfeea9bb 100644 --- a/src/voice-broadcast/utils/startNewVoiceBroadcastRecording.ts +++ b/src/voice-broadcast/utils/startNewVoiceBroadcastRecording.ts @@ -51,10 +51,7 @@ const startBroadcast = async ( if (voiceBroadcastEvent?.getId() === result.event_id) { room.off(RoomStateEvent.Events, onRoomStateEvents); - const recording = new VoiceBroadcastRecording( - voiceBroadcastEvent, - client, - ); + const recording = new VoiceBroadcastRecording(voiceBroadcastEvent, client); recordingsStore.setCurrent(recording); recording.start(); resolve(recording); diff --git a/src/widgets/CapabilityText.tsx b/src/widgets/CapabilityText.tsx index e4790eaad2..dbc16e004a 100644 --- a/src/widgets/CapabilityText.tsx +++ b/src/widgets/CapabilityText.tsx @@ -21,7 +21,8 @@ import { getTimelineRoomIDFromCapability, isTimelineCapability, isTimelineCapabilityFor, - MatrixCapabilities, Symbols, + MatrixCapabilities, + Symbols, WidgetEventCapability, WidgetKind, } from "matrix-widget-api"; @@ -165,17 +166,27 @@ export class CapabilityText { const roomId = getTimelineRoomIDFromCapability(capability); const room = MatrixClientPeg.get().getRoom(roomId); return { - primary: _t("The above, but in as well", {}, { - Room: () => { - if (room) { - return - { room.name } - ; - } else { - return { roomId }; - } + primary: _t( + "The above, but in as well", + {}, + { + Room: () => { + if (room) { + return ( + + {room.name} + + ); + } else { + return ( + + {roomId} + + ); + } + }, }, - }), + ), }; } } @@ -193,9 +204,10 @@ export class CapabilityText { // See if we have a static line of text to provide for the given event type and // direction. The hope is that we do for common event types for friendlier copy. - const evSendRecv = eventCap.kind === EventKind.State - ? CapabilityText.stateSendRecvCaps - : CapabilityText.nonStateSendRecvCaps; + const evSendRecv = + eventCap.kind === EventKind.State + ? CapabilityText.stateSendRecvCaps + : CapabilityText.nonStateSendRecvCaps; if (evSendRecv[eventCap.eventType]) { const textForKind = evSendRecv[eventCap.eventType]; const textForDirection = textForKind[kind] || textForKind[GENERIC_WIDGET_KIND]; @@ -211,40 +223,57 @@ export class CapabilityText { if (kind === WidgetKind.Room) { if (eventCap.direction === EventDirection.Send) { return { - primary: _t("Send %(eventType)s events as you in this room", { - eventType: eventCap.eventType, - }, { - b: sub => { sub }, - }), + primary: _t( + "Send %(eventType)s events as you in this room", + { + eventType: eventCap.eventType, + }, + { + b: (sub) => {sub}, + }, + ), byline: CapabilityText.bylineFor(eventCap), }; } else { return { - primary: _t("See %(eventType)s events posted to this room", { - eventType: eventCap.eventType, - }, { - b: sub => { sub }, - }), + primary: _t( + "See %(eventType)s events posted to this room", + { + eventType: eventCap.eventType, + }, + { + b: (sub) => {sub}, + }, + ), byline: CapabilityText.bylineFor(eventCap), }; } - } else { // assume generic + } else { + // assume generic if (eventCap.direction === EventDirection.Send) { return { - primary: _t("Send %(eventType)s events as you in your active room", { - eventType: eventCap.eventType, - }, { - b: sub => { sub }, - }), + primary: _t( + "Send %(eventType)s events as you in your active room", + { + eventType: eventCap.eventType, + }, + { + b: (sub) => {sub}, + }, + ), byline: CapabilityText.bylineFor(eventCap), }; } else { return { - primary: _t("See %(eventType)s events posted to your active room", { - eventType: eventCap.eventType, - }, { - b: sub => { sub }, - }), + primary: _t( + "See %(eventType)s events posted to your active room", + { + eventType: eventCap.eventType, + }, + { + b: (sub) => {sub}, + }, + ), byline: CapabilityText.bylineFor(eventCap), }; } @@ -253,9 +282,13 @@ export class CapabilityText { // We don't have enough context to render this capability specially, so we'll present it as-is return { - primary: _t("The %(capability)s capability", { capability }, { - b: sub => { sub }, - }), + primary: _t( + "The %(capability)s capability", + { capability }, + { + b: (sub) => {sub}, + }, + ), }; } @@ -264,15 +297,17 @@ export class CapabilityText { if (!eventCap.keyStr) { if (eventCap.direction === EventDirection.Send) { return { - primary: kind === WidgetKind.Room - ? _t("Send messages as you in this room") - : _t("Send messages as you in your active room"), + primary: + kind === WidgetKind.Room + ? _t("Send messages as you in this room") + : _t("Send messages as you in your active room"), }; } else { return { - primary: kind === WidgetKind.Room - ? _t("See messages posted to this room") - : _t("See messages posted to your active room"), + primary: + kind === WidgetKind.Room + ? _t("See messages posted to this room") + : _t("See messages posted to your active room"), }; } } @@ -283,75 +318,85 @@ export class CapabilityText { case MsgType.Text: { if (eventCap.direction === EventDirection.Send) { return { - primary: kind === WidgetKind.Room - ? _t("Send text messages as you in this room") - : _t("Send text messages as you in your active room"), + primary: + kind === WidgetKind.Room + ? _t("Send text messages as you in this room") + : _t("Send text messages as you in your active room"), }; } else { return { - primary: kind === WidgetKind.Room - ? _t("See text messages posted to this room") - : _t("See text messages posted to your active room"), + primary: + kind === WidgetKind.Room + ? _t("See text messages posted to this room") + : _t("See text messages posted to your active room"), }; } } case MsgType.Emote: { if (eventCap.direction === EventDirection.Send) { return { - primary: kind === WidgetKind.Room - ? _t("Send emotes as you in this room") - : _t("Send emotes as you in your active room"), + primary: + kind === WidgetKind.Room + ? _t("Send emotes as you in this room") + : _t("Send emotes as you in your active room"), }; } else { return { - primary: kind === WidgetKind.Room - ? _t("See emotes posted to this room") - : _t("See emotes posted to your active room"), + primary: + kind === WidgetKind.Room + ? _t("See emotes posted to this room") + : _t("See emotes posted to your active room"), }; } } case MsgType.Image: { if (eventCap.direction === EventDirection.Send) { return { - primary: kind === WidgetKind.Room - ? _t("Send images as you in this room") - : _t("Send images as you in your active room"), + primary: + kind === WidgetKind.Room + ? _t("Send images as you in this room") + : _t("Send images as you in your active room"), }; } else { return { - primary: kind === WidgetKind.Room - ? _t("See images posted to this room") - : _t("See images posted to your active room"), + primary: + kind === WidgetKind.Room + ? _t("See images posted to this room") + : _t("See images posted to your active room"), }; } } case MsgType.Video: { if (eventCap.direction === EventDirection.Send) { return { - primary: kind === WidgetKind.Room - ? _t("Send videos as you in this room") - : _t("Send videos as you in your active room"), + primary: + kind === WidgetKind.Room + ? _t("Send videos as you in this room") + : _t("Send videos as you in your active room"), }; } else { return { - primary: kind === WidgetKind.Room - ? _t("See videos posted to this room") - : _t("See videos posted to your active room"), + primary: + kind === WidgetKind.Room + ? _t("See videos posted to this room") + : _t("See videos posted to your active room"), }; } } case MsgType.File: { if (eventCap.direction === EventDirection.Send) { return { - primary: kind === WidgetKind.Room - ? _t("Send general files as you in this room") - : _t("Send general files as you in your active room"), + primary: + kind === WidgetKind.Room + ? _t("Send general files as you in this room") + : _t("Send general files as you in your active room"), }; } else { return { - primary: kind === WidgetKind.Room - ? _t("See general files posted to this room") - : _t("See general files posted to your active room"), + primary: + kind === WidgetKind.Room + ? _t("See general files posted to this room") + : _t("See general files posted to your active room"), }; } } @@ -359,31 +404,47 @@ export class CapabilityText { let primary: TranslatedString; if (eventCap.direction === EventDirection.Send) { if (kind === WidgetKind.Room) { - primary = _t("Send %(msgtype)s messages as you in this room", { - msgtype: eventCap.keyStr, - }, { - b: sub => { sub }, - }); + primary = _t( + "Send %(msgtype)s messages as you in this room", + { + msgtype: eventCap.keyStr, + }, + { + b: (sub) => {sub}, + }, + ); } else { - primary = _t("Send %(msgtype)s messages as you in your active room", { - msgtype: eventCap.keyStr, - }, { - b: sub => { sub }, - }); + primary = _t( + "Send %(msgtype)s messages as you in your active room", + { + msgtype: eventCap.keyStr, + }, + { + b: (sub) => {sub}, + }, + ); } } else { if (kind === WidgetKind.Room) { - primary = _t("See %(msgtype)s messages posted to this room", { - msgtype: eventCap.keyStr, - }, { - b: sub => { sub }, - }); + primary = _t( + "See %(msgtype)s messages posted to this room", + { + msgtype: eventCap.keyStr, + }, + { + b: (sub) => {sub}, + }, + ); } else { - primary = _t("See %(msgtype)s messages posted to your active room", { - msgtype: eventCap.keyStr, - }, { - b: sub => { sub }, - }); + primary = _t( + "See %(msgtype)s messages posted to your active room", + { + msgtype: eventCap.keyStr, + }, + { + b: (sub) => {sub}, + }, + ); } } return { primary }; diff --git a/src/widgets/Jitsi.ts b/src/widgets/Jitsi.ts index 7d506ace12..d97a03eca2 100644 --- a/src/widgets/Jitsi.ts +++ b/src/widgets/Jitsi.ts @@ -44,7 +44,7 @@ export class Jitsi { * * See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification */ - public async getJitsiAuth(): Promise { + public async getJitsiAuth(): Promise { if (!this.preferredDomain) { return null; } @@ -73,7 +73,7 @@ export class Jitsi { let domain = SdkConfig.getObject("jitsi")?.get("preferred_domain") || "meet.element.io"; logger.log("Attempting to get Jitsi conference information from homeserver"); - const wkPreferredDomain = discoveryResponse?.[JITSI_WK_PROPERTY]?.['preferredDomain']; + const wkPreferredDomain = discoveryResponse?.[JITSI_WK_PROPERTY]?.["preferredDomain"]; if (wkPreferredDomain) domain = wkPreferredDomain; // Put the result into memory for us to use later diff --git a/src/widgets/ManagedHybrid.ts b/src/widgets/ManagedHybrid.ts index 86c035182a..8962dab5c0 100644 --- a/src/widgets/ManagedHybrid.ts +++ b/src/widgets/ManagedHybrid.ts @@ -79,10 +79,7 @@ export async function addManagedHybridWidget(roomId: string) { // Ensure the widget is not already present in the room let widgets = WidgetStore.instance.getApps(roomId); - const existing = ( - widgets.some(w => w.id === widgetId) || - WidgetEchoStore.roomHasPendingWidgets(roomId, []) - ); + const existing = widgets.some((w) => w.id === widgetId) || WidgetEchoStore.roomHasPendingWidgets(roomId, []); if (existing) { logger.error(`Managed hybrid widget already present in room ${roomId}`); return; @@ -101,7 +98,7 @@ export async function addManagedHybridWidget(roomId: string) { return; } widgets = WidgetStore.instance.getApps(roomId); - const installedWidget = widgets.find(w => w.id === widgetId); + const installedWidget = widgets.find((w) => w.id === widgetId); if (!installedWidget) { return; } diff --git a/src/widgets/WidgetType.ts b/src/widgets/WidgetType.ts index e42f3ffa9b..92db57ee71 100644 --- a/src/widgets/WidgetType.ts +++ b/src/widgets/WidgetType.ts @@ -21,8 +21,7 @@ export class WidgetType { public static readonly INTEGRATION_MANAGER = new WidgetType("m.integration_manager", "m.integration_manager"); public static readonly CUSTOM = new WidgetType("m.custom", "m.custom"); - constructor(public readonly preferred: string, public readonly legacy: string) { - } + constructor(public readonly preferred: string, public readonly legacy: string) {} public matches(type: string): boolean { return type === this.preferred || type === this.legacy; @@ -30,8 +29,8 @@ export class WidgetType { static fromString(type: string): WidgetType { // First try and match it against something we're already aware of - const known = Object.values(WidgetType).filter(v => v instanceof WidgetType); - const knownMatch = known.find(w => w.matches(type)); + const known = Object.values(WidgetType).filter((v) => v instanceof WidgetType); + const knownMatch = known.find((w) => w.matches(type)); if (knownMatch) return knownMatch; // If that fails, invent a new widget type diff --git a/test/ContentMessages-test.ts b/test/ContentMessages-test.ts index e0fb6c0a32..e2a4797791 100644 --- a/test/ContentMessages-test.ts +++ b/test/ContentMessages-test.ts @@ -63,24 +63,15 @@ describe("ContentMessages", () => { describe("sendStickerContentToRoom", () => { beforeEach(() => { mocked(client.sendStickerMessage).mockReturnValue(prom); - mocked(doMaybeLocalRoomAction).mockImplementation(( - roomId: string, - fn: (actualRoomId: string) => Promise, - client?: MatrixClient, - ) => { - return fn(roomId); - }); + mocked(doMaybeLocalRoomAction).mockImplementation( + (roomId: string, fn: (actualRoomId: string) => Promise, client?: MatrixClient) => { + return fn(roomId); + }, + ); }); it("should forward the call to doMaybeLocalRoomAction", async () => { - await contentMessages.sendStickerContentToRoom( - stickerUrl, - roomId, - null, - imageInfo, - text, - client, - ); + await contentMessages.sendStickerContentToRoom(stickerUrl, roomId, null, imageInfo, text, client); expect(client.sendStickerMessage).toHaveBeenCalledWith(roomId, null, stickerUrl, imageInfo, text); }); }); @@ -88,22 +79,25 @@ describe("ContentMessages", () => { describe("sendContentToRoom", () => { const roomId = "!roomId:server"; beforeEach(() => { - Object.defineProperty(global.Image.prototype, 'src', { + Object.defineProperty(global.Image.prototype, "src", { // Define the property setter set(src) { window.setTimeout(() => this.onload()); }, }); - Object.defineProperty(global.Image.prototype, 'height', { - get() { return 600; }, + Object.defineProperty(global.Image.prototype, "height", { + get() { + return 600; + }, }); - Object.defineProperty(global.Image.prototype, 'width', { - get() { return 800; }, + Object.defineProperty(global.Image.prototype, "width", { + get() { + return 800; + }, }); - mocked(doMaybeLocalRoomAction).mockImplementation(( - roomId: string, - fn: (actualRoomId: string) => Promise, - ) => fn(roomId)); + mocked(doMaybeLocalRoomAction).mockImplementation( + (roomId: string, fn: (actualRoomId: string) => Promise) => fn(roomId), + ); mocked(BlurhashEncoder.instance.getBlurhash).mockResolvedValue(undefined); }); @@ -111,34 +105,46 @@ describe("ContentMessages", () => { mocked(client.uploadContent).mockResolvedValue({ content_uri: "mxc://server/file" }); const file = new File([], "fileName", { type: "image/jpeg" }); await contentMessages.sendContentToRoom(file, roomId, undefined, client, undefined); - expect(client.sendMessage).toHaveBeenCalledWith(roomId, null, expect.objectContaining({ - url: "mxc://server/file", - msgtype: "m.image", - })); + expect(client.sendMessage).toHaveBeenCalledWith( + roomId, + null, + expect.objectContaining({ + url: "mxc://server/file", + msgtype: "m.image", + }), + ); }); it("should fall back to m.file for invalid image files", async () => { mocked(client.uploadContent).mockResolvedValue({ content_uri: "mxc://server/file" }); const file = new File([], "fileName", { type: "image/png" }); await contentMessages.sendContentToRoom(file, roomId, undefined, client, undefined); - expect(client.sendMessage).toHaveBeenCalledWith(roomId, null, expect.objectContaining({ - url: "mxc://server/file", - msgtype: "m.file", - })); + expect(client.sendMessage).toHaveBeenCalledWith( + roomId, + null, + expect.objectContaining({ + url: "mxc://server/file", + msgtype: "m.file", + }), + ); }); it("should use m.video for video files", async () => { - jest.spyOn(document, "createElement").mockImplementation(tagName => { + jest.spyOn(document, "createElement").mockImplementation((tagName) => { const element = createElement(tagName); if (tagName === "video") { (element).load = jest.fn(); (element).play = () => element.onloadeddata(new Event("loadeddata")); (element).pause = jest.fn(); - Object.defineProperty(element, 'videoHeight', { - get() { return 600; }, + Object.defineProperty(element, "videoHeight", { + get() { + return 600; + }, }); - Object.defineProperty(element, 'videoWidth', { - get() { return 800; }, + Object.defineProperty(element, "videoWidth", { + get() { + return 800; + }, }); } return element; @@ -147,31 +153,43 @@ describe("ContentMessages", () => { mocked(client.uploadContent).mockResolvedValue({ content_uri: "mxc://server/file" }); const file = new File([], "fileName", { type: "video/mp4" }); await contentMessages.sendContentToRoom(file, roomId, undefined, client, undefined); - expect(client.sendMessage).toHaveBeenCalledWith(roomId, null, expect.objectContaining({ - url: "mxc://server/file", - msgtype: "m.video", - })); + expect(client.sendMessage).toHaveBeenCalledWith( + roomId, + null, + expect.objectContaining({ + url: "mxc://server/file", + msgtype: "m.video", + }), + ); }); it("should use m.audio for audio files", async () => { mocked(client.uploadContent).mockResolvedValue({ content_uri: "mxc://server/file" }); const file = new File([], "fileName", { type: "audio/mp3" }); await contentMessages.sendContentToRoom(file, roomId, undefined, client, undefined); - expect(client.sendMessage).toHaveBeenCalledWith(roomId, null, expect.objectContaining({ - url: "mxc://server/file", - msgtype: "m.audio", - })); + expect(client.sendMessage).toHaveBeenCalledWith( + roomId, + null, + expect.objectContaining({ + url: "mxc://server/file", + msgtype: "m.audio", + }), + ); }); it("should default to name 'Attachment' if file doesn't have a name", async () => { mocked(client.uploadContent).mockResolvedValue({ content_uri: "mxc://server/file" }); const file = new File([], "", { type: "text/plain" }); await contentMessages.sendContentToRoom(file, roomId, undefined, client, undefined); - expect(client.sendMessage).toHaveBeenCalledWith(roomId, null, expect.objectContaining({ - url: "mxc://server/file", - msgtype: "m.file", - body: "Attachment", - })); + expect(client.sendMessage).toHaveBeenCalledWith( + roomId, + null, + expect.objectContaining({ + url: "mxc://server/file", + msgtype: "m.file", + body: "Attachment", + }), + ); }); it("should keep RoomUpload's total and loaded values up to date", async () => { @@ -196,10 +214,9 @@ describe("ContentMessages", () => { const roomId = "!roomId:server"; beforeEach(() => { - mocked(doMaybeLocalRoomAction).mockImplementation(( - roomId: string, - fn: (actualRoomId: string) => Promise, - ) => fn(roomId)); + mocked(doMaybeLocalRoomAction).mockImplementation( + (roomId: string, fn: (actualRoomId: string) => Promise) => fn(roomId), + ); }); it("should return only uploads for the given relation", async () => { @@ -284,14 +301,19 @@ describe("uploadFile", () => { const res = await uploadFile(client, "!roomId:server", file, progressHandler); expect(res.url).toBeFalsy(); - expect(res.file).toEqual(expect.objectContaining({ - url: "mxc://server/file", - })); + expect(res.file).toEqual( + expect.objectContaining({ + url: "mxc://server/file", + }), + ); expect(encrypt.encryptAttachment).toHaveBeenCalled(); - expect(client.uploadContent).toHaveBeenCalledWith(expect.any(Blob), expect.objectContaining({ - progressHandler, - includeFilename: false, - })); + expect(client.uploadContent).toHaveBeenCalledWith( + expect.any(Blob), + expect.objectContaining({ + progressHandler, + includeFilename: false, + }), + ); expect(mocked(client.uploadContent).mock.calls[0][0]).not.toBe(file); }); diff --git a/test/DecryptionFailureTracker-test.js b/test/DecryptionFailureTracker-test.js index 8572365f08..14f3a67b61 100644 --- a/test/DecryptionFailureTracker-test.js +++ b/test/DecryptionFailureTracker-test.js @@ -14,15 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MatrixEvent } from 'matrix-js-sdk/src/matrix'; +import { MatrixEvent } from "matrix-js-sdk/src/matrix"; -import { DecryptionFailureTracker } from '../src/DecryptionFailureTracker'; +import { DecryptionFailureTracker } from "../src/DecryptionFailureTracker"; class MockDecryptionError extends Error { constructor(code) { super(); - this.code = code || 'MOCK_DECRYPTION_ERROR'; + this.code = code || "MOCK_DECRYPTION_ERROR"; } } @@ -37,12 +37,15 @@ function createFailedDecryptionEvent() { return event; } -describe('DecryptionFailureTracker', function() { - it('tracks a failed decryption for a visible event', function(done) { +describe("DecryptionFailureTracker", function () { + it("tracks a failed decryption for a visible event", function (done) { const failedDecryptionEvent = createFailedDecryptionEvent(); let count = 0; - const tracker = new DecryptionFailureTracker((total) => count += total, () => "UnknownError"); + const tracker = new DecryptionFailureTracker( + (total) => (count += total), + () => "UnknownError", + ); tracker.addVisibleEvent(failedDecryptionEvent); @@ -55,24 +58,27 @@ describe('DecryptionFailureTracker', function() { // Immediately track the newest failures tracker.trackFailures(); - expect(count).not.toBe(0, 'should track a failure for an event that failed decryption'); + expect(count).not.toBe(0, "should track a failure for an event that failed decryption"); done(); }); - it('tracks a failed decryption with expected raw error for a visible event', function(done) { + it("tracks a failed decryption with expected raw error for a visible event", function (done) { const failedDecryptionEvent = createFailedDecryptionEvent(); let count = 0; let reportedRawCode = ""; - const tracker = new DecryptionFailureTracker((total, errcode, rawCode) => { - count += total; - reportedRawCode = rawCode; - }, () => "UnknownError"); + const tracker = new DecryptionFailureTracker( + (total, errcode, rawCode) => { + count += total; + reportedRawCode = rawCode; + }, + () => "UnknownError", + ); tracker.addVisibleEvent(failedDecryptionEvent); - const err = new MockDecryptionError('INBOUND_SESSION_MISMATCH_ROOM_ID'); + const err = new MockDecryptionError("INBOUND_SESSION_MISMATCH_ROOM_ID"); tracker.eventDecrypted(failedDecryptionEvent, err); // Pretend "now" is Infinity @@ -81,17 +87,20 @@ describe('DecryptionFailureTracker', function() { // Immediately track the newest failures tracker.trackFailures(); - expect(count).not.toBe(0, 'should track a failure for an event that failed decryption'); - expect(reportedRawCode).toBe('INBOUND_SESSION_MISMATCH_ROOM_ID', 'Should add the rawCode to the event context'); + expect(count).not.toBe(0, "should track a failure for an event that failed decryption"); + expect(reportedRawCode).toBe("INBOUND_SESSION_MISMATCH_ROOM_ID", "Should add the rawCode to the event context"); done(); }); - it('tracks a failed decryption for an event that becomes visible later', function(done) { + it("tracks a failed decryption for an event that becomes visible later", function (done) { const failedDecryptionEvent = createFailedDecryptionEvent(); let count = 0; - const tracker = new DecryptionFailureTracker((total) => count += total, () => "UnknownError"); + const tracker = new DecryptionFailureTracker( + (total) => (count += total), + () => "UnknownError", + ); const err = new MockDecryptionError(); tracker.eventDecrypted(failedDecryptionEvent, err); @@ -104,16 +113,19 @@ describe('DecryptionFailureTracker', function() { // Immediately track the newest failures tracker.trackFailures(); - expect(count).not.toBe(0, 'should track a failure for an event that failed decryption'); + expect(count).not.toBe(0, "should track a failure for an event that failed decryption"); done(); }); - it('does not track a failed decryption for an event that never becomes visible', function(done) { + it("does not track a failed decryption for an event that never becomes visible", function (done) { const failedDecryptionEvent = createFailedDecryptionEvent(); let count = 0; - const tracker = new DecryptionFailureTracker((total) => count += total, () => "UnknownError"); + const tracker = new DecryptionFailureTracker( + (total) => (count += total), + () => "UnknownError", + ); const err = new MockDecryptionError(); tracker.eventDecrypted(failedDecryptionEvent, err); @@ -124,16 +136,19 @@ describe('DecryptionFailureTracker', function() { // Immediately track the newest failures tracker.trackFailures(); - expect(count).toBe(0, 'should not track a failure for an event that never became visible'); + expect(count).toBe(0, "should not track a failure for an event that never became visible"); done(); }); - it('does not track a failed decryption where the event is subsequently successfully decrypted', (done) => { + it("does not track a failed decryption where the event is subsequently successfully decrypted", (done) => { const decryptedEvent = createFailedDecryptionEvent(); - const tracker = new DecryptionFailureTracker((total) => { - expect(true).toBe(false, 'should not track an event that has since been decrypted correctly'); - }, () => "UnknownError"); + const tracker = new DecryptionFailureTracker( + (total) => { + expect(true).toBe(false, "should not track an event that has since been decrypted correctly"); + }, + () => "UnknownError", + ); tracker.addVisibleEvent(decryptedEvent); @@ -152,36 +167,45 @@ describe('DecryptionFailureTracker', function() { done(); }); - it('does not track a failed decryption where the event is subsequently successfully decrypted ' + - 'and later becomes visible', (done) => { - const decryptedEvent = createFailedDecryptionEvent(); - const tracker = new DecryptionFailureTracker((total) => { - expect(true).toBe(false, 'should not track an event that has since been decrypted correctly'); - }, () => "UnknownError"); + it( + "does not track a failed decryption where the event is subsequently successfully decrypted " + + "and later becomes visible", + (done) => { + const decryptedEvent = createFailedDecryptionEvent(); + const tracker = new DecryptionFailureTracker( + (total) => { + expect(true).toBe(false, "should not track an event that has since been decrypted correctly"); + }, + () => "UnknownError", + ); - const err = new MockDecryptionError(); - tracker.eventDecrypted(decryptedEvent, err); + const err = new MockDecryptionError(); + tracker.eventDecrypted(decryptedEvent, err); - // Indicate successful decryption: clear data can be anything where the msgtype is not m.bad.encrypted - decryptedEvent.setClearData({}); - tracker.eventDecrypted(decryptedEvent, null); + // Indicate successful decryption: clear data can be anything where the msgtype is not m.bad.encrypted + decryptedEvent.setClearData({}); + tracker.eventDecrypted(decryptedEvent, null); - tracker.addVisibleEvent(decryptedEvent); + tracker.addVisibleEvent(decryptedEvent); - // Pretend "now" is Infinity - tracker.checkFailures(Infinity); + // Pretend "now" is Infinity + tracker.checkFailures(Infinity); - // Immediately track the newest failures - tracker.trackFailures(); - done(); - }); + // Immediately track the newest failures + tracker.trackFailures(); + done(); + }, + ); - it('only tracks a single failure per event, despite multiple failed decryptions for multiple events', (done) => { + it("only tracks a single failure per event, despite multiple failed decryptions for multiple events", (done) => { const decryptedEvent = createFailedDecryptionEvent(); const decryptedEvent2 = createFailedDecryptionEvent(); let count = 0; - const tracker = new DecryptionFailureTracker((total) => count += total, () => "UnknownError"); + const tracker = new DecryptionFailureTracker( + (total) => (count += total), + () => "UnknownError", + ); tracker.addVisibleEvent(decryptedEvent); @@ -206,16 +230,19 @@ describe('DecryptionFailureTracker', function() { tracker.trackFailures(); tracker.trackFailures(); - expect(count).toBe(2, count + ' failures tracked, should only track a single failure per event'); + expect(count).toBe(2, count + " failures tracked, should only track a single failure per event"); done(); }); - it('should not track a failure for an event that was tracked previously', (done) => { + it("should not track a failure for an event that was tracked previously", (done) => { const decryptedEvent = createFailedDecryptionEvent(); let count = 0; - const tracker = new DecryptionFailureTracker((total) => count += total, () => "UnknownError"); + const tracker = new DecryptionFailureTracker( + (total) => (count += total), + () => "UnknownError", + ); tracker.addVisibleEvent(decryptedEvent); @@ -233,19 +260,22 @@ describe('DecryptionFailureTracker', function() { tracker.trackFailures(); - expect(count).toBe(1, 'should only track a single failure per event'); + expect(count).toBe(1, "should only track a single failure per event"); done(); }); - xit('should not track a failure for an event that was tracked in a previous session', (done) => { + xit("should not track a failure for an event that was tracked in a previous session", (done) => { // This test uses localStorage, clear it beforehand localStorage.clear(); const decryptedEvent = createFailedDecryptionEvent(); let count = 0; - const tracker = new DecryptionFailureTracker((total) => count += total, () => "UnknownError"); + const tracker = new DecryptionFailureTracker( + (total) => (count += total), + () => "UnknownError", + ); tracker.addVisibleEvent(decryptedEvent); @@ -260,7 +290,10 @@ describe('DecryptionFailureTracker', function() { tracker.trackFailures(); // Simulate the browser refreshing by destroying tracker and creating a new tracker - const secondTracker = new DecryptionFailureTracker((total) => count += total, () => "UnknownError"); + const secondTracker = new DecryptionFailureTracker( + (total) => (count += total), + () => "UnknownError", + ); secondTracker.addVisibleEvent(decryptedEvent); @@ -270,24 +303,24 @@ describe('DecryptionFailureTracker', function() { secondTracker.checkFailures(Infinity); secondTracker.trackFailures(); - expect(count).toBe(1, count + ' failures tracked, should only track a single failure per event'); + expect(count).toBe(1, count + " failures tracked, should only track a single failure per event"); done(); }); - it('should count different error codes separately for multiple failures with different error codes', () => { + it("should count different error codes separately for multiple failures with different error codes", () => { const counts = {}; const tracker = new DecryptionFailureTracker( - (total, errorCode) => counts[errorCode] = (counts[errorCode] || 0) + total, - (error) => error === "UnknownError" ? "UnknownError" : "OlmKeysNotSentError", + (total, errorCode) => (counts[errorCode] = (counts[errorCode] || 0) + total), + (error) => (error === "UnknownError" ? "UnknownError" : "OlmKeysNotSentError"), ); const decryptedEvent1 = createFailedDecryptionEvent(); const decryptedEvent2 = createFailedDecryptionEvent(); const decryptedEvent3 = createFailedDecryptionEvent(); - const error1 = new MockDecryptionError('UnknownError'); - const error2 = new MockDecryptionError('OlmKeysNotSentError'); + const error1 = new MockDecryptionError("UnknownError"); + const error2 = new MockDecryptionError("OlmKeysNotSentError"); tracker.addVisibleEvent(decryptedEvent1); tracker.addVisibleEvent(decryptedEvent2); @@ -305,23 +338,23 @@ describe('DecryptionFailureTracker', function() { tracker.trackFailures(); //expect(counts['UnknownError']).toBe(1, 'should track one UnknownError'); - expect(counts['OlmKeysNotSentError']).toBe(2, 'should track two OlmKeysNotSentError'); + expect(counts["OlmKeysNotSentError"]).toBe(2, "should track two OlmKeysNotSentError"); }); - it('should aggregate error codes correctly', () => { + it("should aggregate error codes correctly", () => { const counts = {}; const tracker = new DecryptionFailureTracker( - (total, errorCode) => counts[errorCode] = (counts[errorCode] || 0) + total, - (errorCode) => 'OlmUnspecifiedError', + (total, errorCode) => (counts[errorCode] = (counts[errorCode] || 0) + total), + (errorCode) => "OlmUnspecifiedError", ); const decryptedEvent1 = createFailedDecryptionEvent(); const decryptedEvent2 = createFailedDecryptionEvent(); const decryptedEvent3 = createFailedDecryptionEvent(); - const error1 = new MockDecryptionError('ERROR_CODE_1'); - const error2 = new MockDecryptionError('ERROR_CODE_2'); - const error3 = new MockDecryptionError('ERROR_CODE_3'); + const error1 = new MockDecryptionError("ERROR_CODE_1"); + const error2 = new MockDecryptionError("ERROR_CODE_2"); + const error3 = new MockDecryptionError("ERROR_CODE_3"); tracker.addVisibleEvent(decryptedEvent1); tracker.addVisibleEvent(decryptedEvent2); @@ -336,20 +369,22 @@ describe('DecryptionFailureTracker', function() { tracker.trackFailures(); - expect(counts['OlmUnspecifiedError']) - .toBe(3, 'should track three OlmUnspecifiedError, got ' + counts['OlmUnspecifiedError']); + expect(counts["OlmUnspecifiedError"]).toBe( + 3, + "should track three OlmUnspecifiedError, got " + counts["OlmUnspecifiedError"], + ); }); - it('should remap error codes correctly', () => { + it("should remap error codes correctly", () => { const counts = {}; const tracker = new DecryptionFailureTracker( - (total, errorCode) => counts[errorCode] = (counts[errorCode] || 0) + total, - (errorCode) => Array.from(errorCode).reverse().join(''), + (total, errorCode) => (counts[errorCode] = (counts[errorCode] || 0) + total), + (errorCode) => Array.from(errorCode).reverse().join(""), ); const decryptedEvent = createFailedDecryptionEvent(); - const error = new MockDecryptionError('ERROR_CODE_1'); + const error = new MockDecryptionError("ERROR_CODE_1"); tracker.addVisibleEvent(decryptedEvent); @@ -360,7 +395,6 @@ describe('DecryptionFailureTracker', function() { tracker.trackFailures(); - expect(counts['1_EDOC_RORRE']) - .toBe(1, 'should track remapped error code'); + expect(counts["1_EDOC_RORRE"]).toBe(1, "should track remapped error code"); }); }); diff --git a/test/DeviceListener-test.ts b/test/DeviceListener-test.ts index 20adbfd45d..99c413938c 100644 --- a/test/DeviceListener-test.ts +++ b/test/DeviceListener-test.ts @@ -1,4 +1,3 @@ - /* Copyright 2022 The Matrix.org Foundation C.I.C. @@ -46,34 +45,35 @@ jest.mock("../src/dispatcher/dispatcher", () => ({ })); jest.mock("../src/SecurityManager", () => ({ - isSecretStorageBeingAccessed: jest.fn(), accessSecretStorage: jest.fn(), + isSecretStorageBeingAccessed: jest.fn(), + accessSecretStorage: jest.fn(), })); jest.mock("../src/utils/device/snoozeBulkUnverifiedDeviceReminder", () => ({ isBulkUnverifiedDeviceReminderSnoozed: jest.fn(), })); -const userId = '@user:server'; -const deviceId = 'my-device-id'; +const userId = "@user:server"; +const deviceId = "my-device-id"; const mockDispatcher = mocked(dis); const flushPromises = async () => await new Promise(process.nextTick); -describe('DeviceListener', () => { +describe("DeviceListener", () => { let mockClient: Mocked | undefined; // spy on various toasts' hide and show functions // easier than mocking - jest.spyOn(SetupEncryptionToast, 'showToast'); - jest.spyOn(SetupEncryptionToast, 'hideToast'); - jest.spyOn(BulkUnverifiedSessionsToast, 'showToast'); - jest.spyOn(BulkUnverifiedSessionsToast, 'hideToast'); - jest.spyOn(UnverifiedSessionToast, 'showToast'); - jest.spyOn(UnverifiedSessionToast, 'hideToast'); + jest.spyOn(SetupEncryptionToast, "showToast"); + jest.spyOn(SetupEncryptionToast, "hideToast"); + jest.spyOn(BulkUnverifiedSessionsToast, "showToast"); + jest.spyOn(BulkUnverifiedSessionsToast, "hideToast"); + jest.spyOn(UnverifiedSessionToast, "showToast"); + jest.spyOn(UnverifiedSessionToast, "hideToast"); beforeEach(() => { jest.resetAllMocks(); mockPlatformPeg({ - getAppVersion: jest.fn().mockResolvedValue('1.2.3'), + getAppVersion: jest.fn().mockResolvedValue("1.2.3"), }); mockClient = getMockClientWithEventEmitter({ isGuest: jest.fn(), @@ -98,8 +98,8 @@ describe('DeviceListener', () => { getAccountData: jest.fn(), checkDeviceTrust: jest.fn().mockReturnValue(new DeviceTrustLevel(false, false, false, false)), }); - jest.spyOn(MatrixClientPeg, 'get').mockReturnValue(mockClient); - jest.spyOn(SettingsStore, 'getValue').mockReturnValue(false); + jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient); + jest.spyOn(SettingsStore, "getValue").mockReturnValue(false); mocked(isBulkUnverifiedDeviceReminderSnoozed).mockClear().mockReturnValue(false); }); @@ -110,51 +110,46 @@ describe('DeviceListener', () => { return instance; }; - describe('client information', () => { - it('watches device client information setting', async () => { - const watchSettingSpy = jest.spyOn(SettingsStore, 'watchSetting'); - const unwatchSettingSpy = jest.spyOn(SettingsStore, 'unwatchSetting'); + describe("client information", () => { + it("watches device client information setting", async () => { + const watchSettingSpy = jest.spyOn(SettingsStore, "watchSetting"); + const unwatchSettingSpy = jest.spyOn(SettingsStore, "unwatchSetting"); const deviceListener = await createAndStart(); - expect(watchSettingSpy).toHaveBeenCalledWith( - 'deviceClientInformationOptIn', null, expect.any(Function), - ); + expect(watchSettingSpy).toHaveBeenCalledWith("deviceClientInformationOptIn", null, expect.any(Function)); deviceListener.stop(); expect(unwatchSettingSpy).toHaveBeenCalled(); }); - describe('when device client information feature is enabled', () => { + describe("when device client information feature is enabled", () => { beforeEach(() => { - jest.spyOn(SettingsStore, 'getValue').mockImplementation( - settingName => settingName === 'deviceClientInformationOptIn', + jest.spyOn(SettingsStore, "getValue").mockImplementation( + (settingName) => settingName === "deviceClientInformationOptIn", ); }); - it('saves client information on start', async () => { + it("saves client information on start", async () => { await createAndStart(); expect(mockClient!.setAccountData).toHaveBeenCalledWith( `io.element.matrix_client_information.${deviceId}`, - { name: 'Element', url: 'localhost', version: '1.2.3' }, + { name: "Element", url: "localhost", version: "1.2.3" }, ); }); - it('catches error and logs when saving client information fails', async () => { - const errorLogSpy = jest.spyOn(logger, 'error'); - const error = new Error('oups'); + it("catches error and logs when saving client information fails", async () => { + const errorLogSpy = jest.spyOn(logger, "error"); + const error = new Error("oups"); mockClient!.setAccountData.mockRejectedValue(error); // doesn't throw await createAndStart(); - expect(errorLogSpy).toHaveBeenCalledWith( - 'Failed to update client information', - error, - ); + expect(errorLogSpy).toHaveBeenCalledWith("Failed to update client information", error); }); - it('saves client information on logged in action', async () => { + it("saves client information on logged in action", async () => { const instance = await createAndStart(); mockClient!.setAccountData.mockClear(); @@ -166,29 +161,30 @@ describe('DeviceListener', () => { expect(mockClient!.setAccountData).toHaveBeenCalledWith( `io.element.matrix_client_information.${deviceId}`, - { name: 'Element', url: 'localhost', version: '1.2.3' }, + { name: "Element", url: "localhost", version: "1.2.3" }, ); }); }); - describe('when device client information feature is disabled', () => { - const clientInfoEvent = new MatrixEvent({ type: `io.element.matrix_client_information.${deviceId}`, - content: { name: 'hello' }, + describe("when device client information feature is disabled", () => { + const clientInfoEvent = new MatrixEvent({ + type: `io.element.matrix_client_information.${deviceId}`, + content: { name: "hello" }, }); const emptyClientInfoEvent = new MatrixEvent({ type: `io.element.matrix_client_information.${deviceId}` }); beforeEach(() => { - jest.spyOn(SettingsStore, 'getValue').mockReturnValue(false); + jest.spyOn(SettingsStore, "getValue").mockReturnValue(false); mockClient!.getAccountData.mockReturnValue(undefined); }); - it('does not save client information on start', async () => { + it("does not save client information on start", async () => { await createAndStart(); expect(mockClient!.setAccountData).not.toHaveBeenCalled(); }); - it('removes client information on start if it exists', async () => { + it("removes client information on start if it exists", async () => { mockClient!.getAccountData.mockReturnValue(clientInfoEvent); await createAndStart(); @@ -198,14 +194,14 @@ describe('DeviceListener', () => { ); }); - it('does not try to remove client info event that are already empty', async () => { + it("does not try to remove client info event that are already empty", async () => { mockClient!.getAccountData.mockReturnValue(emptyClientInfoEvent); await createAndStart(); expect(mockClient!.setAccountData).not.toHaveBeenCalled(); }); - it('does not save client information on logged in action', async () => { + it("does not save client information on logged in action", async () => { const instance = await createAndStart(); // @ts-ignore calling private function @@ -216,51 +212,48 @@ describe('DeviceListener', () => { expect(mockClient!.setAccountData).not.toHaveBeenCalled(); }); - it('saves client information after setting is enabled', async () => { - const watchSettingSpy = jest.spyOn(SettingsStore, 'watchSetting'); + it("saves client information after setting is enabled", async () => { + const watchSettingSpy = jest.spyOn(SettingsStore, "watchSetting"); await createAndStart(); const [settingName, roomId, callback] = watchSettingSpy.mock.calls[0]; - expect(settingName).toEqual('deviceClientInformationOptIn'); + expect(settingName).toEqual("deviceClientInformationOptIn"); expect(roomId).toBeNull(); - callback('deviceClientInformationOptIn', null, SettingLevel.DEVICE, SettingLevel.DEVICE, true); + callback("deviceClientInformationOptIn", null, SettingLevel.DEVICE, SettingLevel.DEVICE, true); await flushPromises(); expect(mockClient!.setAccountData).toHaveBeenCalledWith( `io.element.matrix_client_information.${deviceId}`, - { name: 'Element', url: 'localhost', version: '1.2.3' }, + { name: "Element", url: "localhost", version: "1.2.3" }, ); }); }); }); - describe('recheck', () => { - it('does nothing when cross signing feature is not supported', async () => { + describe("recheck", () => { + it("does nothing when cross signing feature is not supported", async () => { mockClient!.doesServerSupportUnstableFeature.mockResolvedValue(false); await createAndStart(); expect(mockClient!.isCrossSigningReady).not.toHaveBeenCalled(); }); - it('does nothing when crypto is not enabled', async () => { + it("does nothing when crypto is not enabled", async () => { mockClient!.isCryptoEnabled.mockReturnValue(false); await createAndStart(); expect(mockClient!.isCrossSigningReady).not.toHaveBeenCalled(); }); - it('does nothing when initial sync is not complete', async () => { + it("does nothing when initial sync is not complete", async () => { mockClient!.isInitialSyncComplete.mockReturnValue(false); await createAndStart(); expect(mockClient!.isCrossSigningReady).not.toHaveBeenCalled(); }); - describe('set up encryption', () => { - const rooms = [ - { roomId: '!room1' }, - { roomId: '!room2' }, - ] as unknown as Room[]; + describe("set up encryption", () => { + const rooms = [{ roomId: "!room1" }, { roomId: "!room2" }] as unknown as Room[]; beforeEach(() => { mockClient!.isCrossSigningReady.mockResolvedValue(false); @@ -269,21 +262,21 @@ describe('DeviceListener', () => { mockClient!.isRoomEncrypted.mockReturnValue(true); }); - it('hides setup encryption toast when cross signing and secret storage are ready', async () => { + it("hides setup encryption toast when cross signing and secret storage are ready", async () => { mockClient!.isCrossSigningReady.mockResolvedValue(true); mockClient!.isSecretStorageReady.mockResolvedValue(true); await createAndStart(); expect(SetupEncryptionToast.hideToast).toHaveBeenCalled(); }); - it('hides setup encryption toast when it is dismissed', async () => { + it("hides setup encryption toast when it is dismissed", async () => { const instance = await createAndStart(); instance.dismissEncryptionSetup(); await flushPromises(); expect(SetupEncryptionToast.hideToast).toHaveBeenCalled(); }); - it('does not do any checks or show any toasts when secret storage is being accessed', async () => { + it("does not do any checks or show any toasts when secret storage is being accessed", async () => { mocked(isSecretStorageBeingAccessed).mockReturnValue(true); await createAndStart(); @@ -291,7 +284,7 @@ describe('DeviceListener', () => { expect(SetupEncryptionToast.showToast).not.toHaveBeenCalled(); }); - it('does not do any checks or show any toasts when no rooms are encrypted', async () => { + it("does not do any checks or show any toasts when no rooms are encrypted", async () => { mockClient!.isRoomEncrypted.mockReturnValue(false); await createAndStart(); @@ -299,21 +292,22 @@ describe('DeviceListener', () => { expect(SetupEncryptionToast.showToast).not.toHaveBeenCalled(); }); - describe('when user does not have a cross signing id on this device', () => { + describe("when user does not have a cross signing id on this device", () => { beforeEach(() => { mockClient!.getCrossSigningId.mockReturnValue(null); }); - it('shows verify session toast when account has cross signing', async () => { + it("shows verify session toast when account has cross signing", async () => { mockClient!.getStoredCrossSigningForUser.mockReturnValue(new CrossSigningInfo(userId)); await createAndStart(); expect(mockClient!.downloadKeys).toHaveBeenCalled(); expect(SetupEncryptionToast.showToast).toHaveBeenCalledWith( - SetupEncryptionToast.Kind.VERIFY_THIS_SESSION); + SetupEncryptionToast.Kind.VERIFY_THIS_SESSION, + ); }); - it('checks key backup status when when account has cross signing', async () => { + it("checks key backup status when when account has cross signing", async () => { mockClient!.getCrossSigningId.mockReturnValue(null); mockClient!.getStoredCrossSigningForUser.mockReturnValue(new CrossSigningInfo(userId)); await createAndStart(); @@ -322,31 +316,32 @@ describe('DeviceListener', () => { }); }); - describe('when user does have a cross signing id on this device', () => { + describe("when user does have a cross signing id on this device", () => { beforeEach(() => { - mockClient!.getCrossSigningId.mockReturnValue('abc'); + mockClient!.getCrossSigningId.mockReturnValue("abc"); }); - it('shows upgrade encryption toast when user has a key backup available', async () => { + it("shows upgrade encryption toast when user has a key backup available", async () => { // non falsy response mockClient!.getKeyBackupVersion.mockResolvedValue({} as unknown as IKeyBackupInfo); await createAndStart(); expect(SetupEncryptionToast.showToast).toHaveBeenCalledWith( - SetupEncryptionToast.Kind.UPGRADE_ENCRYPTION); + SetupEncryptionToast.Kind.UPGRADE_ENCRYPTION, + ); }); }); }); - describe('key backup status', () => { - it('checks keybackup status when cross signing and secret storage are ready', async () => { + describe("key backup status", () => { + it("checks keybackup status when cross signing and secret storage are ready", async () => { // default mocks set cross signing and secret storage to ready await createAndStart(); expect(mockClient!.getKeyBackupEnabled).toHaveBeenCalled(); expect(mockDispatcher.dispatch).not.toHaveBeenCalled(); }); - it('checks keybackup status when setup encryption toast has been dismissed', async () => { + it("checks keybackup status when setup encryption toast has been dismissed", async () => { mockClient!.isCrossSigningReady.mockResolvedValue(false); const instance = await createAndStart(); @@ -356,20 +351,20 @@ describe('DeviceListener', () => { expect(mockClient!.getKeyBackupEnabled).toHaveBeenCalled(); }); - it('does not dispatch keybackup event when key backup check is not finished', async () => { + it("does not dispatch keybackup event when key backup check is not finished", async () => { // returns null when key backup status hasn't finished being checked mockClient!.getKeyBackupEnabled.mockReturnValue(null); await createAndStart(); expect(mockDispatcher.dispatch).not.toHaveBeenCalled(); }); - it('dispatches keybackup event when key backup is not enabled', async () => { + it("dispatches keybackup event when key backup is not enabled", async () => { mockClient!.getKeyBackupEnabled.mockReturnValue(false); await createAndStart(); expect(mockDispatcher.dispatch).toHaveBeenCalledWith({ action: Action.ReportKeyBackupNotEnabled }); }); - it('does not check key backup status again after check is complete', async () => { + it("does not check key backup status again after check is complete", async () => { mockClient!.getKeyBackupEnabled.mockReturnValue(null); const instance = await createAndStart(); expect(mockClient!.getKeyBackupEnabled).toHaveBeenCalled(); @@ -390,43 +385,41 @@ describe('DeviceListener', () => { }); }); - describe('unverified sessions toasts', () => { + describe("unverified sessions toasts", () => { const currentDevice = new DeviceInfo(deviceId); - const device2 = new DeviceInfo('d2'); - const device3 = new DeviceInfo('d3'); + const device2 = new DeviceInfo("d2"); + const device3 = new DeviceInfo("d3"); const deviceTrustVerified = new DeviceTrustLevel(true, false, false, false); const deviceTrustUnverified = new DeviceTrustLevel(false, false, false, false); beforeEach(() => { mockClient!.isCrossSigningReady.mockResolvedValue(true); - mockClient!.getStoredDevicesForUser.mockReturnValue([ - currentDevice, device2, device3, - ]); + mockClient!.getStoredDevicesForUser.mockReturnValue([currentDevice, device2, device3]); // all devices verified by default mockClient!.checkDeviceTrust.mockReturnValue(deviceTrustVerified); mockClient!.deviceId = currentDevice.deviceId; - jest.spyOn(SettingsStore, 'getValue').mockImplementation( - settingName => settingName === UIFeature.BulkUnverifiedSessionsReminder, + jest.spyOn(SettingsStore, "getValue").mockImplementation( + (settingName) => settingName === UIFeature.BulkUnverifiedSessionsReminder, ); }); - describe('bulk unverified sessions toasts', () => { - it('hides toast when cross signing is not ready', async () => { + describe("bulk unverified sessions toasts", () => { + it("hides toast when cross signing is not ready", async () => { mockClient!.isCrossSigningReady.mockResolvedValue(false); await createAndStart(); expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled(); expect(BulkUnverifiedSessionsToast.showToast).not.toHaveBeenCalled(); }); - it('hides toast when all devices at app start are verified', async () => { + it("hides toast when all devices at app start are verified", async () => { await createAndStart(); expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled(); expect(BulkUnverifiedSessionsToast.showToast).not.toHaveBeenCalled(); }); - it('hides toast when feature is disabled', async () => { + it("hides toast when feature is disabled", async () => { // BulkUnverifiedSessionsReminder set to false - jest.spyOn(SettingsStore, 'getValue').mockReturnValue(false); + jest.spyOn(SettingsStore, "getValue").mockReturnValue(false); // currentDevice, device2 are verified, device3 is unverified // ie if reminder was enabled it should be shown mockClient!.checkDeviceTrust.mockImplementation((_userId, deviceId) => { @@ -442,7 +435,7 @@ describe('DeviceListener', () => { expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled(); }); - it('hides toast when current device is unverified', async () => { + it("hides toast when current device is unverified", async () => { // device2 verified, current and device3 unverified mockClient!.checkDeviceTrust.mockImplementation((_userId, deviceId) => { switch (deviceId) { @@ -457,7 +450,7 @@ describe('DeviceListener', () => { expect(BulkUnverifiedSessionsToast.showToast).not.toHaveBeenCalled(); }); - it('hides toast when reminder is snoozed', async () => { + it("hides toast when reminder is snoozed", async () => { mocked(isBulkUnverifiedDeviceReminderSnoozed).mockReturnValue(true); // currentDevice, device2 are verified, device3 is unverified mockClient!.checkDeviceTrust.mockImplementation((_userId, deviceId) => { @@ -474,7 +467,7 @@ describe('DeviceListener', () => { expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled(); }); - it('shows toast with unverified devices at app start', async () => { + it("shows toast with unverified devices at app start", async () => { // currentDevice, device2 are verified, device3 is unverified mockClient!.checkDeviceTrust.mockImplementation((_userId, deviceId) => { switch (deviceId) { @@ -492,7 +485,7 @@ describe('DeviceListener', () => { expect(BulkUnverifiedSessionsToast.hideToast).not.toHaveBeenCalled(); }); - it('hides toast when unverified sessions at app start have been dismissed', async () => { + it("hides toast when unverified sessions at app start have been dismissed", async () => { // currentDevice, device2 are verified, device3 is unverified mockClient!.checkDeviceTrust.mockImplementation((_userId, deviceId) => { switch (deviceId) { @@ -514,7 +507,7 @@ describe('DeviceListener', () => { expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled(); }); - it('hides toast when unverified sessions are added after app start', async () => { + it("hides toast when unverified sessions are added after app start", async () => { // currentDevice, device2 are verified, device3 is unverified mockClient!.checkDeviceTrust.mockImplementation((_userId, deviceId) => { switch (deviceId) { @@ -525,17 +518,13 @@ describe('DeviceListener', () => { return deviceTrustUnverified; } }); - mockClient!.getStoredDevicesForUser.mockReturnValue([ - currentDevice, device2, - ]); + mockClient!.getStoredDevicesForUser.mockReturnValue([currentDevice, device2]); await createAndStart(); expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled(); // add an unverified device - mockClient!.getStoredDevicesForUser.mockReturnValue([ - currentDevice, device2, device3, - ]); + mockClient!.getStoredDevicesForUser.mockReturnValue([currentDevice, device2, device3]); // trigger a recheck mockClient!.emit(CryptoEvent.DevicesUpdated, [userId], false); await flushPromises(); diff --git a/test/HtmlUtils-test.tsx b/test/HtmlUtils-test.tsx index c92a372a07..00b0564ff5 100644 --- a/test/HtmlUtils-test.tsx +++ b/test/HtmlUtils-test.tsx @@ -14,13 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; // eslint-disable-next-line deprecate/import -import { mount } from 'enzyme'; -import { mocked } from 'jest-mock'; +import { mount } from "enzyme"; +import { mocked } from "jest-mock"; -import { topicToHtml } from '../src/HtmlUtils'; -import SettingsStore from '../src/settings/SettingsStore'; +import { topicToHtml } from "../src/HtmlUtils"; +import SettingsStore from "../src/settings/SettingsStore"; jest.mock("../src/settings/SettingsStore"); @@ -30,38 +30,39 @@ const enableHtmlTopicFeature = () => { }); }; -describe('HtmlUtils', () => { - it('converts plain text topic to HTML', () => { - const component = mount(
{ topicToHtml("pizza", null, null, false) }
); +describe("HtmlUtils", () => { + it("converts plain text topic to HTML", () => { + const component = mount(
{topicToHtml("pizza", null, null, false)}
); const wrapper = component.render(); expect(wrapper.children().first().html()).toEqual("pizza"); }); - it('converts plain text topic with emoji to HTML', () => { - const component = mount(
{ topicToHtml("pizza 🍕", null, null, false) }
); + it("converts plain text topic with emoji to HTML", () => { + const component = mount(
{topicToHtml("pizza 🍕", null, null, false)}
); const wrapper = component.render(); - expect(wrapper.children().first().html()).toEqual("pizza 🍕"); + expect(wrapper.children().first().html()).toEqual('pizza 🍕'); }); - it('converts literal HTML topic to HTML', async () => { + it("converts literal HTML topic to HTML", async () => { enableHtmlTopicFeature(); - const component = mount(
{ topicToHtml("pizza", null, null, false) }
); + const component = mount(
{topicToHtml("pizza", null, null, false)}
); const wrapper = component.render(); expect(wrapper.children().first().html()).toEqual("<b>pizza</b>"); }); - it('converts true HTML topic to HTML', async () => { + it("converts true HTML topic to HTML", async () => { enableHtmlTopicFeature(); - const component = mount(
{ topicToHtml("**pizza**", "pizza", null, false) }
); + const component = mount(
{topicToHtml("**pizza**", "pizza", null, false)}
); const wrapper = component.render(); expect(wrapper.children().first().html()).toEqual("pizza"); }); - it('converts true HTML topic with emoji to HTML', async () => { + it("converts true HTML topic with emoji to HTML", async () => { enableHtmlTopicFeature(); - const component = mount(
{ topicToHtml("**pizza** 🍕", "pizza 🍕", null, false) }
); + const component = mount(
{topicToHtml("**pizza** 🍕", "pizza 🍕", null, false)}
); const wrapper = component.render(); - expect(wrapper.children().first().html()) - .toEqual("pizza 🍕"); + expect(wrapper.children().first().html()).toEqual( + 'pizza 🍕', + ); }); }); diff --git a/test/KeyBindingsManager-test.ts b/test/KeyBindingsManager-test.ts index 7cf9f2dd3d..1724307fbe 100644 --- a/test/KeyBindingsManager-test.ts +++ b/test/KeyBindingsManager-test.ts @@ -14,14 +14,17 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { isKeyComboMatch, KeyCombo } from '../src/KeyBindingsManager'; +import { isKeyComboMatch, KeyCombo } from "../src/KeyBindingsManager"; -function mockKeyEvent(key: string, modifiers?: { - ctrlKey?: boolean; - altKey?: boolean; - shiftKey?: boolean; - metaKey?: boolean; -}): KeyboardEvent { +function mockKeyEvent( + key: string, + modifiers?: { + ctrlKey?: boolean; + altKey?: boolean; + shiftKey?: boolean; + metaKey?: boolean; + }, +): KeyboardEvent { return { key, ctrlKey: modifiers?.ctrlKey ?? false, @@ -31,121 +34,140 @@ function mockKeyEvent(key: string, modifiers?: { } as KeyboardEvent; } -describe('KeyBindingsManager', () => { - it('should match basic key combo', () => { +describe("KeyBindingsManager", () => { + it("should match basic key combo", () => { const combo1: KeyCombo = { - key: 'k', + key: "k", }; - expect(isKeyComboMatch(mockKeyEvent('k'), combo1, false)).toBe(true); - expect(isKeyComboMatch(mockKeyEvent('n'), combo1, false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k"), combo1, false)).toBe(true); + expect(isKeyComboMatch(mockKeyEvent("n"), combo1, false)).toBe(false); }); - it('should match key + modifier key combo', () => { + it("should match key + modifier key combo", () => { const combo: KeyCombo = { - key: 'k', + key: "k", ctrlKey: true, }; - expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true }), combo, false)).toBe(true); - expect(isKeyComboMatch(mockKeyEvent('n', { ctrlKey: true }), combo, false)).toBe(false); - expect(isKeyComboMatch(mockKeyEvent('k'), combo, false)).toBe(false); - expect(isKeyComboMatch(mockKeyEvent('k', { shiftKey: true }), combo, false)).toBe(false); - expect(isKeyComboMatch(mockKeyEvent('k', { shiftKey: true, metaKey: true }), combo, false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true }), combo, false)).toBe(true); + expect(isKeyComboMatch(mockKeyEvent("n", { ctrlKey: true }), combo, false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k"), combo, false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k", { shiftKey: true }), combo, false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k", { shiftKey: true, metaKey: true }), combo, false)).toBe(false); const combo2: KeyCombo = { - key: 'k', + key: "k", metaKey: true, }; - expect(isKeyComboMatch(mockKeyEvent('k', { metaKey: true }), combo2, false)).toBe(true); - expect(isKeyComboMatch(mockKeyEvent('n', { metaKey: true }), combo2, false)).toBe(false); - expect(isKeyComboMatch(mockKeyEvent('k'), combo2, false)).toBe(false); - expect(isKeyComboMatch(mockKeyEvent('k', { altKey: true, metaKey: true }), combo2, false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k", { metaKey: true }), combo2, false)).toBe(true); + expect(isKeyComboMatch(mockKeyEvent("n", { metaKey: true }), combo2, false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k"), combo2, false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k", { altKey: true, metaKey: true }), combo2, false)).toBe(false); const combo3: KeyCombo = { - key: 'k', + key: "k", altKey: true, }; - expect(isKeyComboMatch(mockKeyEvent('k', { altKey: true }), combo3, false)).toBe(true); - expect(isKeyComboMatch(mockKeyEvent('n', { altKey: true }), combo3, false)).toBe(false); - expect(isKeyComboMatch(mockKeyEvent('k'), combo3, false)).toBe(false); - expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true, metaKey: true }), combo3, false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k", { altKey: true }), combo3, false)).toBe(true); + expect(isKeyComboMatch(mockKeyEvent("n", { altKey: true }), combo3, false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k"), combo3, false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true, metaKey: true }), combo3, false)).toBe(false); const combo4: KeyCombo = { - key: 'k', + key: "k", shiftKey: true, }; - expect(isKeyComboMatch(mockKeyEvent('k', { shiftKey: true }), combo4, false)).toBe(true); - expect(isKeyComboMatch(mockKeyEvent('n', { shiftKey: true }), combo4, false)).toBe(false); - expect(isKeyComboMatch(mockKeyEvent('k'), combo4, false)).toBe(false); - expect(isKeyComboMatch(mockKeyEvent('k', { shiftKey: true, ctrlKey: true }), combo4, false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k", { shiftKey: true }), combo4, false)).toBe(true); + expect(isKeyComboMatch(mockKeyEvent("n", { shiftKey: true }), combo4, false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k"), combo4, false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k", { shiftKey: true, ctrlKey: true }), combo4, false)).toBe(false); }); - it('should match key + multiple modifiers key combo', () => { + it("should match key + multiple modifiers key combo", () => { const combo: KeyCombo = { - key: 'k', + key: "k", ctrlKey: true, altKey: true, }; - expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true, altKey: true }), combo, false)).toBe(true); - expect(isKeyComboMatch(mockKeyEvent('n', { ctrlKey: true, altKey: true }), combo, false)).toBe(false); - expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true, metaKey: true }), combo, false)).toBe(false); - expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true, metaKey: true, shiftKey: true }), combo, - false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true, altKey: true }), combo, false)).toBe(true); + expect(isKeyComboMatch(mockKeyEvent("n", { ctrlKey: true, altKey: true }), combo, false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true, metaKey: true }), combo, false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true, metaKey: true, shiftKey: true }), combo, false)).toBe( + false, + ); const combo2: KeyCombo = { - key: 'k', + key: "k", ctrlKey: true, shiftKey: true, altKey: true, }; - expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true, shiftKey: true, altKey: true }), combo2, - false)).toBe(true); - expect(isKeyComboMatch(mockKeyEvent('n', { ctrlKey: true, shiftKey: true, altKey: true }), combo2, - false)).toBe(false); - expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true, metaKey: true }), combo2, false)).toBe(false); - expect(isKeyComboMatch(mockKeyEvent('k', - { ctrlKey: true, shiftKey: true, altKey: true, metaKey: true }), combo2, false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true, shiftKey: true, altKey: true }), combo2, false)).toBe( + true, + ); + expect(isKeyComboMatch(mockKeyEvent("n", { ctrlKey: true, shiftKey: true, altKey: true }), combo2, false)).toBe( + false, + ); + expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true, metaKey: true }), combo2, false)).toBe(false); + expect( + isKeyComboMatch( + mockKeyEvent("k", { ctrlKey: true, shiftKey: true, altKey: true, metaKey: true }), + combo2, + false, + ), + ).toBe(false); const combo3: KeyCombo = { - key: 'k', + key: "k", ctrlKey: true, shiftKey: true, altKey: true, metaKey: true, }; - expect(isKeyComboMatch(mockKeyEvent('k', - { ctrlKey: true, shiftKey: true, altKey: true, metaKey: true }), combo3, false)).toBe(true); - expect(isKeyComboMatch(mockKeyEvent('n', - { ctrlKey: true, shiftKey: true, altKey: true, metaKey: true }), combo3, false)).toBe(false); - expect(isKeyComboMatch(mockKeyEvent('k', - { ctrlKey: true, shiftKey: true, altKey: true }), combo3, false)).toBe(false); + expect( + isKeyComboMatch( + mockKeyEvent("k", { ctrlKey: true, shiftKey: true, altKey: true, metaKey: true }), + combo3, + false, + ), + ).toBe(true); + expect( + isKeyComboMatch( + mockKeyEvent("n", { ctrlKey: true, shiftKey: true, altKey: true, metaKey: true }), + combo3, + false, + ), + ).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true, shiftKey: true, altKey: true }), combo3, false)).toBe( + false, + ); }); - it('should match ctrlOrMeta key combo', () => { + it("should match ctrlOrMeta key combo", () => { const combo: KeyCombo = { - key: 'k', + key: "k", ctrlOrCmdKey: true, }; // PC: - expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true }), combo, false)).toBe(true); - expect(isKeyComboMatch(mockKeyEvent('k', { metaKey: true }), combo, false)).toBe(false); - expect(isKeyComboMatch(mockKeyEvent('n', { ctrlKey: true }), combo, false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true }), combo, false)).toBe(true); + expect(isKeyComboMatch(mockKeyEvent("k", { metaKey: true }), combo, false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("n", { ctrlKey: true }), combo, false)).toBe(false); // MAC: - expect(isKeyComboMatch(mockKeyEvent('k', { metaKey: true }), combo, true)).toBe(true); - expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true }), combo, true)).toBe(false); - expect(isKeyComboMatch(mockKeyEvent('n', { ctrlKey: true }), combo, true)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k", { metaKey: true }), combo, true)).toBe(true); + expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true }), combo, true)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("n", { ctrlKey: true }), combo, true)).toBe(false); }); - it('should match advanced ctrlOrMeta key combo', () => { + it("should match advanced ctrlOrMeta key combo", () => { const combo: KeyCombo = { - key: 'k', + key: "k", ctrlOrCmdKey: true, altKey: true, }; // PC: - expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true, altKey: true }), combo, false)).toBe(true); - expect(isKeyComboMatch(mockKeyEvent('k', { metaKey: true, altKey: true }), combo, false)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true, altKey: true }), combo, false)).toBe(true); + expect(isKeyComboMatch(mockKeyEvent("k", { metaKey: true, altKey: true }), combo, false)).toBe(false); // MAC: - expect(isKeyComboMatch(mockKeyEvent('k', { metaKey: true, altKey: true }), combo, true)).toBe(true); - expect(isKeyComboMatch(mockKeyEvent('k', { ctrlKey: true, altKey: true }), combo, true)).toBe(false); + expect(isKeyComboMatch(mockKeyEvent("k", { metaKey: true, altKey: true }), combo, true)).toBe(true); + expect(isKeyComboMatch(mockKeyEvent("k", { ctrlKey: true, altKey: true }), combo, true)).toBe(false); }); }); diff --git a/test/LegacyCallHandler-test.ts b/test/LegacyCallHandler-test.ts index 5d1ff16261..474fb7d070 100644 --- a/test/LegacyCallHandler-test.ts +++ b/test/LegacyCallHandler-test.ts @@ -21,23 +21,28 @@ import { PushRuleKind, RuleId, TweakName, -} from 'matrix-js-sdk/src/matrix'; -import { CallEvent, CallState, CallType, MatrixCall } from 'matrix-js-sdk/src/webrtc/call'; -import EventEmitter from 'events'; -import { mocked } from 'jest-mock'; -import { CallEventHandlerEvent } from 'matrix-js-sdk/src/webrtc/callEventHandler'; +} from "matrix-js-sdk/src/matrix"; +import { CallEvent, CallState, CallType, MatrixCall } from "matrix-js-sdk/src/webrtc/call"; +import EventEmitter from "events"; +import { mocked } from "jest-mock"; +import { CallEventHandlerEvent } from "matrix-js-sdk/src/webrtc/callEventHandler"; import LegacyCallHandler, { - LegacyCallHandlerEvent, AudioID, PROTOCOL_PSTN, PROTOCOL_PSTN_PREFIXED, PROTOCOL_SIP_NATIVE, PROTOCOL_SIP_VIRTUAL, -} from '../src/LegacyCallHandler'; -import { stubClient, mkStubRoom, untilDispatch } from './test-utils'; -import { MatrixClientPeg } from '../src/MatrixClientPeg'; -import DMRoomMap from '../src/utils/DMRoomMap'; -import SdkConfig from '../src/SdkConfig'; + LegacyCallHandlerEvent, + AudioID, + PROTOCOL_PSTN, + PROTOCOL_PSTN_PREFIXED, + PROTOCOL_SIP_NATIVE, + PROTOCOL_SIP_VIRTUAL, +} from "../src/LegacyCallHandler"; +import { stubClient, mkStubRoom, untilDispatch } from "./test-utils"; +import { MatrixClientPeg } from "../src/MatrixClientPeg"; +import DMRoomMap from "../src/utils/DMRoomMap"; +import SdkConfig from "../src/SdkConfig"; import { Action } from "../src/dispatcher/actions"; import { getFunctionalMembers } from "../src/utils/room/getFunctionalMembers"; -import SettingsStore from '../src/settings/SettingsStore'; -import { UIFeature } from '../src/settings/UIFeature'; +import SettingsStore from "../src/settings/SettingsStore"; +import { UIFeature } from "../src/settings/UIFeature"; jest.mock("../src/utils/room/getFunctionalMembers", () => ({ getFunctionalMembers: jest.fn(), @@ -67,34 +72,34 @@ const VIRTUAL_ROOM_BOB = "$virtual_bob_room:example.org"; const BOB_PHONE_NUMBER = "01818118181"; function mkStubDM(roomId, userId) { - const room = mkStubRoom(roomId, 'room', MatrixClientPeg.get()); + const room = mkStubRoom(roomId, "room", MatrixClientPeg.get()); room.getJoinedMembers = jest.fn().mockReturnValue([ { - userId: '@me:example.org', - name: 'Member', - rawDisplayName: 'Member', + userId: "@me:example.org", + name: "Member", + rawDisplayName: "Member", roomId: roomId, - membership: 'join', - getAvatarUrl: () => 'mxc://avatar.url/image.png', - getMxcAvatarUrl: () => 'mxc://avatar.url/image.png', + membership: "join", + getAvatarUrl: () => "mxc://avatar.url/image.png", + getMxcAvatarUrl: () => "mxc://avatar.url/image.png", }, { userId: userId, - name: 'Member', - rawDisplayName: 'Member', + name: "Member", + rawDisplayName: "Member", roomId: roomId, - membership: 'join', - getAvatarUrl: () => 'mxc://avatar.url/image.png', - getMxcAvatarUrl: () => 'mxc://avatar.url/image.png', + membership: "join", + getAvatarUrl: () => "mxc://avatar.url/image.png", + getMxcAvatarUrl: () => "mxc://avatar.url/image.png", }, { userId: FUNCTIONAL_USER, - name: 'Bot user', - rawDisplayName: 'Bot user', + name: "Bot user", + rawDisplayName: "Bot user", roomId: roomId, - membership: 'join', - getAvatarUrl: () => 'mxc://avatar.url/image.png', - getMxcAvatarUrl: () => 'mxc://avatar.url/image.png', + membership: "join", + getAvatarUrl: () => "mxc://avatar.url/image.png", + getMxcAvatarUrl: () => "mxc://avatar.url/image.png", }, ]); room.currentState.getMembers = room.getJoinedMembers; @@ -127,7 +132,7 @@ function untilCallHandlerEvent(callHandler: LegacyCallHandler, event: LegacyCall }); } -describe('LegacyCallHandler', () => { +describe("LegacyCallHandler", () => { let dmRoomMap; let callHandler; let audioElement: HTMLAudioElement; @@ -136,11 +141,11 @@ describe('LegacyCallHandler', () => { // what addresses the app has looked up via pstn and native lookup let pstnLookup: string; let nativeLookup: string; - const deviceId = 'my-device'; + const deviceId = "my-device"; beforeEach(async () => { stubClient(); - MatrixClientPeg.get().createCall = roomId => { + MatrixClientPeg.get().createCall = (roomId) => { if (fakeCall && fakeCall.roomId !== roomId) { throw new Error("Only one call is supported!"); } @@ -160,16 +165,14 @@ describe('LegacyCallHandler', () => { callHandler = new LegacyCallHandler(); callHandler.start(); - mocked(getFunctionalMembers).mockReturnValue([ - FUNCTIONAL_USER, - ]); + mocked(getFunctionalMembers).mockReturnValue([FUNCTIONAL_USER]); const nativeRoomAlice = mkStubDM(NATIVE_ROOM_ALICE, NATIVE_ALICE); const nativeRoomBob = mkStubDM(NATIVE_ROOM_BOB, NATIVE_BOB); const nativeRoomCharie = mkStubDM(NATIVE_ROOM_CHARLIE, NATIVE_CHARLIE); const virtualBobRoom = mkStubDM(VIRTUAL_ROOM_BOB, VIRTUAL_BOB); - MatrixClientPeg.get().getRoom = roomId => { + MatrixClientPeg.get().getRoom = (roomId) => { switch (roomId) { case NATIVE_ROOM_ALICE: return nativeRoomAlice; @@ -217,44 +220,50 @@ describe('LegacyCallHandler', () => { MatrixClientPeg.get().getThirdpartyUser = (proto, params) => { if ([PROTOCOL_PSTN, PROTOCOL_PSTN_PREFIXED].includes(proto)) { - pstnLookup = params['m.id.phone']; - return Promise.resolve([{ - userid: VIRTUAL_BOB, - protocol: "m.id.phone", - fields: { - is_native: true, - lookup_success: true, - }, - }]); - } else if (proto === PROTOCOL_SIP_NATIVE) { - nativeLookup = params['virtual_mxid']; - if (params['virtual_mxid'] === VIRTUAL_BOB) { - return Promise.resolve([{ - userid: NATIVE_BOB, - protocol: "im.vector.protocol.sip_native", + pstnLookup = params["m.id.phone"]; + return Promise.resolve([ + { + userid: VIRTUAL_BOB, + protocol: "m.id.phone", fields: { is_native: true, lookup_success: true, }, - }]); + }, + ]); + } else if (proto === PROTOCOL_SIP_NATIVE) { + nativeLookup = params["virtual_mxid"]; + if (params["virtual_mxid"] === VIRTUAL_BOB) { + return Promise.resolve([ + { + userid: NATIVE_BOB, + protocol: "im.vector.protocol.sip_native", + fields: { + is_native: true, + lookup_success: true, + }, + }, + ]); } return Promise.resolve([]); } else if (proto === PROTOCOL_SIP_VIRTUAL) { - if (params['native_mxid'] === NATIVE_BOB) { - return Promise.resolve([{ - userid: VIRTUAL_BOB, - protocol: "im.vector.protocol.sip_virtual", - fields: { - is_virtual: true, - lookup_success: true, + if (params["native_mxid"] === NATIVE_BOB) { + return Promise.resolve([ + { + userid: VIRTUAL_BOB, + protocol: "im.vector.protocol.sip_virtual", + fields: { + is_virtual: true, + lookup_success: true, + }, }, - }]); + ]); } return Promise.resolve([]); } }; - audioElement = document.createElement('audio'); + audioElement = document.createElement("audio"); audioElement.id = "remoteAudio"; document.body.appendChild(audioElement); }); @@ -271,7 +280,7 @@ describe('LegacyCallHandler', () => { SdkConfig.unset(); }); - it('should look up the correct user and start a call in the room when a phone number is dialled', async () => { + it("should look up the correct user and start a call in the room when a phone number is dialled", async () => { await callHandler.dialNumber(BOB_PHONE_NUMBER); expect(pstnLookup).toEqual(BOB_PHONE_NUMBER); @@ -289,7 +298,7 @@ describe('LegacyCallHandler', () => { expect(callHandler.roomIdForCall(fakeCall)).toEqual(NATIVE_ROOM_BOB); }); - it('should look up the correct user and start a call in the room when a call is transferred', async () => { + it("should look up the correct user and start a call in the room when a call is transferred", async () => { // we can pass a very minimal object as as the call since we pass consultFirst=true: // we don't need to actually do any transferring const mockTransferreeCall = { type: CallType.Voice }; @@ -304,7 +313,7 @@ describe('LegacyCallHandler', () => { expect(callHandler.roomIdForCall(fakeCall)).toEqual(NATIVE_ROOM_BOB); }); - it('should move calls between rooms when remote asserted identity changes', async () => { + it("should move calls between rooms when remote asserted identity changes", async () => { callHandler.placeCall(NATIVE_ROOM_ALICE, CallType.Voice); await untilCallHandlerEvent(callHandler, LegacyCallHandlerEvent.CallState); @@ -313,7 +322,7 @@ describe('LegacyCallHandler', () => { expect(callHandler.getCallForRoom(NATIVE_ROOM_ALICE)).toBe(fakeCall); let callRoomChangeEventCount = 0; - const roomChangePromise = new Promise(resolve => { + const roomChangePromise = new Promise((resolve) => { callHandler.addListener(LegacyCallHandlerEvent.CallChangeRoom, () => { ++callRoomChangeEventCount; resolve(); @@ -355,7 +364,7 @@ describe('LegacyCallHandler', () => { }); }); -describe('LegacyCallHandler without third party protocols', () => { +describe("LegacyCallHandler without third party protocols", () => { let dmRoomMap; let callHandler: LegacyCallHandler; let audioElement: HTMLAudioElement; @@ -363,7 +372,7 @@ describe('LegacyCallHandler without third party protocols', () => { beforeEach(() => { stubClient(); - MatrixClientPeg.get().createCall = roomId => { + MatrixClientPeg.get().createCall = (roomId) => { if (fakeCall && fakeCall.roomId !== roomId) { throw new Error("Only one call is supported!"); } @@ -380,7 +389,7 @@ describe('LegacyCallHandler without third party protocols', () => { const nativeRoomAlice = mkStubDM(NATIVE_ROOM_ALICE, NATIVE_ALICE); - MatrixClientPeg.get().getRoom = roomId => { + MatrixClientPeg.get().getRoom = (roomId) => { switch (roomId) { case NATIVE_ROOM_ALICE: return nativeRoomAlice; @@ -409,7 +418,7 @@ describe('LegacyCallHandler without third party protocols', () => { throw new Error("Endpoint unsupported."); }; - audioElement = document.createElement('audio'); + audioElement = document.createElement("audio"); audioElement.id = "remoteAudio"; document.body.appendChild(audioElement); }); @@ -426,7 +435,7 @@ describe('LegacyCallHandler without third party protocols', () => { SdkConfig.unset(); }); - it('should still start a native call', async () => { + it("should still start a native call", async () => { callHandler.placeCall(NATIVE_ROOM_ALICE, CallType.Voice); await untilCallHandlerEvent(callHandler, LegacyCallHandlerEvent.CallState); @@ -439,8 +448,8 @@ describe('LegacyCallHandler without third party protocols', () => { expect(callHandler.roomIdForCall(fakeCall)).toEqual(NATIVE_ROOM_ALICE); }); - describe('incoming calls', () => { - const roomId = 'test-room-id'; + describe("incoming calls", () => { + const roomId = "test-room-id"; const mockAudioElement = { play: jest.fn(), @@ -451,35 +460,35 @@ describe('LegacyCallHandler without third party protocols', () => { } as unknown as HTMLMediaElement; beforeEach(() => { jest.clearAllMocks(); - jest.spyOn(SettingsStore, 'getValue').mockImplementation(setting => - setting === UIFeature.Voip); + jest.spyOn(SettingsStore, "getValue").mockImplementation((setting) => setting === UIFeature.Voip); - jest.spyOn(MatrixClientPeg.get(), 'supportsVoip').mockReturnValue(true); + jest.spyOn(MatrixClientPeg.get(), "supportsVoip").mockReturnValue(true); MatrixClientPeg.get().isFallbackICEServerAllowed = jest.fn(); MatrixClientPeg.get().prepareToEncrypt = jest.fn(); MatrixClientPeg.get().pushRules = { global: { - [PushRuleKind.Override]: [{ - rule_id: RuleId.IncomingCall, - default: false, - enabled: true, - actions: [ - { - set_tweak: TweakName.Sound, - value: 'ring', - }, - ] - , - }], + [PushRuleKind.Override]: [ + { + rule_id: RuleId.IncomingCall, + default: false, + enabled: true, + actions: [ + { + set_tweak: TweakName.Sound, + value: "ring", + }, + ], + }, + ], }, }; - jest.spyOn(document, 'getElementById').mockReturnValue(mockAudioElement); + jest.spyOn(document, "getElementById").mockReturnValue(mockAudioElement); // silence local notifications by default - jest.spyOn(MatrixClientPeg.get(), 'getAccountData').mockImplementation((eventType) => { + jest.spyOn(MatrixClientPeg.get(), "getAccountData").mockImplementation((eventType) => { if (eventType.includes(LOCAL_NOTIFICATION_SETTINGS_PREFIX.name)) { return new MatrixEvent({ type: eventType, @@ -491,7 +500,7 @@ describe('LegacyCallHandler without third party protocols', () => { }); }); - it('should unmute
", ); }); it("changes the root tag name", () => { const TAG_NAME = "p"; - const { container } = render( - Hello world! - ); + const { container } = render(Hello world!); expect(container.querySelectorAll("p")).toHaveLength(1); }); @@ -60,31 +54,29 @@ describe("Linkify", () => { // upon clicking the element, change the content, and expect // linkify to update - return
- - { n % 2 === 0 - ? "https://perdu.com" - : "https://matrix.org" } - -
; + return ( +
+ {n % 2 === 0 ? "https://perdu.com" : "https://matrix.org"} +
+ ); } const { container } = render(); expect(container.innerHTML).toBe( "", + '' + + "https://perdu.com" + + "
", ); fireEvent.click(container.querySelector("div")); expect(container.innerHTML).toBe( "", + '' + + "https://matrix.org" + + "", ); }); }); diff --git a/test/components/views/elements/PollCreateDialog-test.tsx b/test/components/views/elements/PollCreateDialog-test.tsx index 1efb988409..1459e501fc 100644 --- a/test/components/views/elements/PollCreateDialog-test.tsx +++ b/test/components/views/elements/PollCreateDialog-test.tsx @@ -24,16 +24,13 @@ import { M_POLL_START, M_TEXT, PollStartEvent, -} from 'matrix-events-sdk'; -import { MatrixEvent } from 'matrix-js-sdk/src/models/event'; +} from "matrix-events-sdk"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; -import { - findById, - getMockClientWithEventEmitter, -} from '../../../test-utils'; +import { findById, getMockClientWithEventEmitter } from "../../../test-utils"; import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; import PollCreateDialog from "../../../../src/components/views/elements/PollCreateDialog"; -import MatrixClientContext from '../../../../src/contexts/MatrixClientContext'; +import MatrixClientContext from "../../../../src/contexts/MatrixClientContext"; // Fake date to give a predictable snapshot const realDateNow = Date.now; @@ -50,7 +47,7 @@ afterAll(() => { describe("PollCreateDialog", () => { const mockClient = getMockClientWithEventEmitter({ - sendEvent: jest.fn().mockResolvedValue({ event_id: '1' }), + sendEvent: jest.fn().mockResolvedValue({ event_id: "1" }), }); beforeEach(() => { @@ -58,48 +55,35 @@ describe("PollCreateDialog", () => { }); it("renders a blank poll", () => { - const dialog = mount( - , - { - wrappingComponent: MatrixClientContext.Provider, - wrappingComponentProps: { value: mockClient }, - }, - ); + const dialog = mount(, { + wrappingComponent: MatrixClientContext.Provider, + wrappingComponentProps: { value: mockClient }, + }); expect(dialog.html()).toMatchSnapshot(); }); it("autofocuses the poll topic on mount", () => { - const dialog = mount( - , - ); - expect(findById(dialog, 'poll-topic-input').at(0).props().autoFocus).toEqual(true); + const dialog = mount(); + expect(findById(dialog, "poll-topic-input").at(0).props().autoFocus).toEqual(true); }); it("autofocuses the new poll option field after clicking add option button", () => { - const dialog = mount( - , - ); - expect(findById(dialog, 'poll-topic-input').at(0).props().autoFocus).toEqual(true); + const dialog = mount(); + expect(findById(dialog, "poll-topic-input").at(0).props().autoFocus).toEqual(true); dialog.find("div.mx_PollCreateDialog_addOption").simulate("click"); - expect(findById(dialog, 'poll-topic-input').at(0).props().autoFocus).toEqual(false); - expect(findById(dialog, 'pollcreate_option_1').at(0).props().autoFocus).toEqual(false); - expect(findById(dialog, 'pollcreate_option_2').at(0).props().autoFocus).toEqual(true); + expect(findById(dialog, "poll-topic-input").at(0).props().autoFocus).toEqual(false); + expect(findById(dialog, "pollcreate_option_1").at(0).props().autoFocus).toEqual(false); + expect(findById(dialog, "pollcreate_option_2").at(0).props().autoFocus).toEqual(true); }); it("renders a question and some options", () => { - const dialog = mount( - , - ); + const dialog = mount(); expect(submitIsDisabled(dialog)).toBe(true); // When I set some values in the boxes - changeValue( - dialog, - "Question or topic", - "How many turnips is the optimal number?", - ); + changeValue(dialog, "Question or topic", "How many turnips is the optimal number?"); changeValue(dialog, "Option 1", "As many as my neighbour"); changeValue(dialog, "Option 2", "The question is meaningless"); dialog.find("div.mx_PollCreateDialog_addOption").simulate("click"); @@ -109,19 +93,11 @@ describe("PollCreateDialog", () => { it("renders info from a previous event", () => { const previousEvent: MatrixEvent = new MatrixEvent( - PollStartEvent.from( - "Poll Q", - ["Answer 1", "Answer 2"], - M_POLL_KIND_DISCLOSED, - ).serialize(), + PollStartEvent.from("Poll Q", ["Answer 1", "Answer 2"], M_POLL_KIND_DISCLOSED).serialize(), ); const dialog = mount( - , + , ); expect(submitIsDisabled(dialog)).toBe(false); @@ -129,17 +105,13 @@ describe("PollCreateDialog", () => { }); it("doesn't allow submitting until there are options", () => { - const dialog = mount( - , - ); + const dialog = mount(); expect(submitIsDisabled(dialog)).toBe(true); }); it("does allow submitting when there are options and a question", () => { // Given a dialog with no info in (which I am unable to submit) - const dialog = mount( - , - ); + const dialog = mount(); expect(submitIsDisabled(dialog)).toBe(true); // When I set some values in the boxes @@ -152,74 +124,42 @@ describe("PollCreateDialog", () => { }); it("shows the open poll description at first", () => { - const dialog = mount( - , - ); - expect( - dialog.find('select').prop("value"), - ).toEqual(M_POLL_KIND_DISCLOSED.name); - expect( - dialog.find('p').text(), - ).toEqual("Voters see results as soon as they have voted"); + const dialog = mount(); + expect(dialog.find("select").prop("value")).toEqual(M_POLL_KIND_DISCLOSED.name); + expect(dialog.find("p").text()).toEqual("Voters see results as soon as they have voted"); }); it("shows the closed poll description if we choose it", () => { - const dialog = mount( - , - ); + const dialog = mount(); changeKind(dialog, M_POLL_KIND_UNDISCLOSED.name); - expect( - dialog.find('select').prop("value"), - ).toEqual(M_POLL_KIND_UNDISCLOSED.name); - expect( - dialog.find('p').text(), - ).toEqual("Results are only revealed when you end the poll"); + expect(dialog.find("select").prop("value")).toEqual(M_POLL_KIND_UNDISCLOSED.name); + expect(dialog.find("p").text()).toEqual("Results are only revealed when you end the poll"); }); it("shows the open poll description if we choose it", () => { - const dialog = mount( - , - ); + const dialog = mount(); changeKind(dialog, M_POLL_KIND_UNDISCLOSED.name); changeKind(dialog, M_POLL_KIND_DISCLOSED.name); - expect( - dialog.find('select').prop("value"), - ).toEqual(M_POLL_KIND_DISCLOSED.name); - expect( - dialog.find('p').text(), - ).toEqual("Voters see results as soon as they have voted"); + expect(dialog.find("select").prop("value")).toEqual(M_POLL_KIND_DISCLOSED.name); + expect(dialog.find("p").text()).toEqual("Voters see results as soon as they have voted"); }); it("shows the closed poll description when editing a closed poll", () => { const previousEvent: MatrixEvent = new MatrixEvent( - PollStartEvent.from( - "Poll Q", - ["Answer 1", "Answer 2"], - M_POLL_KIND_UNDISCLOSED, - ).serialize(), + PollStartEvent.from("Poll Q", ["Answer 1", "Answer 2"], M_POLL_KIND_UNDISCLOSED).serialize(), ); previousEvent.event.event_id = "$prevEventId"; const dialog = mount( - , + , ); - expect( - dialog.find('select').prop("value"), - ).toEqual(M_POLL_KIND_UNDISCLOSED.name); - expect( - dialog.find('p').text(), - ).toEqual("Results are only revealed when you end the poll"); + expect(dialog.find("select").prop("value")).toEqual(M_POLL_KIND_UNDISCLOSED.name); + expect(dialog.find("p").text()).toEqual("Results are only revealed when you end the poll"); }); it("displays a spinner after submitting", () => { - const dialog = mount( - , - ); + const dialog = mount(); changeValue(dialog, "Question or topic", "Q"); changeValue(dialog, "Option 1", "A1"); changeValue(dialog, "Option 2", "A2"); @@ -230,9 +170,7 @@ describe("PollCreateDialog", () => { }); it("sends a poll create event when submitted", () => { - const dialog = mount( - , - ); + const dialog = mount(); changeValue(dialog, "Question or topic", "Q"); changeValue(dialog, "Option 1", "A1"); changeValue(dialog, "Option 2", "A2"); @@ -240,50 +178,40 @@ describe("PollCreateDialog", () => { dialog.find("button").simulate("click"); const [, , eventType, sentEventContent] = mockClient.sendEvent.mock.calls[0]; expect(M_POLL_START.matches(eventType)).toBeTruthy(); - expect(sentEventContent).toEqual( - { - [M_TEXT.name]: "Q\n1. A1\n2. A2", - [M_POLL_START.name]: { - "answers": [ - { - "id": expect.any(String), - [M_TEXT.name]: "A1", - }, - { - "id": expect.any(String), - [M_TEXT.name]: "A2", - }, - ], - "kind": M_POLL_KIND_DISCLOSED.name, - "max_selections": 1, - "question": { - "body": "Q", - "format": undefined, - "formatted_body": undefined, - "msgtype": "m.text", - [M_TEXT.name]: "Q", + expect(sentEventContent).toEqual({ + [M_TEXT.name]: "Q\n1. A1\n2. A2", + [M_POLL_START.name]: { + answers: [ + { + id: expect.any(String), + [M_TEXT.name]: "A1", }, + { + id: expect.any(String), + [M_TEXT.name]: "A2", + }, + ], + kind: M_POLL_KIND_DISCLOSED.name, + max_selections: 1, + question: { + body: "Q", + format: undefined, + formatted_body: undefined, + msgtype: "m.text", + [M_TEXT.name]: "Q", }, }, - ); + }); }); it("sends a poll edit event when editing", () => { const previousEvent: MatrixEvent = new MatrixEvent( - PollStartEvent.from( - "Poll Q", - ["Answer 1", "Answer 2"], - M_POLL_KIND_DISCLOSED, - ).serialize(), + PollStartEvent.from("Poll Q", ["Answer 1", "Answer 2"], M_POLL_KIND_DISCLOSED).serialize(), ); previousEvent.event.event_id = "$prevEventId"; const dialog = mount( - , + , ); changeValue(dialog, "Question or topic", "Poll Q updated"); @@ -293,65 +221,51 @@ describe("PollCreateDialog", () => { const [, , eventType, sentEventContent] = mockClient.sendEvent.mock.calls[0]; expect(M_POLL_START.matches(eventType)).toBeTruthy(); - expect(sentEventContent).toEqual( - { - "m.new_content": { - [M_TEXT.name]: "Poll Q updated\n1. Answer 1\n2. Answer 2 updated", - [M_POLL_START.name]: { - "answers": [ - { - "id": expect.any(String), - [M_TEXT.name]: "Answer 1", - }, - { - "id": expect.any(String), - [M_TEXT.name]: "Answer 2 updated", - }, - ], - "kind": M_POLL_KIND_UNDISCLOSED.name, - "max_selections": 1, - "question": { - "body": "Poll Q updated", - "format": undefined, - "formatted_body": undefined, - "msgtype": "m.text", - [M_TEXT.name]: "Poll Q updated", + expect(sentEventContent).toEqual({ + "m.new_content": { + [M_TEXT.name]: "Poll Q updated\n1. Answer 1\n2. Answer 2 updated", + [M_POLL_START.name]: { + answers: [ + { + id: expect.any(String), + [M_TEXT.name]: "Answer 1", }, + { + id: expect.any(String), + [M_TEXT.name]: "Answer 2 updated", + }, + ], + kind: M_POLL_KIND_UNDISCLOSED.name, + max_selections: 1, + question: { + body: "Poll Q updated", + format: undefined, + formatted_body: undefined, + msgtype: "m.text", + [M_TEXT.name]: "Poll Q updated", }, }, - "m.relates_to": { - "event_id": previousEvent.getId(), - "rel_type": "m.replace", - }, }, - ); + "m.relates_to": { + event_id: previousEvent.getId(), + rel_type: "m.replace", + }, + }); }); }); function createRoom(): Room { - return new Room( - "roomid", - MatrixClientPeg.get(), - "@name:example.com", - {}, - ); + return new Room("roomid", MatrixClientPeg.get(), "@name:example.com", {}); } function changeValue(wrapper: ReactWrapper, labelText: string, value: string) { - wrapper.find(`input[label="${labelText}"]`).simulate( - "change", - { target: { value: value } }, - ); + wrapper.find(`input[label="${labelText}"]`).simulate("change", { target: { value: value } }); } function changeKind(wrapper: ReactWrapper, value: string) { - wrapper.find("select").simulate( - "change", - { target: { value: value } }, - ); + wrapper.find("select").simulate("change", { target: { value: value } }); } function submitIsDisabled(wrapper: ReactWrapper) { return wrapper.find('button[type="submit"]').prop("aria-disabled") === true; } - diff --git a/test/components/views/elements/PowerSelector-test.tsx b/test/components/views/elements/PowerSelector-test.tsx index 2164e88020..4636aaafc7 100644 --- a/test/components/views/elements/PowerSelector-test.tsx +++ b/test/components/views/elements/PowerSelector-test.tsx @@ -14,12 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; import { fireEvent, render, screen } from "@testing-library/react"; import PowerSelector from "../../../../src/components/views/elements/PowerSelector"; -describe('', () => { +describe("", () => { it("should reset back to custom value when custom input is blurred blank", async () => { const fn = jest.fn(); render(); diff --git a/test/components/views/elements/ProgressBar-test.tsx b/test/components/views/elements/ProgressBar-test.tsx index 320304fb76..ffdeb54837 100644 --- a/test/components/views/elements/ProgressBar-test.tsx +++ b/test/components/views/elements/ProgressBar-test.tsx @@ -28,7 +28,9 @@ describe("", () => { expect(progress.value).toBe(0); // Await the animation to conclude to our initial value of 50 - act(() => { jest.runAllTimers(); }); + act(() => { + jest.runAllTimers(); + }); expect(progress.position).toBe(0.5); // Move the needle to 80% @@ -36,7 +38,9 @@ describe("", () => { expect(progress.position).toBe(0.5); // Let the animaiton run a tiny bit, assert it has moved from where it was to where it needs to go - act(() => { jest.advanceTimersByTime(150); }); + act(() => { + jest.advanceTimersByTime(150); + }); expect(progress.position).toBeGreaterThan(0.5); expect(progress.position).toBeLessThan(0.8); }); diff --git a/test/components/views/elements/QRCode-test.tsx b/test/components/views/elements/QRCode-test.tsx index dbd240aa3d..4148366063 100644 --- a/test/components/views/elements/QRCode-test.tsx +++ b/test/components/views/elements/QRCode-test.tsx @@ -24,13 +24,13 @@ describe("", () => { it("renders a QR with defaults", async () => { const { container, getAllByAltText } = render(); - await waitFor(() => getAllByAltText('QR Code').length === 1); + await waitFor(() => getAllByAltText("QR Code").length === 1); expect(container).toMatchSnapshot(); }); it("renders a QR with high error correction level", async () => { const { container, getAllByAltText } = render(); - await waitFor(() => getAllByAltText('QR Code').length === 1); + await waitFor(() => getAllByAltText("QR Code").length === 1); expect(container).toMatchSnapshot(); }); }); diff --git a/test/components/views/elements/ReplyChain-test.tsx b/test/components/views/elements/ReplyChain-test.tsx index bcc33c1fed..0bfafddbc9 100644 --- a/test/components/views/elements/ReplyChain-test.tsx +++ b/test/components/views/elements/ReplyChain-test.tsx @@ -14,12 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -import * as testUtils from '../../../test-utils'; +import * as testUtils from "../../../test-utils"; import { getParentEventId } from "../../../../src/utils/Reply"; describe("ReplyChain", () => { - describe('getParentEventId', () => { - it('retrieves relation reply from unedited event', () => { + describe("getParentEventId", () => { + it("retrieves relation reply from unedited event", () => { const originalEventWithRelation = testUtils.mkEvent({ event: true, type: "m.room.message", @@ -28,7 +28,7 @@ describe("ReplyChain", () => { "body": "> Reply to this message\n\n foo", "m.relates_to": { "m.in_reply_to": { - "event_id": "$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og", + event_id: "$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og", }, }, }, @@ -36,11 +36,12 @@ describe("ReplyChain", () => { room: "room_id", }); - expect(getParentEventId(originalEventWithRelation)) - .toStrictEqual('$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og'); + expect(getParentEventId(originalEventWithRelation)).toStrictEqual( + "$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og", + ); }); - it('retrieves relation reply from original event when edited', () => { + it("retrieves relation reply from original event when edited", () => { const originalEventWithRelation = testUtils.mkEvent({ event: true, type: "m.room.message", @@ -49,7 +50,7 @@ describe("ReplyChain", () => { "body": "> Reply to this message\n\n foo", "m.relates_to": { "m.in_reply_to": { - "event_id": "$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og", + event_id: "$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og", }, }, }, @@ -64,12 +65,12 @@ describe("ReplyChain", () => { "msgtype": "m.text", "body": "> Reply to this message\n\n * foo bar", "m.new_content": { - "msgtype": "m.text", - "body": "foo bar", + msgtype: "m.text", + body: "foo bar", }, "m.relates_to": { - "rel_type": "m.replace", - "event_id": originalEventWithRelation.getId(), + rel_type: "m.replace", + event_id: originalEventWithRelation.getId(), }, }, user: "some_other_user", @@ -80,11 +81,12 @@ describe("ReplyChain", () => { originalEventWithRelation.makeReplaced(editEvent); // The relation should be pulled from the original event - expect(getParentEventId(originalEventWithRelation)) - .toStrictEqual('$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og'); + expect(getParentEventId(originalEventWithRelation)).toStrictEqual( + "$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og", + ); }); - it('retrieves relation reply from edit event when provided', () => { + it("retrieves relation reply from edit event when provided", () => { const originalEvent = testUtils.mkEvent({ event: true, type: "m.room.message", @@ -107,13 +109,13 @@ describe("ReplyChain", () => { "body": "foo bar", "m.relates_to": { "m.in_reply_to": { - "event_id": "$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og", + event_id: "$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og", }, }, }, "m.relates_to": { - "rel_type": "m.replace", - "event_id": originalEvent.getId(), + rel_type: "m.replace", + event_id: originalEvent.getId(), }, }, user: "some_other_user", @@ -124,11 +126,10 @@ describe("ReplyChain", () => { originalEvent.makeReplaced(editEvent); // The relation should be pulled from the edit event - expect(getParentEventId(originalEvent)) - .toStrictEqual('$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og'); + expect(getParentEventId(originalEvent)).toStrictEqual("$qkjmFBTEc0VvfVyzq1CJuh1QZi_xDIgNEFjZ4Pq34og"); }); - it('prefers relation reply from edit event over original event', () => { + it("prefers relation reply from edit event over original event", () => { const originalEventWithRelation = testUtils.mkEvent({ event: true, type: "m.room.message", @@ -137,7 +138,7 @@ describe("ReplyChain", () => { "body": "> Reply to this message\n\n foo", "m.relates_to": { "m.in_reply_to": { - "event_id": "$111", + event_id: "$111", }, }, }, @@ -156,13 +157,13 @@ describe("ReplyChain", () => { "body": "foo bar", "m.relates_to": { "m.in_reply_to": { - "event_id": "$999", + event_id: "$999", }, }, }, "m.relates_to": { - "rel_type": "m.replace", - "event_id": originalEventWithRelation.getId(), + rel_type: "m.replace", + event_id: originalEventWithRelation.getId(), }, }, user: "some_other_user", @@ -173,10 +174,10 @@ describe("ReplyChain", () => { originalEventWithRelation.makeReplaced(editEvent); // The relation should be pulled from the edit event - expect(getParentEventId(originalEventWithRelation)).toStrictEqual('$999'); + expect(getParentEventId(originalEventWithRelation)).toStrictEqual("$999"); }); - it('able to clear relation reply from original event by providing empty relation field', () => { + it("able to clear relation reply from original event by providing empty relation field", () => { const originalEventWithRelation = testUtils.mkEvent({ event: true, type: "m.room.message", @@ -185,7 +186,7 @@ describe("ReplyChain", () => { "body": "> Reply to this message\n\n foo", "m.relates_to": { "m.in_reply_to": { - "event_id": "$111", + event_id: "$111", }, }, }, @@ -206,8 +207,8 @@ describe("ReplyChain", () => { "m.relates_to": {}, }, "m.relates_to": { - "rel_type": "m.replace", - "event_id": originalEventWithRelation.getId(), + rel_type: "m.replace", + event_id: originalEventWithRelation.getId(), }, }, user: "some_other_user", diff --git a/test/components/views/elements/StyledRadioGroup-test.tsx b/test/components/views/elements/StyledRadioGroup-test.tsx index 8868b741bd..55fe40bcbe 100644 --- a/test/components/views/elements/StyledRadioGroup-test.tsx +++ b/test/components/views/elements/StyledRadioGroup-test.tsx @@ -14,47 +14,47 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; import { fireEvent, render } from "@testing-library/react"; import StyledRadioGroup from "../../../../src/components/views/elements/StyledRadioGroup"; -describe('', () => { +describe("", () => { const optionA = { - value: 'Anteater', + value: "Anteater", label: Anteater label, - description: 'anteater description', - className: 'a-class', + description: "anteater description", + className: "a-class", }; const optionB = { - value: 'Badger', + value: "Badger", label: Badger label, }; const optionC = { - value: 'Canary', + value: "Canary", label: Canary label, description: Canary description, }; const defaultDefinitions = [optionA, optionB, optionC]; const defaultProps = { - name: 'test', - className: 'test-class', + name: "test", + className: "test-class", definitions: defaultDefinitions, onChange: jest.fn(), }; const getComponent = (props = {}) => render(); const getInputByValue = (component, value) => component.container.querySelector(`input[value="${value}"]`); - const getCheckedInput = component => component.container.querySelector('input[checked]'); + const getCheckedInput = (component) => component.container.querySelector("input[checked]"); - it('renders radios correctly when no value is provided', () => { + it("renders radios correctly when no value is provided", () => { const component = getComponent(); expect(component.asFragment()).toMatchSnapshot(); expect(getCheckedInput(component)).toBeFalsy(); }); - it('selects correct button when value is provided', () => { + it("selects correct button when value is provided", () => { const component = getComponent({ value: optionC.value, }); @@ -62,14 +62,11 @@ describe('', () => { expect(getCheckedInput(component).value).toEqual(optionC.value); }); - it('selects correct buttons when definitions have checked prop', () => { - const definitions = [ - { ...optionA, checked: true }, - optionB, - { ...optionC, checked: false }, - ]; + it("selects correct buttons when definitions have checked prop", () => { + const definitions = [{ ...optionA, checked: true }, optionB, { ...optionC, checked: false }]; const component = getComponent({ - value: optionC.value, definitions, + value: optionC.value, + definitions, }); expect(getInputByValue(component, optionA.value)).toBeChecked(); @@ -78,26 +75,22 @@ describe('', () => { expect(getInputByValue(component, optionC.value)).not.toBeChecked(); }); - it('disables individual buttons based on definition.disabled', () => { - const definitions = [ - optionA, - { ...optionB, disabled: true }, - { ...optionC, disabled: true }, - ]; + it("disables individual buttons based on definition.disabled", () => { + const definitions = [optionA, { ...optionB, disabled: true }, { ...optionC, disabled: true }]; const component = getComponent({ definitions }); expect(getInputByValue(component, optionA.value)).not.toBeDisabled(); expect(getInputByValue(component, optionB.value)).toBeDisabled(); expect(getInputByValue(component, optionC.value)).toBeDisabled(); }); - it('disables all buttons with disabled prop', () => { + it("disables all buttons with disabled prop", () => { const component = getComponent({ disabled: true }); expect(getInputByValue(component, optionA.value)).toBeDisabled(); expect(getInputByValue(component, optionB.value)).toBeDisabled(); expect(getInputByValue(component, optionC.value)).toBeDisabled(); }); - it('calls onChange on click', () => { + it("calls onChange on click", () => { const onChange = jest.fn(); const component = getComponent({ value: optionC.value, diff --git a/test/components/views/elements/TooltipTarget-test.tsx b/test/components/views/elements/TooltipTarget-test.tsx index 56b0b37f62..ed7ce26501 100644 --- a/test/components/views/elements/TooltipTarget-test.tsx +++ b/test/components/views/elements/TooltipTarget-test.tsx @@ -15,29 +15,26 @@ limitations under the License. */ import React from "react"; -import { - renderIntoDocument, - Simulate, -} from 'react-dom/test-utils'; +import { 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"; -describe('', () => { +describe("", () => { const defaultProps = { - "tooltipTargetClassName": 'test tooltipTargetClassName', - "className": 'test className', - "tooltipClassName": 'test tooltipClassName', - "label": 'test label', + "tooltipTargetClassName": "test tooltipTargetClassName", + "className": "test className", + "tooltipClassName": "test tooltipClassName", + "label": "test label", "alignment": Alignment.Left, - "id": 'test id', - 'data-test-id': 'test', + "id": "test id", + "data-test-id": "test", }; afterEach(() => { // clean up renderer tooltips - const wrapper = document.querySelector('.mx_Tooltip_wrapper'); + const wrapper = document.querySelector(".mx_Tooltip_wrapper"); while (wrapper?.firstChild) { wrapper.removeChild(wrapper.lastChild); } @@ -45,19 +42,19 @@ describe('', () => { const getComponent = (props = {}) => { const wrapper = renderIntoDocument( - // wrap in element so renderIntoDocument can render functional component + // wrap in element so renderIntoDocument can render functional component child , ) as HTMLSpanElement; - return wrapper.querySelector('[data-test-id=test]'); + return wrapper.querySelector("[data-test-id=test]"); }; - const getVisibleTooltip = () => document.querySelector('.mx_Tooltip.mx_Tooltip_visible'); + const getVisibleTooltip = () => document.querySelector(".mx_Tooltip.mx_Tooltip_visible"); - it('renders container', () => { + it("renders container", () => { const component = getComponent(); expect(component).toMatchSnapshot(); expect(getVisibleTooltip()).toBeFalsy(); @@ -72,7 +69,7 @@ describe('', () => { expect(getVisibleTooltip()).toMatchSnapshot(); }); - it('hides tooltip on mouseleave', () => { + it("hides tooltip on mouseleave", () => { const wrapper = getComponent(); act(() => { Simulate.mouseOver(wrapper); @@ -84,7 +81,7 @@ describe('', () => { expect(getVisibleTooltip()).toBeFalsy(); }); - it('displays tooltip on focus', () => { + it("displays tooltip on focus", () => { const wrapper = getComponent(); act(() => { Simulate.focus(wrapper); @@ -92,7 +89,7 @@ describe('', () => { expect(getVisibleTooltip()).toBeTruthy(); }); - it('hides tooltip on blur', async () => { + it("hides tooltip on blur", async () => { const wrapper = getComponent(); act(() => { Simulate.focus(wrapper); diff --git a/test/components/views/location/LiveDurationDropdown-test.tsx b/test/components/views/location/LiveDurationDropdown-test.tsx index 60bd7c7706..73003e589a 100644 --- a/test/components/views/location/LiveDurationDropdown-test.tsx +++ b/test/components/views/location/LiveDurationDropdown-test.tsx @@ -14,51 +14,52 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; // eslint-disable-next-line deprecate/import -import { mount } from 'enzyme'; -import { act } from 'react-dom/test-utils'; +import { mount } from "enzyme"; +import { act } from "react-dom/test-utils"; -import LiveDurationDropdown, { DEFAULT_DURATION_MS } - from '../../../../src/components/views/location/LiveDurationDropdown'; -import { findById, mockPlatformPeg } from '../../../test-utils'; +import LiveDurationDropdown, { + DEFAULT_DURATION_MS, +} from "../../../../src/components/views/location/LiveDurationDropdown"; +import { findById, mockPlatformPeg } from "../../../test-utils"; mockPlatformPeg({ overrideBrowserShortcuts: jest.fn().mockReturnValue(false) }); -describe('', () => { +describe("", () => { const defaultProps = { timeout: DEFAULT_DURATION_MS, onChange: jest.fn(), }; - const getComponent = (props = {}) => - mount(); + const getComponent = (props = {}) => mount(); const getOption = (wrapper, timeout) => findById(wrapper, `live-duration__${timeout}`).at(0); - const getSelectedOption = (wrapper) => findById(wrapper, 'live-duration_value'); - const openDropdown = (wrapper) => act(() => { - wrapper.find('[role="button"]').at(0).simulate('click'); - wrapper.setProps({}); - }); + const getSelectedOption = (wrapper) => findById(wrapper, "live-duration_value"); + const openDropdown = (wrapper) => + act(() => { + wrapper.find('[role="button"]').at(0).simulate("click"); + wrapper.setProps({}); + }); - it('renders timeout as selected option', () => { + it("renders timeout as selected option", () => { const wrapper = getComponent(); - expect(getSelectedOption(wrapper).text()).toEqual('Share for 15m'); + expect(getSelectedOption(wrapper).text()).toEqual("Share for 15m"); }); - it('renders non-default timeout as selected option', () => { + it("renders non-default timeout as selected option", () => { const timeout = 1234567; const wrapper = getComponent({ timeout }); expect(getSelectedOption(wrapper).text()).toEqual(`Share for 21m`); }); - it('renders a dropdown option for a non-default timeout value', () => { + it("renders a dropdown option for a non-default timeout value", () => { const timeout = 1234567; const wrapper = getComponent({ timeout }); openDropdown(wrapper); expect(getOption(wrapper, timeout).text()).toEqual(`Share for 21m`); }); - it('updates value on option selection', () => { + it("updates value on option selection", () => { const onChange = jest.fn(); const wrapper = getComponent({ onChange }); @@ -67,7 +68,7 @@ describe('', () => { openDropdown(wrapper); act(() => { - getOption(wrapper, ONE_HOUR).simulate('click'); + getOption(wrapper, ONE_HOUR).simulate("click"); }); expect(onChange).toHaveBeenCalledWith(ONE_HOUR); diff --git a/test/components/views/location/LocationPicker-test.tsx b/test/components/views/location/LocationPicker-test.tsx index 39b0b3b42e..29b044630b 100644 --- a/test/components/views/location/LocationPicker-test.tsx +++ b/test/components/views/location/LocationPicker-test.tsx @@ -14,34 +14,34 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; import maplibregl from "maplibre-gl"; // eslint-disable-next-line deprecate/import import { mount } from "enzyme"; -import { act } from 'react-dom/test-utils'; +import { act } from "react-dom/test-utils"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; -import { MatrixClient } from 'matrix-js-sdk/src/client'; -import { mocked } from 'jest-mock'; -import { logger } from 'matrix-js-sdk/src/logger'; +import { MatrixClient } from "matrix-js-sdk/src/client"; +import { mocked } from "jest-mock"; +import { logger } from "matrix-js-sdk/src/logger"; import LocationPicker from "../../../../src/components/views/location/LocationPicker"; import { LocationShareType } from "../../../../src/components/views/location/shareLocation"; -import MatrixClientContext from '../../../../src/contexts/MatrixClientContext'; -import { MatrixClientPeg } from '../../../../src/MatrixClientPeg'; -import { findById, findByTestId, mockPlatformPeg } from '../../../test-utils'; -import { findMapStyleUrl, LocationShareError } from '../../../../src/utils/location'; +import MatrixClientContext from "../../../../src/contexts/MatrixClientContext"; +import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; +import { findById, findByTestId, mockPlatformPeg } from "../../../test-utils"; +import { findMapStyleUrl, LocationShareError } from "../../../../src/utils/location"; -jest.mock('../../../../src/utils/location/findMapStyleUrl', () => ({ - findMapStyleUrl: jest.fn().mockReturnValue('tileserver.com'), +jest.mock("../../../../src/utils/location/findMapStyleUrl", () => ({ + findMapStyleUrl: jest.fn().mockReturnValue("tileserver.com"), })); // dropdown uses this mockPlatformPeg({ overrideBrowserShortcuts: jest.fn().mockReturnValue(false) }); describe("LocationPicker", () => { - describe('', () => { - const roomId = '!room:server.org'; - const userId = '@user:server.org'; + describe("", () => { + const roomId = "!room:server.org"; + const userId = "@user:server.org"; const sender = new RoomMember(roomId, userId); const defaultProps = { sender, @@ -56,10 +56,11 @@ describe("LocationPicker", () => { isGuest: jest.fn(), getClientWellKnown: jest.fn(), }; - const getComponent = (props = {}) => mount(, { - wrappingComponent: MatrixClientContext.Provider, - wrappingComponentProps: { value: mockClient }, - }); + const getComponent = (props = {}) => + mount(, { + wrappingComponent: MatrixClientContext.Provider, + wrappingComponentProps: { value: mockClient }, + }); const mockMap = new maplibregl.Map(); const mockGeolocate = new maplibregl.GeolocateControl(); @@ -82,33 +83,33 @@ describe("LocationPicker", () => { }; beforeEach(() => { - jest.spyOn(logger, 'error').mockRestore(); - jest.spyOn(MatrixClientPeg, 'get').mockReturnValue(mockClient as unknown as MatrixClient); + jest.spyOn(logger, "error").mockRestore(); + jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient as unknown as MatrixClient); jest.clearAllMocks(); mocked(mockMap).addControl.mockReset(); - mocked(findMapStyleUrl).mockReturnValue('tileserver.com'); + mocked(findMapStyleUrl).mockReturnValue("tileserver.com"); }); - it('displays error when map emits an error', () => { + it("displays error when map emits an error", () => { // suppress expected error log - jest.spyOn(logger, 'error').mockImplementation(() => { }); + jest.spyOn(logger, "error").mockImplementation(() => {}); const wrapper = getComponent(); act(() => { // @ts-ignore - mocked(mockMap).emit('error', { error: 'Something went wrong' }); + mocked(mockMap).emit("error", { error: "Something went wrong" }); wrapper.setProps({}); }); - expect(findByTestId(wrapper, 'map-rendering-error').find('p').text()).toEqual( - "This homeserver is not configured correctly to display maps, " - + "or the configured map server may be unreachable.", + expect(findByTestId(wrapper, "map-rendering-error").find("p").text()).toEqual( + "This homeserver is not configured correctly to display maps, " + + "or the configured map server may be unreachable.", ); }); - it('displays error when map display is not configured properly', () => { + it("displays error when map display is not configured properly", () => { // suppress expected error log - jest.spyOn(logger, 'error').mockImplementation(() => { }); + jest.spyOn(logger, "error").mockImplementation(() => {}); mocked(findMapStyleUrl).mockImplementation(() => { throw new Error(LocationShareError.MapStyleUrlNotConfigured); }); @@ -116,111 +117,111 @@ describe("LocationPicker", () => { const wrapper = getComponent(); wrapper.setProps({}); - expect(findByTestId(wrapper, 'map-rendering-error').find('p').text()).toEqual( + expect(findByTestId(wrapper, "map-rendering-error").find("p").text()).toEqual( "This homeserver is not configured to display maps.", ); }); - it('displays error when map setup throws', () => { + it("displays error when map setup throws", () => { // suppress expected error log - jest.spyOn(logger, 'error').mockImplementation(() => { }); + jest.spyOn(logger, "error").mockImplementation(() => {}); // throw an error - mocked(mockMap).addControl.mockImplementation(() => { throw new Error('oups'); }); + mocked(mockMap).addControl.mockImplementation(() => { + throw new Error("oups"); + }); const wrapper = getComponent(); wrapper.setProps({}); - expect(findByTestId(wrapper, 'map-rendering-error').find('p').text()).toEqual( - "This homeserver is not configured correctly to display maps, " - + "or the configured map server may be unreachable.", + expect(findByTestId(wrapper, "map-rendering-error").find("p").text()).toEqual( + "This homeserver is not configured correctly to display maps, " + + "or the configured map server may be unreachable.", ); }); - it('initiates map with geolocation', () => { + it("initiates map with geolocation", () => { getComponent(); expect(mockMap.addControl).toHaveBeenCalledWith(mockGeolocate); act(() => { // @ts-ignore - mocked(mockMap).emit('load'); + mocked(mockMap).emit("load"); }); expect(mockGeolocate.trigger).toHaveBeenCalled(); }); const testUserLocationShareTypes = (shareType: LocationShareType.Own | LocationShareType.Live) => { - describe('user location behaviours', () => { - it('closes and displays error when geolocation errors', () => { + describe("user location behaviours", () => { + it("closes and displays error when geolocation errors", () => { // suppress expected error log - jest.spyOn(logger, 'error').mockImplementation(() => { }); + jest.spyOn(logger, "error").mockImplementation(() => {}); const onFinished = jest.fn(); getComponent({ onFinished, shareType }); expect(mockMap.addControl).toHaveBeenCalledWith(mockGeolocate); act(() => { // @ts-ignore - mockMap.emit('load'); + mockMap.emit("load"); // @ts-ignore - mockGeolocate.emit('error', {}); + mockGeolocate.emit("error", {}); }); // dialog is closed on error expect(onFinished).toHaveBeenCalled(); }); - it('sets position on geolocate event', () => { + it("sets position on geolocate event", () => { const wrapper = getComponent({ shareType }); act(() => { // @ts-ignore - mocked(mockGeolocate).emit('geolocate', mockGeolocationPosition); + mocked(mockGeolocate).emit("geolocate", mockGeolocationPosition); wrapper.setProps({}); }); // marker added expect(maplibregl.Marker).toHaveBeenCalled(); - expect(mockMarker.setLngLat).toHaveBeenCalledWith(new maplibregl.LngLat( - 12.4, 43.2, - )); + expect(mockMarker.setLngLat).toHaveBeenCalledWith(new maplibregl.LngLat(12.4, 43.2)); // submit button is enabled when position is truthy - expect(findByTestId(wrapper, 'location-picker-submit-button').at(0).props().disabled).toBeFalsy(); - expect(wrapper.find('MemberAvatar').length).toBeTruthy(); + expect(findByTestId(wrapper, "location-picker-submit-button").at(0).props().disabled).toBeFalsy(); + expect(wrapper.find("MemberAvatar").length).toBeTruthy(); }); - it('disables submit button until geolocation completes', () => { + it("disables submit button until geolocation completes", () => { const onChoose = jest.fn(); const wrapper = getComponent({ shareType, onChoose }); // submit button is enabled when position is truthy - expect(findByTestId(wrapper, 'location-picker-submit-button').at(0).props().disabled).toBeTruthy(); + expect(findByTestId(wrapper, "location-picker-submit-button").at(0).props().disabled).toBeTruthy(); act(() => { - findByTestId(wrapper, 'location-picker-submit-button').at(0).simulate('click'); + findByTestId(wrapper, "location-picker-submit-button").at(0).simulate("click"); }); // nothing happens on button click expect(onChoose).not.toHaveBeenCalled(); act(() => { // @ts-ignore - mocked(mockGeolocate).emit('geolocate', mockGeolocationPosition); + mocked(mockGeolocate).emit("geolocate", mockGeolocationPosition); wrapper.setProps({}); }); // submit button is enabled when position is truthy - expect(findByTestId(wrapper, 'location-picker-submit-button').at(0).props().disabled).toBeFalsy(); + expect(findByTestId(wrapper, "location-picker-submit-button").at(0).props().disabled).toBeFalsy(); }); - it('submits location', () => { + it("submits location", () => { const onChoose = jest.fn(); const wrapper = getComponent({ onChoose, shareType }); act(() => { // @ts-ignore - mocked(mockGeolocate).emit('geolocate', mockGeolocationPosition); + mocked(mockGeolocate).emit("geolocate", mockGeolocationPosition); // make sure button is enabled wrapper.setProps({}); }); act(() => { - findByTestId(wrapper, 'location-picker-submit-button').at(0).simulate('click'); + findByTestId(wrapper, "location-picker-submit-button").at(0).simulate("click"); }); // content of this call is tested in LocationShareMenu-test @@ -229,67 +230,68 @@ describe("LocationPicker", () => { }); }; - describe('for Own location share type', () => { + describe("for Own location share type", () => { testUserLocationShareTypes(LocationShareType.Own); }); - describe('for Live location share type', () => { + describe("for Live location share type", () => { const shareType = LocationShareType.Live; testUserLocationShareTypes(shareType); const getOption = (wrapper, timeout) => findById(wrapper, `live-duration__${timeout}`).at(0); - const getDropdown = wrapper => findByTestId(wrapper, 'live-duration-dropdown'); - const getSelectedOption = (wrapper) => findById(wrapper, 'live-duration_value'); + const getDropdown = (wrapper) => findByTestId(wrapper, "live-duration-dropdown"); + const getSelectedOption = (wrapper) => findById(wrapper, "live-duration_value"); - const openDropdown = (wrapper) => act(() => { - const dropdown = getDropdown(wrapper); - dropdown.find('[role="button"]').at(0).simulate('click'); - wrapper.setProps({}); - }); + const openDropdown = (wrapper) => + act(() => { + const dropdown = getDropdown(wrapper); + dropdown.find('[role="button"]').at(0).simulate("click"); + wrapper.setProps({}); + }); - it('renders live duration dropdown with default option', () => { + it("renders live duration dropdown with default option", () => { const wrapper = getComponent({ shareType }); - expect(getSelectedOption(getDropdown(wrapper)).text()).toEqual('Share for 15m'); + expect(getSelectedOption(getDropdown(wrapper)).text()).toEqual("Share for 15m"); }); - it('updates selected duration', () => { + it("updates selected duration", () => { const wrapper = getComponent({ shareType }); openDropdown(wrapper); const dropdown = getDropdown(wrapper); act(() => { - getOption(dropdown, 3600000).simulate('click'); + getOption(dropdown, 3600000).simulate("click"); }); // value updated - expect(getSelectedOption(getDropdown(wrapper)).text()).toEqual('Share for 1h'); + expect(getSelectedOption(getDropdown(wrapper)).text()).toEqual("Share for 1h"); }); }); - describe('for Pin drop location share type', () => { + describe("for Pin drop location share type", () => { const shareType = LocationShareType.Pin; - it('initiates map with geolocation', () => { + it("initiates map with geolocation", () => { getComponent({ shareType }); expect(mockMap.addControl).toHaveBeenCalledWith(mockGeolocate); act(() => { // @ts-ignore - mocked(mockMap).emit('load'); + mocked(mockMap).emit("load"); }); expect(mockGeolocate.trigger).toHaveBeenCalled(); }); - it('removes geolocation control on geolocation error', () => { + it("removes geolocation control on geolocation error", () => { // suppress expected error log - jest.spyOn(logger, 'error').mockImplementation(() => { }); + jest.spyOn(logger, "error").mockImplementation(() => {}); const onFinished = jest.fn(); getComponent({ onFinished, shareType }); act(() => { // @ts-ignore - mockMap.emit('load'); + mockMap.emit("load"); // @ts-ignore - mockGeolocate.emit('error', {}); + mockGeolocate.emit("error", {}); }); expect(mockMap.removeControl).toHaveBeenCalledWith(mockGeolocate); @@ -297,47 +299,45 @@ describe("LocationPicker", () => { expect(onFinished).not.toHaveBeenCalled(); }); - it('does not set position on geolocate event', () => { + it("does not set position on geolocate event", () => { mocked(maplibregl.Marker).mockClear(); const wrapper = getComponent({ shareType }); act(() => { // @ts-ignore - mocked(mockGeolocate).emit('geolocate', mockGeolocationPosition); + mocked(mockGeolocate).emit("geolocate", mockGeolocationPosition); }); // marker not added - expect(wrapper.find('Marker').length).toBeFalsy(); + expect(wrapper.find("Marker").length).toBeFalsy(); }); - it('sets position on click event', () => { + it("sets position on click event", () => { const wrapper = getComponent({ shareType }); act(() => { // @ts-ignore - mocked(mockMap).emit('click', mockClickEvent); + mocked(mockMap).emit("click", mockClickEvent); wrapper.setProps({}); }); // marker added expect(maplibregl.Marker).toHaveBeenCalled(); - expect(mockMarker.setLngLat).toHaveBeenCalledWith(new maplibregl.LngLat( - 12.4, 43.2, - )); + expect(mockMarker.setLngLat).toHaveBeenCalledWith(new maplibregl.LngLat(12.4, 43.2)); // marker is set, icon not avatar - expect(wrapper.find('.mx_Marker_icon').length).toBeTruthy(); + expect(wrapper.find(".mx_Marker_icon").length).toBeTruthy(); }); - it('submits location', () => { + it("submits location", () => { const onChoose = jest.fn(); const wrapper = getComponent({ onChoose, shareType }); act(() => { // @ts-ignore - mocked(mockMap).emit('click', mockClickEvent); + mocked(mockMap).emit("click", mockClickEvent); wrapper.setProps({}); }); act(() => { - findByTestId(wrapper, 'location-picker-submit-button').at(0).simulate('click'); + findByTestId(wrapper, "location-picker-submit-button").at(0).simulate("click"); }); // content of this call is tested in LocationShareMenu-test diff --git a/test/components/views/location/LocationShareMenu-test.tsx b/test/components/views/location/LocationShareMenu-test.tsx index 7624f6ff86..567fffa591 100644 --- a/test/components/views/location/LocationShareMenu-test.tsx +++ b/test/components/views/location/LocationShareMenu-test.tsx @@ -14,43 +14,43 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; // eslint-disable-next-line deprecate/import -import { mount, ReactWrapper } from 'enzyme'; -import { mocked } from 'jest-mock'; -import { RoomMember } from 'matrix-js-sdk/src/models/room-member'; -import { MatrixClient } from 'matrix-js-sdk/src/client'; -import { RelationType } from 'matrix-js-sdk/src/matrix'; -import { logger } from 'matrix-js-sdk/src/logger'; -import { M_ASSET, LocationAssetType } from 'matrix-js-sdk/src/@types/location'; -import { act } from 'react-dom/test-utils'; +import { mount, ReactWrapper } from "enzyme"; +import { mocked } from "jest-mock"; +import { RoomMember } from "matrix-js-sdk/src/models/room-member"; +import { MatrixClient } from "matrix-js-sdk/src/client"; +import { RelationType } from "matrix-js-sdk/src/matrix"; +import { logger } from "matrix-js-sdk/src/logger"; +import { M_ASSET, LocationAssetType } from "matrix-js-sdk/src/@types/location"; +import { act } from "react-dom/test-utils"; -import LocationShareMenu from '../../../../src/components/views/location/LocationShareMenu'; -import MatrixClientContext from '../../../../src/contexts/MatrixClientContext'; -import { ChevronFace } from '../../../../src/components/structures/ContextMenu'; -import SettingsStore from '../../../../src/settings/SettingsStore'; -import { MatrixClientPeg } from '../../../../src/MatrixClientPeg'; -import { LocationShareType } from '../../../../src/components/views/location/shareLocation'; +import LocationShareMenu from "../../../../src/components/views/location/LocationShareMenu"; +import MatrixClientContext from "../../../../src/contexts/MatrixClientContext"; +import { ChevronFace } from "../../../../src/components/structures/ContextMenu"; +import SettingsStore from "../../../../src/settings/SettingsStore"; +import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; +import { LocationShareType } from "../../../../src/components/views/location/shareLocation"; import { findByTagAndTestId, findByTestId, flushPromisesWithFakeTimers, getMockClientWithEventEmitter, setupAsyncStoreWithClient, -} from '../../../test-utils'; -import Modal from '../../../../src/Modal'; -import { DEFAULT_DURATION_MS } from '../../../../src/components/views/location/LiveDurationDropdown'; -import { OwnBeaconStore } from '../../../../src/stores/OwnBeaconStore'; -import { SettingLevel } from '../../../../src/settings/SettingLevel'; -import QuestionDialog from '../../../../src/components/views/dialogs/QuestionDialog'; +} from "../../../test-utils"; +import Modal from "../../../../src/Modal"; +import { DEFAULT_DURATION_MS } from "../../../../src/components/views/location/LiveDurationDropdown"; +import { OwnBeaconStore } from "../../../../src/stores/OwnBeaconStore"; +import { SettingLevel } from "../../../../src/settings/SettingLevel"; +import QuestionDialog from "../../../../src/components/views/dialogs/QuestionDialog"; jest.useFakeTimers(); -jest.mock('../../../../src/utils/location/findMapStyleUrl', () => ({ - findMapStyleUrl: jest.fn().mockReturnValue('test'), +jest.mock("../../../../src/utils/location/findMapStyleUrl", () => ({ + findMapStyleUrl: jest.fn().mockReturnValue("test"), })); -jest.mock('../../../../src/settings/SettingsStore', () => ({ +jest.mock("../../../../src/settings/SettingsStore", () => ({ getValue: jest.fn(), setValue: jest.fn(), monitorSetting: jest.fn(), @@ -58,44 +58,45 @@ jest.mock('../../../../src/settings/SettingsStore', () => ({ unwatchSetting: jest.fn(), })); -jest.mock('../../../../src/stores/OwnProfileStore', () => ({ +jest.mock("../../../../src/stores/OwnProfileStore", () => ({ OwnProfileStore: { instance: { - displayName: 'Ernie', - getHttpAvatarUrl: jest.fn().mockReturnValue('image.com/img'), + displayName: "Ernie", + getHttpAvatarUrl: jest.fn().mockReturnValue("image.com/img"), }, }, })); -jest.mock('../../../../src/Modal', () => ({ +jest.mock("../../../../src/Modal", () => ({ createDialog: jest.fn(), on: jest.fn(), off: jest.fn(), ModalManagerEvent: { Opened: "opened" }, })); -describe('', () => { - const userId = '@ernie:server.org'; +describe("", () => { + const userId = "@ernie:server.org"; const mockClient = getMockClientWithEventEmitter({ getUserId: jest.fn().mockReturnValue(userId), getClientWellKnown: jest.fn().mockResolvedValue({ - map_style_url: 'maps.com', + map_style_url: "maps.com", }), sendMessage: jest.fn(), - unstable_createLiveBeacon: jest.fn().mockResolvedValue({ event_id: '1' }), - unstable_setLiveBeacon: jest.fn().mockResolvedValue({ event_id: '1' }), + unstable_createLiveBeacon: jest.fn().mockResolvedValue({ event_id: "1" }), + unstable_setLiveBeacon: jest.fn().mockResolvedValue({ event_id: "1" }), getVisibleRooms: jest.fn().mockReturnValue([]), }); const defaultProps = { menuPosition: { - top: 1, left: 1, + top: 1, + left: 1, chevronFace: ChevronFace.Bottom, }, onFinished: jest.fn(), openMenu: jest.fn(), - roomId: '!room:server.org', - sender: new RoomMember('!room:server.org', userId), + roomId: "!room:server.org", + sender: new RoomMember("!room:server.org", userId), }; const position = { @@ -105,7 +106,7 @@ describe('', () => { accuracy: 10, }, timestamp: 1646305006802, - type: 'geolocate', + type: "geolocate", }; const makeOwnBeaconStore = async () => { @@ -122,11 +123,11 @@ describe('', () => { }); beforeEach(async () => { - jest.spyOn(logger, 'error').mockRestore(); + jest.spyOn(logger, "error").mockRestore(); mocked(SettingsStore).getValue.mockReturnValue(false); mockClient.sendMessage.mockClear(); - mockClient.unstable_createLiveBeacon.mockClear().mockResolvedValue({ event_id: '1' }); - jest.spyOn(MatrixClientPeg, 'get').mockReturnValue(mockClient as unknown as MatrixClient); + mockClient.unstable_createLiveBeacon.mockClear().mockResolvedValue({ event_id: "1" }); + jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient as unknown as MatrixClient); mocked(Modal).createDialog.mockClear(); jest.clearAllMocks(); @@ -135,20 +136,20 @@ describe('', () => { }); const getShareTypeOption = (component: ReactWrapper, shareType: LocationShareType) => - findByTagAndTestId(component, `share-location-option-${shareType}`, 'button'); + findByTagAndTestId(component, `share-location-option-${shareType}`, "button"); const getBackButton = (component: ReactWrapper) => - findByTagAndTestId(component, 'share-dialog-buttons-back', 'button'); + findByTagAndTestId(component, "share-dialog-buttons-back", "button"); const getCancelButton = (component: ReactWrapper) => - findByTagAndTestId(component, 'share-dialog-buttons-cancel', 'button'); + findByTagAndTestId(component, "share-dialog-buttons-cancel", "button"); const getSubmitButton = (component: ReactWrapper) => - findByTagAndTestId(component, 'location-picker-submit-button', 'button'); + findByTagAndTestId(component, "location-picker-submit-button", "button"); const setLocation = (component: ReactWrapper) => { // set the location - const locationPickerInstance = component.find('LocationPicker').instance(); + const locationPickerInstance = component.find("LocationPicker").instance(); act(() => { // @ts-ignore locationPickerInstance.onGeolocate(position); @@ -159,38 +160,38 @@ describe('', () => { const setShareType = (component: ReactWrapper, shareType: LocationShareType) => act(() => { - getShareTypeOption(component, shareType).at(0).simulate('click'); + getShareTypeOption(component, shareType).at(0).simulate("click"); component.setProps({}); }); - describe('when only Own share type is enabled', () => { + describe("when only Own share type is enabled", () => { beforeEach(() => enableSettings([])); - it('renders own and live location options', () => { + it("renders own and live location options", () => { const component = getComponent(); expect(getShareTypeOption(component, LocationShareType.Own).length).toBe(1); expect(getShareTypeOption(component, LocationShareType.Live).length).toBe(1); }); - it('renders back button from location picker screen', () => { + it("renders back button from location picker screen", () => { const component = getComponent(); setShareType(component, LocationShareType.Own); expect(getBackButton(component).length).toBe(1); }); - it('clicking cancel button from location picker closes dialog', () => { + it("clicking cancel button from location picker closes dialog", () => { const onFinished = jest.fn(); const component = getComponent({ onFinished }); act(() => { - getCancelButton(component).at(0).simulate('click'); + getCancelButton(component).at(0).simulate("click"); }); expect(onFinished).toHaveBeenCalled(); }); - it('creates static own location share event on submission', () => { + it("creates static own location share event on submission", () => { const onFinished = jest.fn(); const component = getComponent({ onFinished }); @@ -199,7 +200,7 @@ describe('', () => { setLocation(component); act(() => { - getSubmitButton(component).at(0).simulate('click'); + getSubmitButton(component).at(0).simulate("click"); component.setProps({}); }); @@ -207,66 +208,68 @@ describe('', () => { const [messageRoomId, relation, messageBody] = mockClient.sendMessage.mock.calls[0]; expect(messageRoomId).toEqual(defaultProps.roomId); expect(relation).toEqual(null); - expect(messageBody).toEqual(expect.objectContaining({ - [M_ASSET.name]: { - type: LocationAssetType.Self, - }, - })); + expect(messageBody).toEqual( + expect.objectContaining({ + [M_ASSET.name]: { + type: LocationAssetType.Self, + }, + }), + ); }); }); - describe('with pin drop share type enabled', () => { - it('renders share type switch with own and pin drop options', () => { + describe("with pin drop share type enabled", () => { + it("renders share type switch with own and pin drop options", () => { const component = getComponent(); - expect(component.find('LocationPicker').length).toBe(0); + expect(component.find("LocationPicker").length).toBe(0); expect(getShareTypeOption(component, LocationShareType.Own).length).toBe(1); expect(getShareTypeOption(component, LocationShareType.Pin).length).toBe(1); }); - it('does not render back button on share type screen', () => { + it("does not render back button on share type screen", () => { const component = getComponent(); expect(getBackButton(component).length).toBe(0); }); - it('clicking cancel button from share type screen closes dialog', () => { + it("clicking cancel button from share type screen closes dialog", () => { const onFinished = jest.fn(); const component = getComponent({ onFinished }); act(() => { - getCancelButton(component).at(0).simulate('click'); + getCancelButton(component).at(0).simulate("click"); }); expect(onFinished).toHaveBeenCalled(); }); - it('selecting own location share type advances to location picker', () => { + it("selecting own location share type advances to location picker", () => { const component = getComponent(); setShareType(component, LocationShareType.Own); - expect(component.find('LocationPicker').length).toBe(1); + expect(component.find("LocationPicker").length).toBe(1); }); - it('clicking back button from location picker screen goes back to share screen', () => { + it("clicking back button from location picker screen goes back to share screen", () => { const onFinished = jest.fn(); const component = getComponent({ onFinished }); // advance to location picker setShareType(component, LocationShareType.Own); - expect(component.find('LocationPicker').length).toBe(1); + expect(component.find("LocationPicker").length).toBe(1); act(() => { - getBackButton(component).at(0).simulate('click'); + getBackButton(component).at(0).simulate("click"); component.setProps({}); }); // back to share type - expect(component.find('ShareType').length).toBe(1); + expect(component.find("ShareType").length).toBe(1); }); - it('creates pin drop location share event on submission', () => { + it("creates pin drop location share event on submission", () => { const onFinished = jest.fn(); const component = getComponent({ onFinished }); @@ -276,7 +279,7 @@ describe('', () => { setLocation(component); act(() => { - getSubmitButton(component).at(0).simulate('click'); + getSubmitButton(component).at(0).simulate("click"); component.setProps({}); }); @@ -284,76 +287,81 @@ describe('', () => { const [messageRoomId, relation, messageBody] = mockClient.sendMessage.mock.calls[0]; expect(messageRoomId).toEqual(defaultProps.roomId); expect(relation).toEqual(null); - expect(messageBody).toEqual(expect.objectContaining({ - [M_ASSET.name]: { - type: LocationAssetType.Pin, - }, - })); + expect(messageBody).toEqual( + expect.objectContaining({ + [M_ASSET.name]: { + type: LocationAssetType.Pin, + }, + }), + ); }); }); - describe('with live location disabled', () => { + describe("with live location disabled", () => { beforeEach(() => enableSettings([])); const getToggle = (component: ReactWrapper) => - findByTestId(component, 'enable-live-share-toggle').find('[role="switch"]').at(0); + findByTestId(component, "enable-live-share-toggle").find('[role="switch"]').at(0); const getSubmitEnableButton = (component: ReactWrapper) => - findByTestId(component, 'enable-live-share-submit').at(0); + findByTestId(component, "enable-live-share-submit").at(0); - it('goes to labs flag screen after live options is clicked', () => { + it("goes to labs flag screen after live options is clicked", () => { const onFinished = jest.fn(); const component = getComponent({ onFinished }); setShareType(component, LocationShareType.Live); - expect(findByTestId(component, 'location-picker-enable-live-share')).toMatchSnapshot(); + expect(findByTestId(component, "location-picker-enable-live-share")).toMatchSnapshot(); }); - it('disables OK button when labs flag is not enabled', () => { + it("disables OK button when labs flag is not enabled", () => { const component = getComponent(); setShareType(component, LocationShareType.Live); - expect(getSubmitEnableButton(component).props()['disabled']).toBeTruthy(); + expect(getSubmitEnableButton(component).props()["disabled"]).toBeTruthy(); }); - it('enables OK button when labs flag is toggled to enabled', () => { + it("enables OK button when labs flag is toggled to enabled", () => { const component = getComponent(); setShareType(component, LocationShareType.Live); act(() => { - getToggle(component).simulate('click'); + getToggle(component).simulate("click"); component.setProps({}); }); - expect(getSubmitEnableButton(component).props()['disabled']).toBeFalsy(); + expect(getSubmitEnableButton(component).props()["disabled"]).toBeFalsy(); }); - it('enables live share setting on ok button submit', () => { + it("enables live share setting on ok button submit", () => { const component = getComponent(); setShareType(component, LocationShareType.Live); act(() => { - getToggle(component).simulate('click'); + getToggle(component).simulate("click"); component.setProps({}); }); act(() => { - getSubmitEnableButton(component).simulate('click'); + getSubmitEnableButton(component).simulate("click"); }); expect(SettingsStore.setValue).toHaveBeenCalledWith( - 'feature_location_share_live', undefined, SettingLevel.DEVICE, true, + "feature_location_share_live", + undefined, + SettingLevel.DEVICE, + true, ); }); - it('navigates to location picker when live share is enabled in settings store', () => { + it("navigates to location picker when live share is enabled in settings store", () => { // @ts-ignore mocked(SettingsStore.watchSetting).mockImplementation((featureName, roomId, callback) => { - callback(featureName, roomId, SettingLevel.DEVICE, '', ''); + callback(featureName, roomId, SettingLevel.DEVICE, "", ""); window.setTimeout(() => { - callback(featureName, roomId, SettingLevel.DEVICE, '', ''); + callback(featureName, roomId, SettingLevel.DEVICE, "", ""); }, 1000); }); mocked(SettingsStore.getValue).mockReturnValue(false); @@ -362,7 +370,7 @@ describe('', () => { setShareType(component, LocationShareType.Live); // we're on enable live share screen - expect(findByTestId(component, 'location-picker-enable-live-share').length).toBeTruthy(); + expect(findByTestId(component, "location-picker-enable-live-share").length).toBeTruthy(); act(() => { mocked(SettingsStore.getValue).mockReturnValue(true); @@ -373,24 +381,24 @@ describe('', () => { component.setProps({}); // advanced to location picker - expect(component.find('LocationPicker').length).toBeTruthy(); + expect(component.find("LocationPicker").length).toBeTruthy(); }); }); - describe('Live location share', () => { + describe("Live location share", () => { beforeEach(() => enableSettings(["feature_location_share_live"])); - it('does not display live location share option when composer has a relation', () => { + it("does not display live location share option when composer has a relation", () => { const relation = { rel_type: RelationType.Thread, - event_id: '12345', + event_id: "12345", }; const component = getComponent({ relation }); expect(getShareTypeOption(component, LocationShareType.Live).length).toBeFalsy(); }); - it('creates beacon info event on submission', async () => { + it("creates beacon info event on submission", async () => { const onFinished = jest.fn(); const component = getComponent({ onFinished }); @@ -399,7 +407,7 @@ describe('', () => { setLocation(component); act(() => { - getSubmitButton(component).at(0).simulate('click'); + getSubmitButton(component).at(0).simulate("click"); component.setProps({}); }); @@ -409,21 +417,23 @@ describe('', () => { expect(onFinished).toHaveBeenCalled(); const [eventRoomId, eventContent] = mockClient.unstable_createLiveBeacon.mock.calls[0]; expect(eventRoomId).toEqual(defaultProps.roomId); - expect(eventContent).toEqual(expect.objectContaining({ - // default timeout - timeout: DEFAULT_DURATION_MS, - description: `Ernie's live location`, - live: true, - [M_ASSET.name]: { - type: LocationAssetType.Self, - }, - })); + expect(eventContent).toEqual( + expect.objectContaining({ + // default timeout + timeout: DEFAULT_DURATION_MS, + description: `Ernie's live location`, + live: true, + [M_ASSET.name]: { + type: LocationAssetType.Self, + }, + }), + ); }); - it('opens error dialog when beacon creation fails', async () => { + it("opens error dialog when beacon creation fails", async () => { // stub logger to keep console clean from expected error - const logSpy = jest.spyOn(logger, 'error').mockReturnValue(undefined); - const error = new Error('oh no'); + const logSpy = jest.spyOn(logger, "error").mockReturnValue(undefined); + const error = new Error("oh no"); mockClient.unstable_createLiveBeacon.mockRejectedValue(error); const component = getComponent(); @@ -432,7 +442,7 @@ describe('', () => { setLocation(component); act(() => { - getSubmitButton(component).at(0).simulate('click'); + getSubmitButton(component).at(0).simulate("click"); component.setProps({}); }); @@ -441,18 +451,21 @@ describe('', () => { await flushPromisesWithFakeTimers(); expect(logSpy).toHaveBeenCalledWith("We couldn't start sharing your live location", error); - expect(mocked(Modal).createDialog).toHaveBeenCalledWith(QuestionDialog, expect.objectContaining({ - button: 'Try again', - description: 'Element could not send your location. Please try again later.', - title: `We couldn't send your location`, - cancelButton: 'Cancel', - })); + expect(mocked(Modal).createDialog).toHaveBeenCalledWith( + QuestionDialog, + expect.objectContaining({ + button: "Try again", + description: "Element could not send your location. Please try again later.", + title: `We couldn't send your location`, + cancelButton: "Cancel", + }), + ); }); - it('opens error dialog when beacon creation fails with permission error', async () => { + it("opens error dialog when beacon creation fails with permission error", async () => { // stub logger to keep console clean from expected error - const logSpy = jest.spyOn(logger, 'error').mockReturnValue(undefined); - const error = { errcode: 'M_FORBIDDEN' } as unknown as Error; + const logSpy = jest.spyOn(logger, "error").mockReturnValue(undefined); + const error = { errcode: "M_FORBIDDEN" } as unknown as Error; mockClient.unstable_createLiveBeacon.mockRejectedValue(error); const component = getComponent(); @@ -461,7 +474,7 @@ describe('', () => { setLocation(component); act(() => { - getSubmitButton(component).at(0).simulate('click'); + getSubmitButton(component).at(0).simulate("click"); component.setProps({}); }); @@ -470,12 +483,15 @@ describe('', () => { await flushPromisesWithFakeTimers(); expect(logSpy).toHaveBeenCalledWith("Insufficient permissions to start sharing your live location", error); - expect(mocked(Modal).createDialog).toHaveBeenCalledWith(QuestionDialog, expect.objectContaining({ - button: 'OK', - description: 'You need to have the right permissions in order to share locations in this room.', - title: `You don't have permission to share locations`, - hasCancelButton: false, - })); + expect(mocked(Modal).createDialog).toHaveBeenCalledWith( + QuestionDialog, + expect.objectContaining({ + button: "OK", + description: "You need to have the right permissions in order to share locations in this room.", + title: `You don't have permission to share locations`, + hasCancelButton: false, + }), + ); }); }); }); diff --git a/test/components/views/location/LocationViewDialog-test.tsx b/test/components/views/location/LocationViewDialog-test.tsx index 04b08c9af6..31e40f94db 100644 --- a/test/components/views/location/LocationViewDialog-test.tsx +++ b/test/components/views/location/LocationViewDialog-test.tsx @@ -14,23 +14,23 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; // eslint-disable-next-line deprecate/import -import { mount } from 'enzyme'; -import { RoomMember } from 'matrix-js-sdk/src/matrix'; -import { LocationAssetType } from 'matrix-js-sdk/src/@types/location'; -import maplibregl from 'maplibre-gl'; +import { mount } from "enzyme"; +import { RoomMember } from "matrix-js-sdk/src/matrix"; +import { LocationAssetType } from "matrix-js-sdk/src/@types/location"; +import maplibregl from "maplibre-gl"; -import LocationViewDialog from '../../../../src/components/views/location/LocationViewDialog'; -import { TILE_SERVER_WK_KEY } from '../../../../src/utils/WellKnownUtils'; -import { getMockClientWithEventEmitter, makeLocationEvent } from '../../../test-utils'; +import LocationViewDialog from "../../../../src/components/views/location/LocationViewDialog"; +import { TILE_SERVER_WK_KEY } from "../../../../src/utils/WellKnownUtils"; +import { getMockClientWithEventEmitter, makeLocationEvent } from "../../../test-utils"; -describe('', () => { - const roomId = '!room:server'; - const userId = '@user:server'; +describe("", () => { + const roomId = "!room:server"; + const userId = "@user:server"; const mockClient = getMockClientWithEventEmitter({ getClientWellKnown: jest.fn().mockReturnValue({ - [TILE_SERVER_WK_KEY.name]: { map_style_url: 'maps.com' }, + [TILE_SERVER_WK_KEY.name]: { map_style_url: "maps.com" }, }), isGuest: jest.fn().mockReturnValue(false), }); @@ -40,24 +40,23 @@ describe('', () => { mxEvent: defaultEvent, onFinished: jest.fn(), }; - const getComponent = (props = {}) => - mount(); + const getComponent = (props = {}) => mount(); beforeAll(() => { maplibregl.AttributionControl = jest.fn(); }); - it('renders map correctly', () => { + it("renders map correctly", () => { const component = getComponent(); - expect(component.find('Map')).toMatchSnapshot(); + expect(component.find("Map")).toMatchSnapshot(); }); - it('renders marker correctly for self share', () => { + it("renders marker correctly for self share", () => { const selfShareEvent = makeLocationEvent("geo:51.5076,-0.1276", LocationAssetType.Self); const member = new RoomMember(roomId, userId); // @ts-ignore cheat assignment to property selfShareEvent.sender = member; const component = getComponent({ mxEvent: selfShareEvent }); - expect(component.find('SmartMarker').props()['roomMember']).toEqual(member); + expect(component.find("SmartMarker").props()["roomMember"]).toEqual(member); }); }); diff --git a/test/components/views/location/Map-test.tsx b/test/components/views/location/Map-test.tsx index 0e22d1cdd8..8cb3109a7e 100644 --- a/test/components/views/location/Map-test.tsx +++ b/test/components/views/location/Map-test.tsx @@ -14,29 +14,29 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; // eslint-disable-next-line deprecate/import -import { mount } from 'enzyme'; -import { act } from 'react-dom/test-utils'; -import maplibregl from 'maplibre-gl'; -import { ClientEvent } from 'matrix-js-sdk/src/matrix'; -import { logger } from 'matrix-js-sdk/src/logger'; +import { mount } from "enzyme"; +import { act } from "react-dom/test-utils"; +import maplibregl from "maplibre-gl"; +import { ClientEvent } from "matrix-js-sdk/src/matrix"; +import { logger } from "matrix-js-sdk/src/logger"; -import Map from '../../../../src/components/views/location/Map'; -import { findByTestId, getMockClientWithEventEmitter } from '../../../test-utils'; -import MatrixClientContext from '../../../../src/contexts/MatrixClientContext'; -import { TILE_SERVER_WK_KEY } from '../../../../src/utils/WellKnownUtils'; +import Map from "../../../../src/components/views/location/Map"; +import { findByTestId, getMockClientWithEventEmitter } from "../../../test-utils"; +import MatrixClientContext from "../../../../src/contexts/MatrixClientContext"; +import { TILE_SERVER_WK_KEY } from "../../../../src/utils/WellKnownUtils"; -describe('', () => { +describe("", () => { const defaultProps = { - centerGeoUri: 'geo:52,41', - id: 'test-123', + centerGeoUri: "geo:52,41", + id: "test-123", onError: jest.fn(), onClick: jest.fn(), }; const matrixClient = getMockClientWithEventEmitter({ getClientWellKnown: jest.fn().mockReturnValue({ - [TILE_SERVER_WK_KEY.name]: { map_style_url: 'maps.com' }, + [TILE_SERVER_WK_KEY.name]: { map_style_url: "maps.com" }, }), }); const getComponent = (props = {}) => @@ -52,33 +52,33 @@ describe('', () => { beforeEach(() => { jest.clearAllMocks(); matrixClient.getClientWellKnown.mockReturnValue({ - [TILE_SERVER_WK_KEY.name]: { map_style_url: 'maps.com' }, + [TILE_SERVER_WK_KEY.name]: { map_style_url: "maps.com" }, }); - jest.spyOn(logger, 'error').mockRestore(); + jest.spyOn(logger, "error").mockRestore(); }); const mockMap = new maplibregl.Map(); - it('renders', () => { + it("renders", () => { const component = getComponent(); expect(component).toBeTruthy(); }); - describe('onClientWellKnown emits', () => { - it('updates map style when style url is truthy', () => { + describe("onClientWellKnown emits", () => { + it("updates map style when style url is truthy", () => { getComponent(); act(() => { matrixClient.emit(ClientEvent.ClientWellKnown, { - [TILE_SERVER_WK_KEY.name]: { map_style_url: 'new.maps.com' }, + [TILE_SERVER_WK_KEY.name]: { map_style_url: "new.maps.com" }, }); }); - expect(mockMap.setStyle).toHaveBeenCalledWith('new.maps.com'); + expect(mockMap.setStyle).toHaveBeenCalledWith("new.maps.com"); }); - it('does not update map style when style url is truthy', () => { + it("does not update map style when style url is truthy", () => { getComponent(); act(() => { @@ -91,58 +91,60 @@ describe('', () => { }); }); - describe('map centering', () => { - it('does not try to center when no center uri provided', () => { + describe("map centering", () => { + it("does not try to center when no center uri provided", () => { getComponent({ centerGeoUri: null }); expect(mockMap.setCenter).not.toHaveBeenCalled(); }); - it('sets map center to centerGeoUri', () => { - getComponent({ centerGeoUri: 'geo:51,42' }); + it("sets map center to centerGeoUri", () => { + getComponent({ centerGeoUri: "geo:51,42" }); expect(mockMap.setCenter).toHaveBeenCalledWith({ lat: 51, lon: 42 }); }); - it('handles invalid centerGeoUri', () => { - const logSpy = jest.spyOn(logger, 'error').mockImplementation(); - getComponent({ centerGeoUri: '123 Sesame Street' }); + it("handles invalid centerGeoUri", () => { + const logSpy = jest.spyOn(logger, "error").mockImplementation(); + getComponent({ centerGeoUri: "123 Sesame Street" }); expect(mockMap.setCenter).not.toHaveBeenCalled(); - expect(logSpy).toHaveBeenCalledWith('Could not set map center'); + expect(logSpy).toHaveBeenCalledWith("Could not set map center"); }); - it('updates map center when centerGeoUri prop changes', () => { - const component = getComponent({ centerGeoUri: 'geo:51,42' }); + it("updates map center when centerGeoUri prop changes", () => { + const component = getComponent({ centerGeoUri: "geo:51,42" }); - component.setProps({ centerGeoUri: 'geo:53,45' }); - component.setProps({ centerGeoUri: 'geo:56,47' }); + component.setProps({ centerGeoUri: "geo:53,45" }); + component.setProps({ centerGeoUri: "geo:56,47" }); expect(mockMap.setCenter).toHaveBeenCalledWith({ lat: 51, lon: 42 }); expect(mockMap.setCenter).toHaveBeenCalledWith({ lat: 53, lon: 45 }); expect(mockMap.setCenter).toHaveBeenCalledWith({ lat: 56, lon: 47 }); }); }); - describe('map bounds', () => { - it('does not try to fit map bounds when no bounds provided', () => { + describe("map bounds", () => { + it("does not try to fit map bounds when no bounds provided", () => { getComponent({ bounds: null }); expect(mockMap.fitBounds).not.toHaveBeenCalled(); }); - it('fits map to bounds', () => { + it("fits map to bounds", () => { const bounds = { north: 51, south: 50, east: 42, west: 41 }; getComponent({ bounds }); - expect(mockMap.fitBounds).toHaveBeenCalledWith(new maplibregl.LngLatBounds([bounds.west, bounds.south], - [bounds.east, bounds.north]), { padding: 100, maxZoom: 15 }); + expect(mockMap.fitBounds).toHaveBeenCalledWith( + new maplibregl.LngLatBounds([bounds.west, bounds.south], [bounds.east, bounds.north]), + { padding: 100, maxZoom: 15 }, + ); }); - it('handles invalid bounds', () => { - const logSpy = jest.spyOn(logger, 'error').mockImplementation(); - const bounds = { north: 'a', south: 'b', east: 42, west: 41 }; + it("handles invalid bounds", () => { + const logSpy = jest.spyOn(logger, "error").mockImplementation(); + const bounds = { north: "a", south: "b", east: 42, west: 41 }; getComponent({ bounds }); expect(mockMap.fitBounds).not.toHaveBeenCalled(); - expect(logSpy).toHaveBeenCalledWith('Invalid map bounds'); + expect(logSpy).toHaveBeenCalledWith("Invalid map bounds"); }); - it('updates map bounds when bounds prop changes', () => { - const component = getComponent({ centerGeoUri: 'geo:51,42' }); + it("updates map bounds when bounds prop changes", () => { + const component = getComponent({ centerGeoUri: "geo:51,42" }); const bounds = { north: 51, south: 50, east: 42, west: 41 }; const bounds2 = { north: 53, south: 51, east: 45, west: 44 }; @@ -152,8 +154,8 @@ describe('', () => { }); }); - describe('children', () => { - it('renders without children', () => { + describe("children", () => { + it("renders without children", () => { const component = getComponent({ children: null }); component.setProps({}); @@ -162,18 +164,22 @@ describe('', () => { expect(component).toBeTruthy(); }); - it('renders children with map renderProp', () => { - const children = ({ map }) =>
Hello, world
; + it("renders children with map renderProp", () => { + const children = ({ map }) => ( +
+ Hello, world +
+ ); const component = getComponent({ children }); // renders child with map instance - expect(findByTestId(component, 'test-child').props()['data-map']).toEqual(mockMap); + expect(findByTestId(component, "test-child").props()["data-map"]).toEqual(mockMap); }); }); - describe('onClick', () => { - it('eats clicks to maplibre attribution button', () => { + describe("onClick", () => { + it("eats clicks to maplibre attribution button", () => { const onClick = jest.fn(); const component = getComponent({ onClick }); @@ -181,20 +187,20 @@ describe('', () => { // this is added to the dom by maplibregl // which is mocked // just fake the target - const fakeEl = document.createElement('div'); - fakeEl.className = 'maplibregl-ctrl-attrib-button'; - component.simulate('click', { target: fakeEl }); + const fakeEl = document.createElement("div"); + fakeEl.className = "maplibregl-ctrl-attrib-button"; + component.simulate("click", { target: fakeEl }); }); expect(onClick).not.toHaveBeenCalled(); }); - it('calls onClick', () => { + it("calls onClick", () => { const onClick = jest.fn(); const component = getComponent({ onClick }); act(() => { - component.simulate('click'); + component.simulate("click"); }); expect(onClick).toHaveBeenCalled(); diff --git a/test/components/views/location/MapError-test.tsx b/test/components/views/location/MapError-test.tsx index 27a42dd95a..94e97c6f9e 100644 --- a/test/components/views/location/MapError-test.tsx +++ b/test/components/views/location/MapError-test.tsx @@ -14,43 +14,43 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import { render, RenderResult } from '@testing-library/react'; +import React from "react"; +import { render, RenderResult } from "@testing-library/react"; -import { MapError, MapErrorProps } from '../../../../src/components/views/location/MapError'; -import { LocationShareError } from '../../../../src/utils/location'; +import { MapError, MapErrorProps } from "../../../../src/components/views/location/MapError"; +import { LocationShareError } from "../../../../src/utils/location"; -describe('', () => { +describe("", () => { const defaultProps = { onFinished: jest.fn(), error: LocationShareError.MapStyleUrlNotConfigured, - className: 'test', + className: "test", }; const getComponent = (props: Partial = {}): RenderResult => render(); - it('renders correctly for MapStyleUrlNotConfigured', () => { + it("renders correctly for MapStyleUrlNotConfigured", () => { const { container } = getComponent(); expect(container).toMatchSnapshot(); }); - it('renders correctly for MapStyleUrlNotReachable', () => { + it("renders correctly for MapStyleUrlNotReachable", () => { const { container } = getComponent({ error: LocationShareError.MapStyleUrlNotReachable, }); expect(container).toMatchSnapshot(); }); - it('does not render button when onFinished falsy', () => { + it("does not render button when onFinished falsy", () => { const { queryByText } = getComponent({ error: LocationShareError.MapStyleUrlNotReachable, onFinished: undefined, }); // no button - expect(queryByText('OK')).toBeFalsy(); + expect(queryByText("OK")).toBeFalsy(); }); - it('applies class when isMinimised is truthy', () => { + it("applies class when isMinimised is truthy", () => { const { container } = getComponent({ isMinimised: true, }); diff --git a/test/components/views/location/Marker-test.tsx b/test/components/views/location/Marker-test.tsx index 767f1097bd..841a3fa47e 100644 --- a/test/components/views/location/Marker-test.tsx +++ b/test/components/views/location/Marker-test.tsx @@ -14,39 +14,38 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; // eslint-disable-next-line deprecate/import -import { mount } from 'enzyme'; -import { RoomMember } from 'matrix-js-sdk/src/matrix'; +import { mount } from "enzyme"; +import { RoomMember } from "matrix-js-sdk/src/matrix"; -import Marker from '../../../../src/components/views/location/Marker'; +import Marker from "../../../../src/components/views/location/Marker"; -describe('', () => { +describe("", () => { const defaultProps = { - id: 'abc123', + id: "abc123", }; - const getComponent = (props = {}) => - mount(); + const getComponent = (props = {}) => mount(); - it('renders with location icon when no room member', () => { + it("renders with location icon when no room member", () => { const component = getComponent(); expect(component).toMatchSnapshot(); }); - it('does not try to use member color without room member', () => { + it("does not try to use member color without room member", () => { const component = getComponent({ useMemberColor: true }); - expect(component.find('div').at(0).props().className).toEqual('mx_Marker mx_Marker_defaultColor'); + expect(component.find("div").at(0).props().className).toEqual("mx_Marker mx_Marker_defaultColor"); }); - it('uses member color class', () => { - const member = new RoomMember('!room:server', '@user:server'); + it("uses member color class", () => { + const member = new RoomMember("!room:server", "@user:server"); const component = getComponent({ useMemberColor: true, roomMember: member }); - expect(component.find('div').at(0).props().className).toEqual('mx_Marker mx_Username_color3'); + expect(component.find("div").at(0).props().className).toEqual("mx_Marker mx_Username_color3"); }); - it('renders member avatar when roomMember is truthy', () => { - const member = new RoomMember('!room:server', '@user:server'); + it("renders member avatar when roomMember is truthy", () => { + const member = new RoomMember("!room:server", "@user:server"); const component = getComponent({ roomMember: member }); - expect(component.find('MemberAvatar').length).toBeTruthy(); + expect(component.find("MemberAvatar").length).toBeTruthy(); }); }); diff --git a/test/components/views/location/SmartMarker-test.tsx b/test/components/views/location/SmartMarker-test.tsx index 3f617bcb5d..569c80638a 100644 --- a/test/components/views/location/SmartMarker-test.tsx +++ b/test/components/views/location/SmartMarker-test.tsx @@ -14,34 +14,33 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; // eslint-disable-next-line deprecate/import -import { mount } from 'enzyme'; -import { mocked } from 'jest-mock'; -import maplibregl from 'maplibre-gl'; +import { mount } from "enzyme"; +import { mocked } from "jest-mock"; +import maplibregl from "maplibre-gl"; -import SmartMarker from '../../../../src/components/views/location/SmartMarker'; +import SmartMarker from "../../../../src/components/views/location/SmartMarker"; -jest.mock('../../../../src/utils/location/findMapStyleUrl', () => ({ - findMapStyleUrl: jest.fn().mockReturnValue('tileserver.com'), +jest.mock("../../../../src/utils/location/findMapStyleUrl", () => ({ + findMapStyleUrl: jest.fn().mockReturnValue("tileserver.com"), })); -describe('', () => { +describe("", () => { const mockMap = new maplibregl.Map(); const mockMarker = new maplibregl.Marker(); const defaultProps = { map: mockMap, - geoUri: 'geo:43.2,54.6', + geoUri: "geo:43.2,54.6", }; - const getComponent = (props = {}) => - mount(); + const getComponent = (props = {}) => mount(); beforeEach(() => { jest.clearAllMocks(); }); - it('creates a marker on mount', () => { + it("creates a marker on mount", () => { const component = getComponent(); expect(component).toMatchSnapshot(); @@ -55,11 +54,11 @@ describe('', () => { expect(mockMarker.addTo).toHaveBeenCalledWith(mockMap); }); - it('updates marker position on change', () => { - const component = getComponent({ geoUri: 'geo:40,50' }); + it("updates marker position on change", () => { + const component = getComponent({ geoUri: "geo:40,50" }); - component.setProps({ geoUri: 'geo:41,51' }); - component.setProps({ geoUri: 'geo:42,52' }); + component.setProps({ geoUri: "geo:41,51" }); + component.setProps({ geoUri: "geo:42,52" }); // marker added only once expect(maplibregl.Marker).toHaveBeenCalledTimes(1); @@ -69,7 +68,7 @@ describe('', () => { expect(mocked(mockMarker.setLngLat)).toHaveBeenCalledWith({ lat: 42, lon: 52 }); }); - it('removes marker on unmount', () => { + it("removes marker on unmount", () => { const component = getComponent(); expect(component).toMatchSnapshot(); diff --git a/test/components/views/location/ZoomButtons-test.tsx b/test/components/views/location/ZoomButtons-test.tsx index 1192b6aed0..5c5b63b299 100644 --- a/test/components/views/location/ZoomButtons-test.tsx +++ b/test/components/views/location/ZoomButtons-test.tsx @@ -14,48 +14,47 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; // eslint-disable-next-line deprecate/import -import { mount } from 'enzyme'; -import maplibregl from 'maplibre-gl'; -import { act } from 'react-dom/test-utils'; +import { mount } from "enzyme"; +import maplibregl from "maplibre-gl"; +import { act } from "react-dom/test-utils"; -import ZoomButtons from '../../../../src/components/views/location/ZoomButtons'; -import { findByTestId } from '../../../test-utils'; +import ZoomButtons from "../../../../src/components/views/location/ZoomButtons"; +import { findByTestId } from "../../../test-utils"; -describe('', () => { +describe("", () => { const mockMap = new maplibregl.Map(); const defaultProps = { map: mockMap, }; - const getComponent = (props = {}) => - mount(); + const getComponent = (props = {}) => mount(); beforeEach(() => { jest.clearAllMocks(); }); - it('renders buttons', () => { + it("renders buttons", () => { const component = getComponent(); expect(component).toMatchSnapshot(); }); - it('calls map zoom in on zoom in click', () => { + it("calls map zoom in on zoom in click", () => { const component = getComponent(); act(() => { - findByTestId(component, 'map-zoom-in-button').at(0).simulate('click'); + findByTestId(component, "map-zoom-in-button").at(0).simulate("click"); }); expect(mockMap.zoomIn).toHaveBeenCalled(); expect(component).toBeTruthy(); }); - it('calls map zoom out on zoom out click', () => { + it("calls map zoom out on zoom out click", () => { const component = getComponent(); act(() => { - findByTestId(component, 'map-zoom-out-button').at(0).simulate('click'); + findByTestId(component, "map-zoom-out-button").at(0).simulate("click"); }); expect(mockMap.zoomOut).toHaveBeenCalled(); diff --git a/test/components/views/location/shareLocation-test.ts b/test/components/views/location/shareLocation-test.ts index 63658045f8..910d471362 100644 --- a/test/components/views/location/shareLocation-test.ts +++ b/test/components/views/location/shareLocation-test.ts @@ -47,13 +47,11 @@ describe("shareLocation", () => { } as unknown as MatrixClient; mocked(makeLocationContent).mockReturnValue(content); - mocked(doMaybeLocalRoomAction).mockImplementation(( - roomId: string, - fn: (actualRoomId: string) => Promise, - client?: MatrixClient, - ) => { - return fn(roomId); - }); + mocked(doMaybeLocalRoomAction).mockImplementation( + (roomId: string, fn: (actualRoomId: string) => Promise, client?: MatrixClient) => { + return fn(roomId); + }, + ); shareLocationFn = shareLocation(client, roomId, shareType, null, () => {}); }); diff --git a/test/components/views/messages/CallEvent-test.tsx b/test/components/views/messages/CallEvent-test.tsx index 23b7a978a1..c1f04e8978 100644 --- a/test/components/views/messages/CallEvent-test.tsx +++ b/test/components/views/messages/CallEvent-test.tsx @@ -68,16 +68,18 @@ describe("CallEvent", () => { alice = mkRoomMember(room.roomId, "@alice:example.org"); bob = mkRoomMember(room.roomId, "@bob:example.org"); jest.spyOn(room, "getMember").mockImplementation( - userId => [alice, bob].find(member => member.userId === userId) ?? null, + (userId) => [alice, bob].find((member) => member.userId === userId) ?? null, ); - client.getRoom.mockImplementation(roomId => roomId === room.roomId ? room : null); + client.getRoom.mockImplementation((roomId) => (roomId === room.roomId ? room : null)); client.getRooms.mockReturnValue([room]); client.reEmitter.reEmit(room, [RoomStateEvent.Events]); - await Promise.all([CallStore.instance, WidgetMessagingStore.instance].map( - store => setupAsyncStoreWithClient(store, client), - )); + await Promise.all( + [CallStore.instance, WidgetMessagingStore.instance].map((store) => + setupAsyncStoreWithClient(store, client), + ), + ); MockedCall.create(room, "1"); const maybeCall = CallStore.instance.getCall(room.roomId); @@ -99,7 +101,9 @@ describe("CallEvent", () => { jest.restoreAllMocks(); }); - const renderEvent = () => { render(); }; + const renderEvent = () => { + render(); + }; it("shows a message and duration if the call was ended", () => { jest.advanceTimersByTime(90000); @@ -121,7 +125,10 @@ describe("CallEvent", () => { it("shows call details and connection controls if the call is loaded", async () => { jest.advanceTimersByTime(90000); - call.participants = new Map([[alice, new Set(["a"])], [bob, new Set(["b"])]]); + call.participants = new Map([ + [alice, new Set(["a"])], + [bob, new Set(["b"])], + ]); renderEvent(); screen.getByText("@alice:example.org started a video call"); @@ -132,11 +139,13 @@ describe("CallEvent", () => { const dispatcherSpy = jest.fn(); const dispatcherRef = defaultDispatcher.register(dispatcherSpy); fireEvent.click(screen.getByRole("button", { name: "Join" })); - await waitFor(() => expect(dispatcherSpy).toHaveBeenCalledWith({ - action: Action.ViewRoom, - room_id: room.roomId, - view_call: true, - })); + await waitFor(() => + expect(dispatcherSpy).toHaveBeenCalledWith({ + action: Action.ViewRoom, + room_id: room.roomId, + view_call: true, + }), + ); defaultDispatcher.unregister(dispatcherRef); await act(() => call.connect()); diff --git a/test/components/views/messages/DateSeparator-test.tsx b/test/components/views/messages/DateSeparator-test.tsx index 58a7a77d9b..9bcf371f24 100644 --- a/test/components/views/messages/DateSeparator-test.tsx +++ b/test/components/views/messages/DateSeparator-test.tsx @@ -31,7 +31,7 @@ describe("DateSeparator", () => { const HOUR_MS = 3600000; const DAY_MS = HOUR_MS * 24; // Friday Dec 17 2021, 9:09am - const now = '2021-12-17T08:09:00.000Z'; + const now = "2021-12-17T08:09:00.000Z"; const nowMs = 1639728540000; const defaultProps = { ts: nowMs, @@ -47,23 +47,23 @@ describe("DateSeparator", () => { const mockClient = getMockClientWithEventEmitter({}); const getComponent = (props = {}) => - render(( + render( - - )); + , + ); type TestCase = [string, number, string]; const testCases: TestCase[] = [ - ['the exact same moment', nowMs, 'Today'], - ['same day as current day', nowMs - HOUR_MS, 'Today'], - ['day before the current day', nowMs - (HOUR_MS * 12), 'Yesterday'], - ['2 days ago', nowMs - DAY_MS * 2, 'Wednesday'], - ['144 hours ago', nowMs - HOUR_MS * 144, 'Sat, Dec 11 2021'], + ["the exact same moment", nowMs, "Today"], + ["same day as current day", nowMs - HOUR_MS, "Today"], + ["day before the current day", nowMs - HOUR_MS * 12, "Yesterday"], + ["2 days ago", nowMs - DAY_MS * 2, "Wednesday"], + ["144 hours ago", nowMs - HOUR_MS * 144, "Sat, Dec 11 2021"], [ - '6 days ago, but less than 144h', - new Date('Saturday Dec 11 2021 23:59:00 GMT+0100 (Central European Standard Time)').getTime(), - 'Saturday', + "6 days ago, but less than 144h", + new Date("Saturday Dec 11 2021 23:59:00 GMT+0100 (Central European Standard Time)").getTime(), + "Saturday", ], ]; @@ -80,24 +80,25 @@ describe("DateSeparator", () => { global.Date = RealDate; }); - it('renders the date separator correctly', () => { + it("renders the date separator correctly", () => { const { asFragment } = getComponent(); expect(asFragment()).toMatchSnapshot(); expect(SettingsStore.getValue).toHaveBeenCalledWith(UIFeature.TimelineEnableRelativeDates); }); - it.each(testCases)('formats date correctly when current time is %s', (_d, ts, result) => { + it.each(testCases)("formats date correctly when current time is %s", (_d, ts, result) => { expect(getComponent({ ts, forExport: false }).container.textContent).toEqual(result); }); - describe('when forExport is true', () => { - it.each(testCases)('formats date in full when current time is %s', (_d, ts) => { - expect(getComponent({ ts, forExport: true }).container.textContent) - .toEqual(formatFullDateNoTime(new Date(ts))); + describe("when forExport is true", () => { + it.each(testCases)("formats date in full when current time is %s", (_d, ts) => { + expect(getComponent({ ts, forExport: true }).container.textContent).toEqual( + formatFullDateNoTime(new Date(ts)), + ); }); }); - describe('when Settings.TimelineEnableRelativeDates is falsy', () => { + describe("when Settings.TimelineEnableRelativeDates is falsy", () => { beforeEach(() => { (SettingsStore.getValue as jest.Mock) = jest.fn((arg) => { if (arg === UIFeature.TimelineEnableRelativeDates) { @@ -105,13 +106,14 @@ describe("DateSeparator", () => { } }); }); - it.each(testCases)('formats date in full when current time is %s', (_d, ts) => { - expect(getComponent({ ts, forExport: false }).container.textContent) - .toEqual(formatFullDateNoTime(new Date(ts))); + it.each(testCases)("formats date in full when current time is %s", (_d, ts) => { + expect(getComponent({ ts, forExport: false }).container.textContent).toEqual( + formatFullDateNoTime(new Date(ts)), + ); }); }); - describe('when feature_jump_to_date is enabled', () => { + describe("when feature_jump_to_date is enabled", () => { beforeEach(() => { mocked(SettingsStore).getValue.mockImplementation((arg): any => { if (arg === "feature_jump_to_date") { @@ -119,7 +121,7 @@ describe("DateSeparator", () => { } }); }); - it('renders the date separator correctly', () => { + it("renders the date separator correctly", () => { const { asFragment } = getComponent(); expect(asFragment()).toMatchSnapshot(); }); diff --git a/test/components/views/messages/EncryptionEvent-test.tsx b/test/components/views/messages/EncryptionEvent-test.tsx index 7b70fa4f6f..11268e077a 100644 --- a/test/components/views/messages/EncryptionEvent-test.tsx +++ b/test/components/views/messages/EncryptionEvent-test.tsx @@ -14,22 +14,24 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; import { mocked } from "jest-mock"; import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix"; -import { render, screen } from '@testing-library/react'; +import { render, screen } from "@testing-library/react"; import EncryptionEvent from "../../../../src/components/views/messages/EncryptionEvent"; import { createTestClient, mkMessage } from "../../../test-utils"; -import { MatrixClientPeg } from '../../../../src/MatrixClientPeg'; -import { LocalRoom } from '../../../../src/models/LocalRoom'; -import DMRoomMap from '../../../../src/utils/DMRoomMap'; -import MatrixClientContext from '../../../../src/contexts/MatrixClientContext'; +import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; +import { LocalRoom } from "../../../../src/models/LocalRoom"; +import DMRoomMap from "../../../../src/utils/DMRoomMap"; +import MatrixClientContext from "../../../../src/contexts/MatrixClientContext"; const renderEncryptionEvent = (client: MatrixClient, event: MatrixEvent) => { - render( - - ); + render( + + + , + ); }; const checkTexts = (title: string, subTitle: string) => { @@ -69,8 +71,8 @@ describe("EncryptionEvent", () => { renderEncryptionEvent(client, event); checkTexts( "Encryption enabled", - "Messages in this room are end-to-end encrypted. " - + "When people join, you can verify them in their profile, just tap on their avatar.", + "Messages in this room are end-to-end encrypted. " + + "When people join, you can verify them in their profile, just tap on their avatar.", ); }); @@ -83,10 +85,7 @@ describe("EncryptionEvent", () => { it("should show the expected texts", () => { renderEncryptionEvent(client, event); - checkTexts( - "Encryption enabled", - "Some encryption parameters have been changed.", - ); + checkTexts("Encryption enabled", "Some encryption parameters have been changed."); }); }); diff --git a/test/components/views/messages/MBeaconBody-test.tsx b/test/components/views/messages/MBeaconBody-test.tsx index dbfa1f39d1..b779c35a9c 100644 --- a/test/components/views/messages/MBeaconBody-test.tsx +++ b/test/components/views/messages/MBeaconBody-test.tsx @@ -14,68 +14,58 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; // eslint-disable-next-line deprecate/import -import { mount } from 'enzyme'; -import { act } from 'react-dom/test-utils'; -import maplibregl from 'maplibre-gl'; -import { - BeaconEvent, - getBeaconInfoIdentifier, - RelationType, - MatrixEvent, - EventType, -} from 'matrix-js-sdk/src/matrix'; -import { Relations } from 'matrix-js-sdk/src/models/relations'; -import { M_BEACON } from 'matrix-js-sdk/src/@types/beacon'; +import { mount } from "enzyme"; +import { act } from "react-dom/test-utils"; +import maplibregl from "maplibre-gl"; +import { BeaconEvent, getBeaconInfoIdentifier, RelationType, MatrixEvent, EventType } from "matrix-js-sdk/src/matrix"; +import { Relations } from "matrix-js-sdk/src/models/relations"; +import { M_BEACON } from "matrix-js-sdk/src/@types/beacon"; -import MBeaconBody from '../../../../src/components/views/messages/MBeaconBody'; +import MBeaconBody from "../../../../src/components/views/messages/MBeaconBody"; import { getMockClientWithEventEmitter, makeBeaconEvent, makeBeaconInfoEvent, makeRoomWithBeacons, makeRoomWithStateEvents, -} from '../../../test-utils'; -import { RoomPermalinkCreator } from '../../../../src/utils/permalinks/Permalinks'; -import { MediaEventHelper } from '../../../../src/utils/MediaEventHelper'; -import MatrixClientContext from '../../../../src/contexts/MatrixClientContext'; -import Modal from '../../../../src/Modal'; -import { TILE_SERVER_WK_KEY } from '../../../../src/utils/WellKnownUtils'; -import { MapError } from '../../../../src/components/views/location/MapError'; -import * as mapUtilHooks from '../../../../src/utils/location/useMap'; -import { LocationShareError } from '../../../../src/utils/location'; +} from "../../../test-utils"; +import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks"; +import { MediaEventHelper } from "../../../../src/utils/MediaEventHelper"; +import MatrixClientContext from "../../../../src/contexts/MatrixClientContext"; +import Modal from "../../../../src/Modal"; +import { TILE_SERVER_WK_KEY } from "../../../../src/utils/WellKnownUtils"; +import { MapError } from "../../../../src/components/views/location/MapError"; +import * as mapUtilHooks from "../../../../src/utils/location/useMap"; +import { LocationShareError } from "../../../../src/utils/location"; -describe('', () => { +describe("", () => { // 14.03.2022 16:15 const now = 1647270879403; // stable date for snapshots - jest.spyOn(global.Date, 'now').mockReturnValue(now); - const roomId = '!room:server'; - const aliceId = '@alice:server'; + jest.spyOn(global.Date, "now").mockReturnValue(now); + const roomId = "!room:server"; + const aliceId = "@alice:server"; const mockMap = new maplibregl.Map(); const mockMarker = new maplibregl.Marker(); const mockClient = getMockClientWithEventEmitter({ getClientWellKnown: jest.fn().mockReturnValue({ - [TILE_SERVER_WK_KEY.name]: { map_style_url: 'maps.com' }, + [TILE_SERVER_WK_KEY.name]: { map_style_url: "maps.com" }, }), getUserId: jest.fn().mockReturnValue(aliceId), getRoom: jest.fn(), redactEvent: jest.fn(), }); - const defaultEvent = makeBeaconInfoEvent(aliceId, - roomId, - { isLive: true }, - '$alice-room1-1', - ); + const defaultEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: true }, "$alice-room1-1"); const defaultProps = { mxEvent: defaultEvent, highlights: [], - highlightLink: '', + highlightLink: "", onHeightChanged: jest.fn(), onMessageAllowed: jest.fn(), // we dont use these and they pollute the snapshots @@ -89,7 +79,7 @@ describe('', () => { wrappingComponentProps: { value: mockClient }, }); - const modalSpy = jest.spyOn(Modal, 'createDialog').mockReturnValue(undefined); + const modalSpy = jest.spyOn(Modal, "createDialog").mockReturnValue(undefined); beforeAll(() => { maplibregl.AttributionControl = jest.fn(); @@ -100,73 +90,66 @@ describe('', () => { }); const testBeaconStatuses = () => { - it('renders stopped beacon UI for an explicitly stopped beacon', () => { - const beaconInfoEvent = makeBeaconInfoEvent(aliceId, - roomId, - { isLive: false }, - '$alice-room1-1', - ); + it("renders stopped beacon UI for an explicitly stopped beacon", () => { + const beaconInfoEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: false }, "$alice-room1-1"); makeRoomWithStateEvents([beaconInfoEvent], { roomId, mockClient }); const component = getComponent({ mxEvent: beaconInfoEvent }); expect(component.text()).toEqual("Live location ended"); }); - it('renders stopped beacon UI for an expired beacon', () => { - const beaconInfoEvent = makeBeaconInfoEvent(aliceId, + it("renders stopped beacon UI for an expired beacon", () => { + const beaconInfoEvent = makeBeaconInfoEvent( + aliceId, roomId, // puts this beacons live period in the past { isLive: true, timestamp: now - 600000, timeout: 500 }, - '$alice-room1-1', + "$alice-room1-1", ); makeRoomWithStateEvents([beaconInfoEvent], { roomId, mockClient }); const component = getComponent({ mxEvent: beaconInfoEvent }); expect(component.text()).toEqual("Live location ended"); }); - it('renders loading beacon UI for a beacon that has not started yet', () => { + it("renders loading beacon UI for a beacon that has not started yet", () => { const beaconInfoEvent = makeBeaconInfoEvent( aliceId, roomId, // puts this beacons start timestamp in the future { isLive: true, timestamp: now + 60000, timeout: 500 }, - '$alice-room1-1', + "$alice-room1-1", ); makeRoomWithStateEvents([beaconInfoEvent], { roomId, mockClient }); const component = getComponent({ mxEvent: beaconInfoEvent }); expect(component.text()).toEqual("Loading live location..."); }); - it('does not open maximised map when on click when beacon is stopped', () => { - const beaconInfoEvent = makeBeaconInfoEvent(aliceId, + it("does not open maximised map when on click when beacon is stopped", () => { + const beaconInfoEvent = makeBeaconInfoEvent( + aliceId, roomId, // puts this beacons live period in the past { isLive: true, timestamp: now - 600000, timeout: 500 }, - '$alice-room1-1', + "$alice-room1-1", ); makeRoomWithStateEvents([beaconInfoEvent], { roomId, mockClient }); const component = getComponent({ mxEvent: beaconInfoEvent }); act(() => { - component.find('.mx_MBeaconBody_map').at(0).simulate('click'); + component.find(".mx_MBeaconBody_map").at(0).simulate("click"); }); expect(modalSpy).not.toHaveBeenCalled(); }); - it('renders stopped UI when a beacon event is not the latest beacon for a user', () => { + it("renders stopped UI when a beacon event is not the latest beacon for a user", () => { const aliceBeaconInfo1 = makeBeaconInfoEvent( aliceId, roomId, // this one is a little older { isLive: true, timestamp: now - 500 }, - '$alice-room1-1', + "$alice-room1-1", ); aliceBeaconInfo1.event.origin_server_ts = now - 500; - const aliceBeaconInfo2 = makeBeaconInfoEvent( - aliceId, - roomId, - { isLive: true }, - '$alice-room1-2', - ); + const aliceBeaconInfo2 = makeBeaconInfoEvent(aliceId, roomId, { isLive: true }, "$alice-room1-2"); makeRoomWithStateEvents([aliceBeaconInfo1, aliceBeaconInfo2], { roomId, mockClient }); @@ -175,21 +158,16 @@ describe('', () => { expect(component.text()).toEqual("Live location ended"); }); - it('renders stopped UI when a beacon event is replaced', () => { + it("renders stopped UI when a beacon event is replaced", () => { const aliceBeaconInfo1 = makeBeaconInfoEvent( aliceId, roomId, // this one is a little older { isLive: true, timestamp: now - 500 }, - '$alice-room1-1', + "$alice-room1-1", ); aliceBeaconInfo1.event.origin_server_ts = now - 500; - const aliceBeaconInfo2 = makeBeaconInfoEvent( - aliceId, - roomId, - { isLive: true }, - '$alice-room1-2', - ); + const aliceBeaconInfo2 = makeBeaconInfoEvent(aliceId, roomId, { isLive: true }, "$alice-room1-2"); const room = makeRoomWithStateEvents([aliceBeaconInfo1], { roomId, mockClient }); const component = getComponent({ mxEvent: aliceBeaconInfo1 }); @@ -210,14 +188,9 @@ describe('', () => { testBeaconStatuses(); - describe('on liveness change', () => { - it('renders stopped UI when a beacon stops being live', () => { - const aliceBeaconInfo = makeBeaconInfoEvent( - aliceId, - roomId, - { isLive: true }, - '$alice-room1-1', - ); + describe("on liveness change", () => { + it("renders stopped UI when a beacon stops being live", () => { + const aliceBeaconInfo = makeBeaconInfoEvent(aliceId, roomId, { isLive: true }, "$alice-room1-1"); const room = makeRoomWithStateEvents([aliceBeaconInfo], { roomId, mockClient }); const beaconInstance = room.currentState.beacons.get(getBeaconInfoIdentifier(aliceBeaconInfo)); @@ -236,97 +209,96 @@ describe('', () => { }); }); - describe('latestLocationState', () => { - const aliceBeaconInfo = makeBeaconInfoEvent( - aliceId, - roomId, - { isLive: true }, - '$alice-room1-1', - ); + describe("latestLocationState", () => { + const aliceBeaconInfo = makeBeaconInfoEvent(aliceId, roomId, { isLive: true }, "$alice-room1-1"); - const location1 = makeBeaconEvent( - aliceId, { beaconInfoId: aliceBeaconInfo.getId(), geoUri: 'geo:51,41', timestamp: now + 1 }, - ); - const location2 = makeBeaconEvent( - aliceId, { beaconInfoId: aliceBeaconInfo.getId(), geoUri: 'geo:52,42', timestamp: now + 10000 }, - ); + const location1 = makeBeaconEvent(aliceId, { + beaconInfoId: aliceBeaconInfo.getId(), + geoUri: "geo:51,41", + timestamp: now + 1, + }); + const location2 = makeBeaconEvent(aliceId, { + beaconInfoId: aliceBeaconInfo.getId(), + geoUri: "geo:52,42", + timestamp: now + 10000, + }); - it('renders a live beacon without a location correctly', () => { + it("renders a live beacon without a location correctly", () => { makeRoomWithStateEvents([aliceBeaconInfo], { roomId, mockClient }); const component = getComponent({ mxEvent: aliceBeaconInfo }); expect(component.text()).toEqual("Loading live location..."); }); - it('does nothing on click when a beacon has no location', () => { + it("does nothing on click when a beacon has no location", () => { makeRoomWithStateEvents([aliceBeaconInfo], { roomId, mockClient }); const component = getComponent({ mxEvent: aliceBeaconInfo }); act(() => { - component.find('.mx_MBeaconBody_map').at(0).simulate('click'); + component.find(".mx_MBeaconBody_map").at(0).simulate("click"); }); expect(modalSpy).not.toHaveBeenCalled(); }); - it('renders a live beacon with a location correctly', () => { + it("renders a live beacon with a location correctly", () => { const room = makeRoomWithStateEvents([aliceBeaconInfo], { roomId, mockClient }); const beaconInstance = room.currentState.beacons.get(getBeaconInfoIdentifier(aliceBeaconInfo)); beaconInstance.addLocations([location1]); const component = getComponent({ mxEvent: aliceBeaconInfo }); - expect(component.find('Map').length).toBeTruthy; + expect(component.find("Map").length).toBeTruthy; }); - it('opens maximised map view on click when beacon has a live location', () => { + it("opens maximised map view on click when beacon has a live location", () => { const room = makeRoomWithStateEvents([aliceBeaconInfo], { roomId, mockClient }); const beaconInstance = room.currentState.beacons.get(getBeaconInfoIdentifier(aliceBeaconInfo)); beaconInstance.addLocations([location1]); const component = getComponent({ mxEvent: aliceBeaconInfo }); act(() => { - component.find('Map').simulate('click'); + component.find("Map").simulate("click"); }); // opens modal expect(modalSpy).toHaveBeenCalled(); }); - it('does nothing on click when a beacon has no location', () => { + it("does nothing on click when a beacon has no location", () => { makeRoomWithStateEvents([aliceBeaconInfo], { roomId, mockClient }); const component = getComponent({ mxEvent: aliceBeaconInfo }); act(() => { - component.find('.mx_MBeaconBody_map').at(0).simulate('click'); + component.find(".mx_MBeaconBody_map").at(0).simulate("click"); }); expect(modalSpy).not.toHaveBeenCalled(); }); - it('renders a live beacon with a location correctly', () => { + it("renders a live beacon with a location correctly", () => { const room = makeRoomWithStateEvents([aliceBeaconInfo], { roomId, mockClient }); const beaconInstance = room.currentState.beacons.get(getBeaconInfoIdentifier(aliceBeaconInfo)); beaconInstance.addLocations([location1]); const component = getComponent({ mxEvent: aliceBeaconInfo }); - expect(component.find('Map').length).toBeTruthy; + expect(component.find("Map").length).toBeTruthy; }); - it('opens maximised map view on click when beacon has a live location', () => { + it("opens maximised map view on click when beacon has a live location", () => { const room = makeRoomWithStateEvents([aliceBeaconInfo], { roomId, mockClient }); const beaconInstance = room.currentState.beacons.get(getBeaconInfoIdentifier(aliceBeaconInfo)); beaconInstance.addLocations([location1]); const component = getComponent({ mxEvent: aliceBeaconInfo }); act(() => { - component.find('Map').simulate('click'); + component.find("Map").simulate("click"); }); // opens modal expect(modalSpy).toHaveBeenCalled(); }); - it('updates latest location', () => { + it("updates latest location", () => { const room = makeRoomWithStateEvents([aliceBeaconInfo], { roomId, mockClient }); const component = getComponent({ mxEvent: aliceBeaconInfo }); @@ -349,33 +321,30 @@ describe('', () => { }); }); - describe('redaction', () => { + describe("redaction", () => { const makeEvents = (): { beaconInfoEvent: MatrixEvent; location1: MatrixEvent; location2: MatrixEvent; } => { - const beaconInfoEvent = makeBeaconInfoEvent( - aliceId, - roomId, - { isLive: true }, - '$alice-room1-1', - ); + const beaconInfoEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: true }, "$alice-room1-1"); const location1 = makeBeaconEvent( - aliceId, { beaconInfoId: beaconInfoEvent.getId(), geoUri: 'geo:51,41', timestamp: now + 1 }, + aliceId, + { beaconInfoId: beaconInfoEvent.getId(), geoUri: "geo:51,41", timestamp: now + 1 }, roomId, ); - location1.event.event_id = '1'; + location1.event.event_id = "1"; const location2 = makeBeaconEvent( - aliceId, { beaconInfoId: beaconInfoEvent.getId(), geoUri: 'geo:52,42', timestamp: now + 10000 }, + aliceId, + { beaconInfoId: beaconInfoEvent.getId(), geoUri: "geo:52,42", timestamp: now + 10000 }, roomId, ); - location2.event.event_id = '2'; + location2.event.event_id = "2"; return { beaconInfoEvent, location1, location2 }; }; - const redactionEvent = new MatrixEvent({ type: EventType.RoomRedaction, content: { reason: 'test reason' } }); + const redactionEvent = new MatrixEvent({ type: EventType.RoomRedaction, content: { reason: "test reason" } }); const setupRoomWithBeacon = (beaconInfoEvent, locationEvents: MatrixEvent[] = []) => { const room = makeRoomWithStateEvents([beaconInfoEvent], { roomId, mockClient }); @@ -384,14 +353,14 @@ describe('', () => { }; const mockGetRelationsForEvent = (locationEvents: MatrixEvent[] = []) => { const relations = new Relations(RelationType.Reference, M_BEACON.name, mockClient); - jest.spyOn(relations, 'getRelations').mockReturnValue(locationEvents); + jest.spyOn(relations, "getRelations").mockReturnValue(locationEvents); const getRelationsForEvent = jest.fn().mockReturnValue(relations); return getRelationsForEvent; }; - it('does nothing when getRelationsForEvent is falsy', () => { + it("does nothing when getRelationsForEvent is falsy", () => { const { beaconInfoEvent, location1, location2 } = makeEvents(); setupRoomWithBeacon(beaconInfoEvent, [location1, location2]); @@ -405,10 +374,10 @@ describe('', () => { expect(mockClient.redactEvent).not.toHaveBeenCalled(); }); - it('cleans up redaction listener on unmount', () => { + it("cleans up redaction listener on unmount", () => { const { beaconInfoEvent, location1, location2 } = makeEvents(); setupRoomWithBeacon(beaconInfoEvent, [location1, location2]); - const removeListenerSpy = jest.spyOn(beaconInfoEvent, 'removeListener'); + const removeListenerSpy = jest.spyOn(beaconInfoEvent, "removeListener"); const component = getComponent({ mxEvent: beaconInfoEvent }); @@ -419,7 +388,7 @@ describe('', () => { expect(removeListenerSpy).toHaveBeenCalled(); }); - it('does nothing when beacon has no related locations', async () => { + it("does nothing when beacon has no related locations", async () => { const { beaconInfoEvent } = makeEvents(); // no locations setupRoomWithBeacon(beaconInfoEvent, []); @@ -432,12 +401,14 @@ describe('', () => { }); expect(getRelationsForEvent).toHaveBeenCalledWith( - beaconInfoEvent.getId(), RelationType.Reference, M_BEACON.name, + beaconInfoEvent.getId(), + RelationType.Reference, + M_BEACON.name, ); expect(mockClient.redactEvent).not.toHaveBeenCalled(); }); - it('redacts related locations on beacon redaction', async () => { + it("redacts related locations on beacon redaction", async () => { const { beaconInfoEvent, location1, location2 } = makeEvents(); setupRoomWithBeacon(beaconInfoEvent, [location1, location2]); @@ -450,43 +421,36 @@ describe('', () => { }); expect(getRelationsForEvent).toHaveBeenCalledWith( - beaconInfoEvent.getId(), RelationType.Reference, M_BEACON.name, + beaconInfoEvent.getId(), + RelationType.Reference, + M_BEACON.name, ); expect(mockClient.redactEvent).toHaveBeenCalledTimes(2); - expect(mockClient.redactEvent).toHaveBeenCalledWith( - roomId, - location1.getId(), - undefined, - { reason: 'test reason' }, - ); - expect(mockClient.redactEvent).toHaveBeenCalledWith( - roomId, - location2.getId(), - undefined, - { reason: 'test reason' }, - ); + expect(mockClient.redactEvent).toHaveBeenCalledWith(roomId, location1.getId(), undefined, { + reason: "test reason", + }); + expect(mockClient.redactEvent).toHaveBeenCalledWith(roomId, location2.getId(), undefined, { + reason: "test reason", + }); }); }); - describe('when map display is not configured', () => { + describe("when map display is not configured", () => { beforeEach(() => { // mock map utils to raise MapStyleUrlNotConfigured error - jest.spyOn(mapUtilHooks, 'useMap').mockImplementation( - ({ onError }) => { - onError(new Error(LocationShareError.MapStyleUrlNotConfigured)); - return mockMap; - }); + jest.spyOn(mapUtilHooks, "useMap").mockImplementation(({ onError }) => { + onError(new Error(LocationShareError.MapStyleUrlNotConfigured)); + return mockMap; + }); }); - it('renders maps unavailable error for a live beacon with location', () => { - const beaconInfoEvent = makeBeaconInfoEvent(aliceId, - roomId, - { isLive: true }, - '$alice-room1-1', - ); - const location1 = makeBeaconEvent( - aliceId, { beaconInfoId: beaconInfoEvent.getId(), geoUri: 'geo:51,41', timestamp: now + 1 }, - ); + it("renders maps unavailable error for a live beacon with location", () => { + const beaconInfoEvent = makeBeaconInfoEvent(aliceId, roomId, { isLive: true }, "$alice-room1-1"); + const location1 = makeBeaconEvent(aliceId, { + beaconInfoId: beaconInfoEvent.getId(), + geoUri: "geo:51,41", + timestamp: now + 1, + }); makeRoomWithBeacons(roomId, mockClient, [beaconInfoEvent], [location1]); diff --git a/test/components/views/messages/MImageBody-test.tsx b/test/components/views/messages/MImageBody-test.tsx index b2cbb85602..2399fe0fd3 100644 --- a/test/components/views/messages/MImageBody-test.tsx +++ b/test/components/views/messages/MImageBody-test.tsx @@ -48,8 +48,8 @@ describe("", () => { getIgnoredUsers: jest.fn(), getVersions: jest.fn().mockResolvedValue({ unstable_features: { - 'org.matrix.msc3882': true, - 'org.matrix.msc3886': true, + "org.matrix.msc3882": true, + "org.matrix.msc3886": true, }, }), }); @@ -75,11 +75,13 @@ describe("", () => { it("should show error when encrypted media cannot be downloaded", async () => { fetchMock.getOnce(url, { status: 500 }); - render(); + render( + , + ); await screen.findByText("Error downloading image"); }); @@ -88,11 +90,13 @@ describe("", () => { fetchMock.getOnce(url, "thisistotallyanencryptedpng"); mocked(encrypt.decryptAttachment).mockRejectedValue(new Error("Failed to decrypt")); - render(); + render( + , + ); await screen.findByText("Error decrypting image"); }); diff --git a/test/components/views/messages/MKeyVerificationConclusion-test.tsx b/test/components/views/messages/MKeyVerificationConclusion-test.tsx index 5484282b6f..03966d1673 100644 --- a/test/components/views/messages/MKeyVerificationConclusion-test.tsx +++ b/test/components/views/messages/MKeyVerificationConclusion-test.tsx @@ -14,33 +14,40 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import TestRenderer from 'react-test-renderer'; -import { EventEmitter } from 'events'; -import { MatrixEvent, EventType } from 'matrix-js-sdk/src/matrix'; -import { CryptoEvent } from 'matrix-js-sdk/src/crypto'; -import { UserTrustLevel } from 'matrix-js-sdk/src/crypto/CrossSigning'; -import { VerificationRequest } from 'matrix-js-sdk/src/crypto/verification/request/VerificationRequest'; +import React from "react"; +import TestRenderer from "react-test-renderer"; +import { EventEmitter } from "events"; +import { MatrixEvent, EventType } from "matrix-js-sdk/src/matrix"; +import { CryptoEvent } from "matrix-js-sdk/src/crypto"; +import { UserTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning"; +import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; -import { MatrixClientPeg } from '../../../../src/MatrixClientPeg'; -import MKeyVerificationConclusion from '../../../../src/components/views/messages/MKeyVerificationConclusion'; -import { getMockClientWithEventEmitter } from '../../../test-utils'; +import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; +import MKeyVerificationConclusion from "../../../../src/components/views/messages/MKeyVerificationConclusion"; +import { getMockClientWithEventEmitter } from "../../../test-utils"; -const trustworthy = ({ isCrossSigningVerified: () => true }) as unknown as UserTrustLevel; -const untrustworthy = ({ isCrossSigningVerified: () => false }) as unknown as UserTrustLevel; +const trustworthy = { isCrossSigningVerified: () => true } as unknown as UserTrustLevel; +const untrustworthy = { isCrossSigningVerified: () => false } as unknown as UserTrustLevel; describe("MKeyVerificationConclusion", () => { - const userId = '@user:server'; + const userId = "@user:server"; const mockClient = getMockClientWithEventEmitter({ getRoom: jest.fn(), getUserId: jest.fn().mockReturnValue(userId), checkUserTrust: jest.fn(), }); - const getMockVerificationRequest = ( - { pending, cancelled, done, otherUserId }: - { pending?: boolean, cancelled?: boolean, done?: boolean, otherUserId?: string }, - ) => { + const getMockVerificationRequest = ({ + pending, + cancelled, + done, + otherUserId, + }: { + pending?: boolean; + cancelled?: boolean; + done?: boolean; + otherUserId?: string; + }) => { class MockVerificationRequest extends EventEmitter { constructor( public readonly pending: boolean, @@ -60,41 +67,33 @@ describe("MKeyVerificationConclusion", () => { }); afterAll(() => { - jest.spyOn(MatrixClientPeg, 'get').mockRestore(); + jest.spyOn(MatrixClientPeg, "get").mockRestore(); }); it("shouldn't render if there's no verificationRequest", () => { const event = new MatrixEvent({}); - const renderer = TestRenderer.create( - , - ); + const renderer = TestRenderer.create(); expect(renderer.toJSON()).toBeNull(); }); it("shouldn't render if the verificationRequest is pending", () => { const event = new MatrixEvent({}); event.verificationRequest = getMockVerificationRequest({ pending: true }); - const renderer = TestRenderer.create( - , - ); + const renderer = TestRenderer.create(); expect(renderer.toJSON()).toBeNull(); }); it("shouldn't render if the event type is cancel but the request type isn't", () => { const event = new MatrixEvent({ type: EventType.KeyVerificationCancel }); event.verificationRequest = getMockVerificationRequest({ cancelled: false }); - const renderer = TestRenderer.create( - , - ); + const renderer = TestRenderer.create(); expect(renderer.toJSON()).toBeNull(); }); it("shouldn't render if the event type is done but the request type isn't", () => { const event = new MatrixEvent({ type: "m.key.verification.done" }); event.verificationRequest = getMockVerificationRequest({ done: false }); - const renderer = TestRenderer.create( - , - ); + const renderer = TestRenderer.create(); expect(renderer.toJSON()).toBeNull(); }); @@ -103,9 +102,7 @@ describe("MKeyVerificationConclusion", () => { const event = new MatrixEvent({ type: "m.key.verification.done" }); event.verificationRequest = getMockVerificationRequest({ done: true }); - const renderer = TestRenderer.create( - , - ); + const renderer = TestRenderer.create(); expect(renderer.toJSON()).toBeNull(); }); @@ -114,9 +111,7 @@ describe("MKeyVerificationConclusion", () => { const event = new MatrixEvent({ type: "m.key.verification.done" }); event.verificationRequest = getMockVerificationRequest({ done: true, otherUserId: "@someuser:domain" }); - const renderer = TestRenderer.create( - , - ); + const renderer = TestRenderer.create(); expect(renderer.toJSON()).toBeNull(); mockClient.checkUserTrust.mockReturnValue(trustworthy); diff --git a/test/components/views/messages/MLocationBody-test.tsx b/test/components/views/messages/MLocationBody-test.tsx index 857926dd06..cae3727874 100644 --- a/test/components/views/messages/MLocationBody-test.tsx +++ b/test/components/views/messages/MLocationBody-test.tsx @@ -14,33 +14,33 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; // eslint-disable-next-line deprecate/import import { mount } from "enzyme"; import { LocationAssetType } from "matrix-js-sdk/src/@types/location"; -import { ClientEvent, RoomMember } from 'matrix-js-sdk/src/matrix'; -import maplibregl from 'maplibre-gl'; -import { logger } from 'matrix-js-sdk/src/logger'; -import { act } from 'react-dom/test-utils'; -import { SyncState } from 'matrix-js-sdk/src/sync'; +import { ClientEvent, RoomMember } from "matrix-js-sdk/src/matrix"; +import maplibregl from "maplibre-gl"; +import { logger } from "matrix-js-sdk/src/logger"; +import { act } from "react-dom/test-utils"; +import { SyncState } from "matrix-js-sdk/src/sync"; import MLocationBody from "../../../../src/components/views/messages/MLocationBody"; import MatrixClientContext from "../../../../src/contexts/MatrixClientContext"; import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks"; import { MediaEventHelper } from "../../../../src/utils/MediaEventHelper"; -import Modal from '../../../../src/Modal'; +import Modal from "../../../../src/Modal"; import SdkConfig from "../../../../src/SdkConfig"; -import { TILE_SERVER_WK_KEY } from '../../../../src/utils/WellKnownUtils'; +import { TILE_SERVER_WK_KEY } from "../../../../src/utils/WellKnownUtils"; import { makeLocationEvent } from "../../../test-utils/location"; -import { getMockClientWithEventEmitter } from '../../../test-utils'; +import { getMockClientWithEventEmitter } from "../../../test-utils"; describe("MLocationBody", () => { - describe('', () => { - const roomId = '!room:server'; - const userId = '@user:server'; + describe("", () => { + const roomId = "!room:server"; + const userId = "@user:server"; const mockClient = getMockClientWithEventEmitter({ getClientWellKnown: jest.fn().mockReturnValue({ - [TILE_SERVER_WK_KEY.name]: { map_style_url: 'maps.com' }, + [TILE_SERVER_WK_KEY.name]: { map_style_url: "maps.com" }, }), isGuest: jest.fn().mockReturnValue(false), }); @@ -48,26 +48,27 @@ describe("MLocationBody", () => { const defaultProps = { mxEvent: defaultEvent, highlights: [], - highlightLink: '', + highlightLink: "", onHeightChanged: jest.fn(), onMessageAllowed: jest.fn(), permalinkCreator: {} as RoomPermalinkCreator, mediaEventHelper: {} as MediaEventHelper, }; - const getComponent = (props = {}) => mount(, { - wrappingComponent: MatrixClientContext.Provider, - wrappingComponentProps: { value: mockClient }, - }); + const getComponent = (props = {}) => + mount(, { + wrappingComponent: MatrixClientContext.Provider, + wrappingComponentProps: { value: mockClient }, + }); const getMapErrorComponent = () => { const mockMap = new maplibregl.Map(); mockClient.getClientWellKnown.mockReturnValue({ - [TILE_SERVER_WK_KEY.name]: { map_style_url: 'bad-tile-server.com' }, + [TILE_SERVER_WK_KEY.name]: { map_style_url: "bad-tile-server.com" }, }); const component = getComponent(); // simulate error initialising map in maplibregl // @ts-ignore - mockMap.emit('error', { status: 404 }); + mockMap.emit("error", { status: 404 }); return component; }; @@ -80,33 +81,33 @@ describe("MLocationBody", () => { jest.clearAllMocks(); }); - describe('with error', () => { + describe("with error", () => { let sdkConfigSpy; beforeEach(() => { // eat expected errors to keep console clean - jest.spyOn(logger, 'error').mockImplementation(() => { }); + jest.spyOn(logger, "error").mockImplementation(() => {}); mockClient.getClientWellKnown.mockReturnValue({}); - sdkConfigSpy = jest.spyOn(SdkConfig, 'get').mockReturnValue({}); + sdkConfigSpy = jest.spyOn(SdkConfig, "get").mockReturnValue({}); }); afterAll(() => { sdkConfigSpy.mockRestore(); - jest.spyOn(logger, 'error').mockRestore(); + jest.spyOn(logger, "error").mockRestore(); }); - it('displays correct fallback content without error style when map_style_url is not configured', () => { + it("displays correct fallback content without error style when map_style_url is not configured", () => { const component = getComponent(); expect(component.find(".mx_EventTile_body")).toMatchSnapshot(); }); - it('displays correct fallback content when map_style_url is misconfigured', () => { + it("displays correct fallback content when map_style_url is misconfigured", () => { const component = getMapErrorComponent(); component.setProps({}); expect(component.find(".mx_EventTile_body")).toMatchSnapshot(); }); - it('should clear the error on reconnect', () => { + it("should clear the error on reconnect", () => { const component = getMapErrorComponent(); expect((component.state() as React.ComponentState).error).toBeDefined(); mockClient.emit(ClientEvent.Sync, SyncState.Reconnecting, SyncState.Error); @@ -114,57 +115,58 @@ describe("MLocationBody", () => { }); }); - describe('without error', () => { + describe("without error", () => { beforeEach(() => { mockClient.getClientWellKnown.mockReturnValue({ - [TILE_SERVER_WK_KEY.name]: { map_style_url: 'maps.com' }, + [TILE_SERVER_WK_KEY.name]: { map_style_url: "maps.com" }, }); // MLocationBody uses random number for map id // stabilise for test - jest.spyOn(global.Math, 'random').mockReturnValue(0.123456); + jest.spyOn(global.Math, "random").mockReturnValue(0.123456); }); afterAll(() => { - jest.spyOn(global.Math, 'random').mockRestore(); + jest.spyOn(global.Math, "random").mockRestore(); }); - it('renders map correctly', () => { + it("renders map correctly", () => { const mockMap = new maplibregl.Map(); const component = getComponent(); expect(component).toMatchSnapshot(); // map was centered expect(mockMap.setCenter).toHaveBeenCalledWith({ - lat: 51.5076, lon: -0.1276, + lat: 51.5076, + lon: -0.1276, }); }); - it('opens map dialog on click', () => { - const modalSpy = jest.spyOn(Modal, 'createDialog').mockReturnValue(undefined); + it("opens map dialog on click", () => { + const modalSpy = jest.spyOn(Modal, "createDialog").mockReturnValue(undefined); const component = getComponent(); act(() => { - component.find('Map').at(0).simulate('click'); + component.find("Map").at(0).simulate("click"); }); expect(modalSpy).toHaveBeenCalled(); }); - it('renders marker correctly for a non-self share', () => { + it("renders marker correctly for a non-self share", () => { const mockMap = new maplibregl.Map(); const component = getComponent(); - expect(component.find('SmartMarker').at(0).props()).toEqual( + expect(component.find("SmartMarker").at(0).props()).toEqual( expect.objectContaining({ map: mockMap, - geoUri: 'geo:51.5076,-0.1276', + geoUri: "geo:51.5076,-0.1276", roomMember: undefined, }), ); }); - it('renders marker correctly for a self share', () => { + it("renders marker correctly for a self share", () => { const selfShareEvent = makeLocationEvent("geo:51.5076,-0.1276", LocationAssetType.Self); const member = new RoomMember(roomId, userId); // @ts-ignore cheat assignment to property @@ -172,9 +174,7 @@ describe("MLocationBody", () => { const component = getComponent({ mxEvent: selfShareEvent }); // render self locations with user avatars - expect(component.find('SmartMarker').at(0).props()['roomMember']).toEqual( - member, - ); + expect(component.find("SmartMarker").at(0).props()["roomMember"]).toEqual(member); }); }); }); diff --git a/test/components/views/messages/MPollBody-test.tsx b/test/components/views/messages/MPollBody-test.tsx index f2ffabfac6..c3907a61b9 100644 --- a/test/components/views/messages/MPollBody-test.tsx +++ b/test/components/views/messages/MPollBody-test.tsx @@ -49,7 +49,7 @@ const CHECKED = "mx_MPollBody_option_checked"; const mockClient = getMockClientWithEventEmitter({ getUserId: jest.fn().mockReturnValue("@me:example.com"), - sendEvent: jest.fn().mockReturnValue(Promise.resolve({ "event_id": "fake_send_id" })), + sendEvent: jest.fn().mockReturnValue(Promise.resolve({ event_id: "fake_send_id" })), getRoom: jest.fn(), }); @@ -76,9 +76,7 @@ describe("MPollBody", () => { const ev2 = responseEvent(); const badEvent = badResponseEvent(); - const voteRelations = new RelatedRelations([ - newVoteRelations([ev1, badEvent, ev2]), - ]); + const voteRelations = new RelatedRelations([newVoteRelations([ev1, badEvent, ev2])]); expect( allVotes( { getRoomId: () => "$room" } as MatrixEvent, @@ -87,21 +85,13 @@ describe("MPollBody", () => { new RelatedRelations([newEndRelations([])]), ), ).toEqual([ - new UserVote( - ev1.getTs(), - ev1.getSender(), - ev1.getContent()[M_POLL_RESPONSE.name].answers, - ), + new UserVote(ev1.getTs(), ev1.getSender(), ev1.getContent()[M_POLL_RESPONSE.name].answers), new UserVote( badEvent.getTs(), badEvent.getSender(), [], // should be spoiled ), - new UserVote( - ev2.getTs(), - ev2.getSender(), - ev2.getContent()[M_POLL_RESPONSE.name].answers, - ), + new UserVote(ev2.getTs(), ev2.getSender(), ev2.getContent()[M_POLL_RESPONSE.name].answers), ]); }); @@ -117,13 +107,7 @@ describe("MPollBody", () => { setRedactionAllowedForMeOnly(mockClient); - expect( - pollEndTs( - { getRoomId: () => "$room" } as MatrixEvent, - mockClient, - endRelations, - ), - ).toBe(12); + expect(pollEndTs({ getRoomId: () => "$room" } as MatrixEvent, mockClient, endRelations)).toBe(12); }); it("ignores unauthorised end poll event when finding end ts", () => { @@ -138,13 +122,7 @@ describe("MPollBody", () => { setRedactionAllowedForMeOnly(mockClient); - expect( - pollEndTs( - { getRoomId: () => "$room" } as MatrixEvent, - mockClient, - endRelations, - ), - ).toBe(13); + expect(pollEndTs({ getRoomId: () => "$room" } as MatrixEvent, mockClient, endRelations)).toBe(13); }); it("counts only votes before the end poll event", () => { @@ -157,18 +135,9 @@ describe("MPollBody", () => { responseEvent("ps@matrix.org", "wings", 19), ]), ]); - const endRelations = new RelatedRelations([ - newEndRelations([ - endEvent("@me:example.com", 25), - ]), - ]); + const endRelations = new RelatedRelations([newEndRelations([endEvent("@me:example.com", 25)])]); expect( - allVotes( - { getRoomId: () => "$room" } as MatrixEvent, - MatrixClientPeg.get(), - voteRelations, - endRelations, - ), + allVotes({ getRoomId: () => "$room" } as MatrixEvent, MatrixClientPeg.get(), voteRelations, endRelations), ).toEqual([ new UserVote(13, "sf@matrix.org", ["wings"]), new UserVote(13, "id@matrix.org", ["wings"]), @@ -184,8 +153,7 @@ describe("MPollBody", () => { expect(votesCount(body, "italian")).toBe(""); expect(votesCount(body, "wings")).toBe(""); expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("No votes cast"); - expect(body.find('h2').html()) - .toEqual("

What should we order for the party?

"); + expect(body.find("h2").html()).toEqual("

What should we order for the party?

"); }); it("finds votes from multiple people", () => { @@ -210,9 +178,7 @@ describe("MPollBody", () => { responseEvent("@catrd:example.com", "poutine"), responseEvent("@dune2:example.com", "wings"), ]; - const ends = [ - endEvent("@notallowed:example.com", 12), - ]; + const ends = [endEvent("@notallowed:example.com", 12)]; const body = newMPollBody(votes, ends); // Even though an end event was sent, we render the poll as unfinished @@ -236,27 +202,23 @@ describe("MPollBody", () => { expect(votesCount(body, "poutine")).toBe(""); expect(votesCount(body, "italian")).toBe(""); expect(votesCount(body, "wings")).toBe(""); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe( - "4 votes cast. Vote to see the results"); + expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("4 votes cast. Vote to see the results"); }); it("hides a single vote if I have not voted", () => { - const votes = [ - responseEvent("@alice:example.com", "pizza"), - ]; + const votes = [responseEvent("@alice:example.com", "pizza")]; const body = newMPollBody(votes); expect(votesCount(body, "pizza")).toBe(""); expect(votesCount(body, "poutine")).toBe(""); expect(votesCount(body, "italian")).toBe(""); expect(votesCount(body, "wings")).toBe(""); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe( - "1 vote cast. Vote to see the results"); + expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("1 vote cast. Vote to see the results"); }); it("takes someone's most recent vote if they voted several times", () => { const votes = [ responseEvent("@me:example.com", "pizza", 12), - responseEvent("@me:example.com", "wings", 20), // latest me + responseEvent("@me:example.com", "wings", 20), // latest me responseEvent("@qbert:example.com", "pizza", 14), responseEvent("@qbert:example.com", "poutine", 16), // latest qbert responseEvent("@qbert:example.com", "wings", 15), @@ -321,8 +283,7 @@ describe("MPollBody", () => { const votes = [responseEvent("@me:example.com", "pizza", 100)]; const body = newMPollBody(votes); const props: IBodyProps = body.instance().props as IBodyProps; - const voteRelations = props!.getRelationsForEvent!( - "$mypoll", "m.reference", M_POLL_RESPONSE.name); + const voteRelations = props!.getRelationsForEvent!("$mypoll", "m.reference", M_POLL_RESPONSE.name); expect(voteRelations).toBeDefined(); clickRadio(body, "pizza"); @@ -343,8 +304,7 @@ describe("MPollBody", () => { const votes = [responseEvent("@me:example.com", "pizza")]; const body = newMPollBody(votes); const props: IBodyProps = body.instance().props as IBodyProps; - const voteRelations = props!.getRelationsForEvent!( - "$mypoll", "m.reference", M_POLL_RESPONSE.name); + const voteRelations = props!.getRelationsForEvent!("$mypoll", "m.reference", M_POLL_RESPONSE.name); expect(voteRelations).toBeDefined(); clickRadio(body, "pizza"); @@ -369,10 +329,7 @@ describe("MPollBody", () => { it("highlights my vote even if I did it on another device", () => { // Given I voted italian - const votes = [ - responseEvent("@me:example.com", "italian"), - responseEvent("@nf:example.com", "wings"), - ]; + const votes = [responseEvent("@me:example.com", "italian"), responseEvent("@nf:example.com", "wings")]; const body = newMPollBody(votes); // But I didn't click anything locally @@ -384,10 +341,7 @@ describe("MPollBody", () => { it("ignores extra answers", () => { // When cb votes for 2 things, we consider the first only - const votes = [ - responseEvent("@cb:example.com", ["pizza", "wings"]), - responseEvent("@me:example.com", "wings"), - ]; + const votes = [responseEvent("@cb:example.com", ["pizza", "wings"]), responseEvent("@me:example.com", "wings")]; const body = newMPollBody(votes); expect(votesCount(body, "pizza")).toBe("1 vote"); expect(votesCount(body, "poutine")).toBe("0 votes"); @@ -470,14 +424,12 @@ describe("MPollBody", () => { it("renders the first 20 answers if 21 were given", () => { const answers = Array.from(Array(21).keys()).map((i) => { - return { "id": `id${i}`, [M_TEXT.name]: `Name ${i}` }; + return { id: `id${i}`, [M_TEXT.name]: `Name ${i}` }; }); const votes = []; const ends = []; const body = newMPollBody(votes, ends, answers); - expect( - body.find('.mx_MPollBody_option').length, - ).toBe(20); + expect(body.find(".mx_MPollBody_option").length).toBe(20); }); it("hides scores if I voted but the poll is undisclosed", () => { @@ -493,8 +445,7 @@ describe("MPollBody", () => { expect(votesCount(body, "poutine")).toBe(""); expect(votesCount(body, "italian")).toBe(""); expect(votesCount(body, "wings")).toBe(""); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe( - "Results will be visible when the poll is ended"); + expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Results will be visible when the poll is ended"); }); it("highlights my vote if the poll is undisclosed", () => { @@ -522,16 +473,13 @@ describe("MPollBody", () => { responseEvent("@catrd:example.com", "poutine"), responseEvent("@dune2:example.com", "wings"), ]; - const ends = [ - endEvent("@me:example.com", 12), - ]; + const ends = [endEvent("@me:example.com", 12)]; const body = newMPollBody(votes, ends, null, false); expect(endedVotesCount(body, "pizza")).toBe("3 votes"); expect(endedVotesCount(body, "poutine")).toBe("1 vote"); expect(endedVotesCount(body, "italian")).toBe("0 votes"); expect(endedVotesCount(body, "wings")).toBe("1 vote"); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe( - "Final result based on 5 votes"); + expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 5 votes"); }); it("sends a vote event when I choose an option", () => { @@ -548,9 +496,7 @@ describe("MPollBody", () => { clickRadio(body, "wings"); clickRadio(body, "wings"); clickRadio(body, "wings"); - expect(mockClient.sendEvent).toHaveBeenCalledWith( - ...expectedResponseEventCall("wings"), - ); + expect(mockClient.sendEvent).toHaveBeenCalledWith(...expectedResponseEventCall("wings")); }); it("sends no vote event when I click what I already chose", () => { @@ -576,13 +522,8 @@ describe("MPollBody", () => { }); it("sends no events when I click in an ended poll", () => { - const ends = [ - endEvent("@me:example.com", 25), - ]; - const votes = [ - responseEvent("@uy:example.com", "wings", 15), - responseEvent("@uy:example.com", "poutine", 15), - ]; + const ends = [endEvent("@me:example.com", 25)]; + const votes = [responseEvent("@uy:example.com", "wings", 15), responseEvent("@uy:example.com", "poutine", 15)]; const body = newMPollBody(votes, ends); clickEndedOption(body, "wings"); clickEndedOption(body, "italian"); @@ -622,9 +563,7 @@ describe("MPollBody", () => { responseEvent("@fa:example.com", "poutine", 18), responseEvent("@of:example.com", "poutine", 31), // Late ]; - const ends = [ - endEvent("@me:example.com", 25), - ]; + const ends = [endEvent("@me:example.com", 25)]; expect(runFindTopAnswer(votes, ends)).toEqual("Italian, Pizza and Poutine"); }); @@ -646,7 +585,7 @@ describe("MPollBody", () => { it("counts votes as normal if the poll is ended", () => { const votes = [ responseEvent("@me:example.com", "pizza", 12), - responseEvent("@me:example.com", "wings", 20), // latest me + responseEvent("@me:example.com", "wings", 20), // latest me responseEvent("@qbert:example.com", "pizza", 14), responseEvent("@qbert:example.com", "poutine", 16), // latest qbert responseEvent("@qbert:example.com", "wings", 15), @@ -657,9 +596,7 @@ describe("MPollBody", () => { expect(endedVotesCount(body, "poutine")).toBe("1 vote"); expect(endedVotesCount(body, "italian")).toBe("0 votes"); expect(endedVotesCount(body, "wings")).toBe("1 vote"); - expect( - body.find(".mx_MPollBody_totalVotes").text(), - ).toBe("Final result based on 2 votes"); + expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 2 votes"); }); it("counts a single vote as normal if the poll is ended", () => { @@ -670,9 +607,7 @@ describe("MPollBody", () => { expect(endedVotesCount(body, "poutine")).toBe("1 vote"); expect(endedVotesCount(body, "italian")).toBe("0 votes"); expect(endedVotesCount(body, "wings")).toBe("0 votes"); - expect( - body.find(".mx_MPollBody_totalVotes").text(), - ).toBe("Final result based on 1 vote"); + expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 1 vote"); }); it("shows ended vote counts of different numbers", () => { @@ -692,18 +627,16 @@ describe("MPollBody", () => { expect(endedVotesCount(body, "poutine")).toBe("0 votes"); expect(endedVotesCount(body, "italian")).toBe("0 votes"); expect(endedVotesCount(body, "wings")).toBe("3 votes"); - expect( - body.find(".mx_MPollBody_totalVotes").text(), - ).toBe("Final result based on 5 votes"); + expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 5 votes"); }); it("ignores votes that arrived after poll ended", () => { const votes = [ - responseEvent("@sd:example.com", "wings", 30), // Late + responseEvent("@sd:example.com", "wings", 30), // Late responseEvent("@ff:example.com", "wings", 20), responseEvent("@ut:example.com", "wings", 14), responseEvent("@iu:example.com", "wings", 15), - responseEvent("@jf:example.com", "wings", 35), // Late + responseEvent("@jf:example.com", "wings", 35), // Late responseEvent("@wf:example.com", "pizza", 15), responseEvent("@ld:example.com", "pizza", 15), ]; @@ -714,23 +647,21 @@ describe("MPollBody", () => { expect(endedVotesCount(body, "poutine")).toBe("0 votes"); expect(endedVotesCount(body, "italian")).toBe("0 votes"); expect(endedVotesCount(body, "wings")).toBe("3 votes"); - expect( - body.find(".mx_MPollBody_totalVotes").text(), - ).toBe("Final result based on 5 votes"); + expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 5 votes"); }); it("counts votes that arrived after an unauthorised poll end event", () => { const votes = [ - responseEvent("@sd:example.com", "wings", 30), // Late + responseEvent("@sd:example.com", "wings", 30), // Late responseEvent("@ff:example.com", "wings", 20), responseEvent("@ut:example.com", "wings", 14), responseEvent("@iu:example.com", "wings", 15), - responseEvent("@jf:example.com", "wings", 35), // Late + responseEvent("@jf:example.com", "wings", 35), // Late responseEvent("@wf:example.com", "pizza", 15), responseEvent("@ld:example.com", "pizza", 15), ]; const ends = [ - endEvent("@unauthorised:example.com", 5), // Should be ignored + endEvent("@unauthorised:example.com", 5), // Should be ignored endEvent("@me:example.com", 25), ]; const body = newMPollBody(votes, ends); @@ -739,9 +670,7 @@ describe("MPollBody", () => { expect(endedVotesCount(body, "poutine")).toBe("0 votes"); expect(endedVotesCount(body, "italian")).toBe("0 votes"); expect(endedVotesCount(body, "wings")).toBe("3 votes"); - expect( - body.find(".mx_MPollBody_totalVotes").text(), - ).toBe("Final result based on 5 votes"); + expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 5 votes"); }); it("ignores votes that arrived after the first end poll event", () => { @@ -749,11 +678,11 @@ describe("MPollBody", () => { // "Votes sent on or before the end event's timestamp are valid votes" const votes = [ - responseEvent("@sd:example.com", "wings", 30), // Late + responseEvent("@sd:example.com", "wings", 30), // Late responseEvent("@ff:example.com", "wings", 20), responseEvent("@ut:example.com", "wings", 14), - responseEvent("@iu:example.com", "wings", 25), // Just on time - responseEvent("@jf:example.com", "wings", 35), // Late + responseEvent("@iu:example.com", "wings", 25), // Just on time + responseEvent("@jf:example.com", "wings", 35), // Late responseEvent("@wf:example.com", "pizza", 15), responseEvent("@ld:example.com", "pizza", 15), ]; @@ -768,9 +697,7 @@ describe("MPollBody", () => { expect(endedVotesCount(body, "poutine")).toBe("0 votes"); expect(endedVotesCount(body, "italian")).toBe("0 votes"); expect(endedVotesCount(body, "wings")).toBe("3 votes"); - expect( - body.find(".mx_MPollBody_totalVotes").text(), - ).toBe("Final result based on 5 votes"); + expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 5 votes"); }); it("highlights the winning vote in an ended poll", () => { @@ -788,12 +715,8 @@ describe("MPollBody", () => { expect(endedVoteChecked(body, "pizza")).toBe(false); // Double-check by looking for the endedOptionWinner class - expect( - endedVoteDiv(body, "wings").hasClass("mx_MPollBody_endedOptionWinner"), - ).toBe(true); - expect( - endedVoteDiv(body, "pizza").hasClass("mx_MPollBody_endedOptionWinner"), - ).toBe(false); + expect(endedVoteDiv(body, "wings").hasClass("mx_MPollBody_endedOptionWinner")).toBe(true); + expect(endedVoteDiv(body, "pizza").hasClass("mx_MPollBody_endedOptionWinner")).toBe(false); }); it("highlights multiple winning votes", () => { @@ -836,9 +759,9 @@ describe("MPollBody", () => { it("says poll is not ended if asking for relations returns undefined", () => { const pollEvent = new MatrixEvent({ - "event_id": "$mypoll", - "room_id": "#myroom:example.com", - "content": newPollStart([]), + event_id: "$mypoll", + room_id: "#myroom:example.com", + content: newPollStart([]), }); mockClient.getRoom.mockImplementation((_roomId) => { return { @@ -849,45 +772,38 @@ describe("MPollBody", () => { }, } as unknown as Room; }); - const getRelationsForEvent = - (eventId: string, relationType: string, eventType: string) => { - expect(eventId).toBe("$mypoll"); - expect(relationType).toBe("m.reference"); - expect(M_POLL_END.matches(eventType)).toBe(true); - return undefined; - }; - expect( - isPollEnded( - pollEvent, - MatrixClientPeg.get(), - getRelationsForEvent, - ), - ).toBe(false); + const getRelationsForEvent = (eventId: string, relationType: string, eventType: string) => { + expect(eventId).toBe("$mypoll"); + expect(relationType).toBe("m.reference"); + expect(M_POLL_END.matches(eventType)).toBe(true); + return undefined; + }; + expect(isPollEnded(pollEvent, MatrixClientPeg.get(), getRelationsForEvent)).toBe(false); }); it("Displays edited content and new answer IDs if the poll has been edited", () => { const pollEvent = new MatrixEvent({ - "type": M_POLL_START.name, - "event_id": "$mypoll", - "room_id": "#myroom:example.com", - "content": newPollStart( + type: M_POLL_START.name, + event_id: "$mypoll", + room_id: "#myroom:example.com", + content: newPollStart( [ - { "id": "o1", [M_TEXT.name]: "old answer 1" }, - { "id": "o2", [M_TEXT.name]: "old answer 2" }, + { id: "o1", [M_TEXT.name]: "old answer 1" }, + { id: "o2", [M_TEXT.name]: "old answer 2" }, ], "old question", ), }); const replacingEvent = new MatrixEvent({ - "type": M_POLL_START.name, - "event_id": "$mypollreplacement", - "room_id": "#myroom:example.com", - "content": { + type: M_POLL_START.name, + event_id: "$mypollreplacement", + room_id: "#myroom:example.com", + content: { "m.new_content": newPollStart( [ - { "id": "n1", [M_TEXT.name]: "new answer 1" }, - { "id": "n2", [M_TEXT.name]: "new answer 2" }, - { "id": "n3", [M_TEXT.name]: "new answer 3" }, + { id: "n1", [M_TEXT.name]: "new answer 1" }, + { id: "n2", [M_TEXT.name]: "new answer 2" }, + { id: "n3", [M_TEXT.name]: "new answer 3" }, ], "new question", ), @@ -895,18 +811,15 @@ describe("MPollBody", () => { }); pollEvent.makeReplaced(replacingEvent); const body = newMPollBodyFromEvent(pollEvent, []); - expect(body.find('h2').html()) - .toEqual( - "

new question" - + " (edited)" - + "

", - ); + expect(body.find("h2").html()).toEqual( + "

new question" + ' (edited)' + "

", + ); const inputs = body.find('input[type="radio"]'); expect(inputs).toHaveLength(3); expect(inputs.at(0).prop("value")).toEqual("n1"); expect(inputs.at(1).prop("value")).toEqual("n2"); expect(inputs.at(2).prop("value")).toEqual("n3"); - const options = body.find('.mx_MPollBody_optionText'); + const options = body.find(".mx_MPollBody_optionText"); expect(options).toHaveLength(3); expect(options.at(0).text()).toEqual("new answer 1"); expect(options.at(1).text()).toEqual("new answer 2"); @@ -1027,10 +940,7 @@ function newEndRelations(relationEvents: Array): Relations { return newRelations(relationEvents, M_POLL_END.name); } -function newRelations( - relationEvents: Array, - eventType: string, -): Relations { +function newRelations(relationEvents: Array, eventType: string): Relations { const voteRelations = new Relations("m.reference", eventType, null); for (const ev of relationEvents) { voteRelations.addEvent(ev); @@ -1045,10 +955,10 @@ function newMPollBody( disclosed = true, ): ReactWrapper { const mxEvent = new MatrixEvent({ - "type": M_POLL_START.name, - "event_id": "$mypoll", - "room_id": "#myroom:example.com", - "content": newPollStart(answers, null, disclosed), + type: M_POLL_START.name, + event_id: "$mypoll", + room_id: "#myroom:example.com", + content: newPollStart(answers, null, disclosed), }); return newMPollBodyFromEvent(mxEvent, relationEvents, endEvents); } @@ -1060,10 +970,10 @@ function newMPollBodyFromEvent( ): ReactWrapper { const voteRelations = newVoteRelations(relationEvents); const endRelations = newEndRelations(endEvents); - return mount( { + return mount( + { expect(eventId).toBe("$mypoll"); expect(relationType).toBe("m.reference"); if (M_POLL_RESPONSE.matches(eventType)) { @@ -1073,22 +983,22 @@ function newMPollBodyFromEvent( } else { fail("Unexpected eventType: " + eventType); } - } - } - - // We don't use any of these props, but they're required. - highlightLink="unused" - highlights={[]} - mediaEventHelper={null} - onHeightChanged={() => {}} - onMessageAllowed={() => {}} - permalinkCreator={null} - />, { - wrappingComponent: MatrixClientContext.Provider, - wrappingComponentProps: { - value: mockClient, + }} + // We don't use any of these props, but they're required. + highlightLink="unused" + highlights={[]} + mediaEventHelper={null} + onHeightChanged={() => {}} + onMessageAllowed={() => {}} + permalinkCreator={null} + />, + { + wrappingComponent: MatrixClientContext.Provider, + wrappingComponentProps: { + value: mockClient, + }, }, - }); + ); } function clickRadio(wrapper: ReactWrapper, value: string) { @@ -1104,21 +1014,15 @@ function clickEndedOption(wrapper: ReactWrapper, value: string) { } function voteButton(wrapper: ReactWrapper, value: string): ReactWrapper { - return wrapper.find( - `div.mx_MPollBody_option`, - ).findWhere(w => w.key() === value); + return wrapper.find(`div.mx_MPollBody_option`).findWhere((w) => w.key() === value); } function votesCount(wrapper: ReactWrapper, value: string): string { - return wrapper.find( - `StyledRadioButton[value="${value}"] .mx_MPollBody_optionVoteCount`, - ).text(); + return wrapper.find(`StyledRadioButton[value="${value}"] .mx_MPollBody_optionVoteCount`).text(); } function endedVoteChecked(wrapper: ReactWrapper, value: string): boolean { - return endedVoteDiv(wrapper, value) - .closest(".mx_MPollBody_option") - .hasClass("mx_MPollBody_option_checked"); + return endedVoteDiv(wrapper, value).closest(".mx_MPollBody_option").hasClass("mx_MPollBody_option_checked"); } function endedVoteDiv(wrapper: ReactWrapper, value: string): ReactWrapper { @@ -1126,22 +1030,16 @@ function endedVoteDiv(wrapper: ReactWrapper, value: string): ReactWrapper { } function endedVotesCount(wrapper: ReactWrapper, value: string): string { - return wrapper.find( - `div[data-value="${value}"] .mx_MPollBody_optionVoteCount`, - ).text(); + return wrapper.find(`div[data-value="${value}"] .mx_MPollBody_optionVoteCount`).text(); } -function newPollStart( - answers?: POLL_ANSWER[], - question?: string, - disclosed = true, -): M_POLL_START_EVENT_CONTENT { +function newPollStart(answers?: POLL_ANSWER[], question?: string, disclosed = true): M_POLL_START_EVENT_CONTENT { if (!answers) { answers = [ - { "id": "pizza", [M_TEXT.name]: "Pizza" }, - { "id": "poutine", [M_TEXT.name]: "Poutine" }, - { "id": "italian", [M_TEXT.name]: "Italian" }, - { "id": "wings", [M_TEXT.name]: "Wings" }, + { id: "pizza", [M_TEXT.name]: "Pizza" }, + { id: "poutine", [M_TEXT.name]: "Poutine" }, + { id: "italian", [M_TEXT.name]: "Italian" }, + { id: "wings", [M_TEXT.name]: "Wings" }, ]; } @@ -1149,43 +1047,35 @@ function newPollStart( question = "What should we order for the party?"; } - const answersFallback = answers - .map((a, i) => `${i + 1}. ${a[M_TEXT.name]}`) - .join("\n"); + const answersFallback = answers.map((a, i) => `${i + 1}. ${a[M_TEXT.name]}`).join("\n"); const fallback = `${question}\n${answersFallback}`; return { [M_POLL_START.name]: { - "question": { + question: { [M_TEXT.name]: question, }, - "kind": ( - disclosed - ? M_POLL_KIND_DISCLOSED.name - : M_POLL_KIND_UNDISCLOSED.name - ), - "answers": answers, + kind: disclosed ? M_POLL_KIND_DISCLOSED.name : M_POLL_KIND_UNDISCLOSED.name, + answers: answers, }, [M_TEXT.name]: fallback, }; } function badResponseEvent(): MatrixEvent { - return new MatrixEvent( - { - "event_id": nextId(), - "type": M_POLL_RESPONSE.name, - "sender": "@malicious:example.com", - "content": { - "m.relates_to": { - "rel_type": "m.reference", - "event_id": "$mypoll", - }, - // Does not actually contain a response + return new MatrixEvent({ + event_id: nextId(), + type: M_POLL_RESPONSE.name, + sender: "@malicious:example.com", + content: { + "m.relates_to": { + rel_type: "m.reference", + event_id: "$mypoll", }, + // Does not actually contain a response }, - ); + }); } function responseEvent( @@ -1194,116 +1084,103 @@ function responseEvent( ts = 0, ): MatrixEvent { const ans = typeof answers === "string" ? [answers] : answers; - return new MatrixEvent( - { - "event_id": nextId(), - "room_id": "#myroom:example.com", - "origin_server_ts": ts, - "type": M_POLL_RESPONSE.name, - "sender": sender, - "content": { - "m.relates_to": { - "rel_type": "m.reference", - "event_id": "$mypoll", - }, - [M_POLL_RESPONSE.name]: { - "answers": ans, - }, + return new MatrixEvent({ + event_id: nextId(), + room_id: "#myroom:example.com", + origin_server_ts: ts, + type: M_POLL_RESPONSE.name, + sender: sender, + content: { + "m.relates_to": { + rel_type: "m.reference", + event_id: "$mypoll", + }, + [M_POLL_RESPONSE.name]: { + answers: ans, }, }, - ); + }); } function expectedResponseEvent(answer: string) { return { - "content": { + content: { [M_POLL_RESPONSE.name]: { - "answers": [answer], + answers: [answer], }, "m.relates_to": { - "event_id": "$mypoll", - "rel_type": "m.reference", + event_id: "$mypoll", + rel_type: "m.reference", }, }, - "roomId": "#myroom:example.com", - "eventType": M_POLL_RESPONSE.name, - "txnId": undefined, - "callback": undefined, + roomId: "#myroom:example.com", + eventType: M_POLL_RESPONSE.name, + txnId: undefined, + callback: undefined, }; } function expectedResponseEventCall(answer: string) { - const { - content, roomId, eventType, - } = expectedResponseEvent(answer); - return [ - roomId, eventType, content, - ]; + const { content, roomId, eventType } = expectedResponseEvent(answer); + return [roomId, eventType, content]; } -function endEvent( - sender = "@me:example.com", - ts = 0, -): MatrixEvent { - return new MatrixEvent( - { - "event_id": nextId(), - "room_id": "#myroom:example.com", - "origin_server_ts": ts, - "type": M_POLL_END.name, - "sender": sender, - "content": { - "m.relates_to": { - "rel_type": "m.reference", - "event_id": "$mypoll", - }, - [M_POLL_END.name]: {}, - [M_TEXT.name]: "The poll has ended. Something.", +function endEvent(sender = "@me:example.com", ts = 0): MatrixEvent { + return new MatrixEvent({ + event_id: nextId(), + room_id: "#myroom:example.com", + origin_server_ts: ts, + type: M_POLL_END.name, + sender: sender, + content: { + "m.relates_to": { + rel_type: "m.reference", + event_id: "$mypoll", }, + [M_POLL_END.name]: {}, + [M_TEXT.name]: "The poll has ended. Something.", }, - ); + }); } function runIsPollEnded(ends: MatrixEvent[]) { const pollEvent = new MatrixEvent({ - "event_id": "$mypoll", - "room_id": "#myroom:example.com", - "type": M_POLL_START.name, - "content": newPollStart(), + event_id: "$mypoll", + room_id: "#myroom:example.com", + type: M_POLL_START.name, + content: newPollStart(), }); setRedactionAllowedForMeOnly(mockClient); - const getRelationsForEvent = - (eventId: string, relationType: string, eventType: string) => { - expect(eventId).toBe("$mypoll"); - expect(relationType).toBe("m.reference"); - expect(M_POLL_END.matches(eventType)).toBe(true); - return newEndRelations(ends); - }; + const getRelationsForEvent = (eventId: string, relationType: string, eventType: string) => { + expect(eventId).toBe("$mypoll"); + expect(relationType).toBe("m.reference"); + expect(M_POLL_END.matches(eventType)).toBe(true); + return newEndRelations(ends); + }; return isPollEnded(pollEvent, mockClient, getRelationsForEvent); } function runFindTopAnswer(votes: MatrixEvent[], ends: MatrixEvent[]) { const pollEvent = new MatrixEvent({ - "event_id": "$mypoll", - "room_id": "#myroom:example.com", - "type": M_POLL_START.name, - "content": newPollStart(), + event_id: "$mypoll", + room_id: "#myroom:example.com", + type: M_POLL_START.name, + content: newPollStart(), }); - const getRelationsForEvent = - (eventId: string, relationType: string, eventType: string) => { - expect(eventId).toBe("$mypoll"); - expect(relationType).toBe("m.reference"); - if (M_POLL_RESPONSE.matches(eventType)) { - return newVoteRelations(votes); - } else if (M_POLL_END.matches(eventType)) { - return newEndRelations(ends); - } else { - fail(`eventType should be end or vote but was ${eventType}`); - } - }; + const getRelationsForEvent = (eventId: string, relationType: string, eventType: string) => { + expect(eventId).toBe("$mypoll"); + expect(relationType).toBe("m.reference"); + if (M_POLL_RESPONSE.matches(eventType)) { + return newVoteRelations(votes); + } else if (M_POLL_END.matches(eventType)) { + return newEndRelations(ends); + } else { + fail(`eventType should be end or vote but was ${eventType}`); + } + }; return findTopAnswer(pollEvent, MatrixClientPeg.get(), getRelationsForEvent); } diff --git a/test/components/views/messages/MVideoBody-test.tsx b/test/components/views/messages/MVideoBody-test.tsx index 37cb398e49..cbfed1f306 100644 --- a/test/components/views/messages/MVideoBody-test.tsx +++ b/test/components/views/messages/MVideoBody-test.tsx @@ -14,25 +14,26 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import { MatrixEvent } from 'matrix-js-sdk/src/matrix'; -import { render, RenderResult } from '@testing-library/react'; +import React from "react"; +import { MatrixEvent } from "matrix-js-sdk/src/matrix"; +import { render, RenderResult } from "@testing-library/react"; import MatrixClientContext from "../../../../src/contexts/MatrixClientContext"; import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks"; import { MediaEventHelper } from "../../../../src/utils/MediaEventHelper"; -import { getMockClientWithEventEmitter } from '../../../test-utils'; -import MVideoBody from '../../../../src/components/views/messages/MVideoBody'; +import { getMockClientWithEventEmitter } from "../../../test-utils"; +import MVideoBody from "../../../../src/components/views/messages/MVideoBody"; -jest.mock( - "../../../../src/customisations/Media", - () => { - return { mediaFromContent: () => { return { isEncrypted: false }; } }; - }, -); +jest.mock("../../../../src/customisations/Media", () => { + return { + mediaFromContent: () => { + return { isEncrypted: false }; + }, + }; +}); describe("MVideoBody", () => { - it('does not crash when given a portrait image', () => { + it("does not crash when given a portrait image", () => { // Check for an unreliable crash caused by a fractional-sized // image dimension being used for a CanvasImageData. const { asFragment } = makeMVideoBody(720, 1280); @@ -68,7 +69,7 @@ function makeMVideoBody(w: number, h: number): RenderResult { const defaultProps = { mxEvent: event, highlights: [], - highlightLink: '', + highlightLink: "", onHeightChanged: jest.fn(), onMessageAllowed: jest.fn(), permalinkCreator: {} as RoomPermalinkCreator, diff --git a/test/components/views/messages/MessageActionBar-test.tsx b/test/components/views/messages/MessageActionBar-test.tsx index 670d39ec64..d3eeb0e3b6 100644 --- a/test/components/views/messages/MessageActionBar-test.tsx +++ b/test/components/views/messages/MessageActionBar-test.tsx @@ -14,57 +14,50 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import { render, fireEvent } from '@testing-library/react'; -import { act } from 'react-test-renderer'; -import { - EventType, - EventStatus, - MatrixEvent, - MatrixEventEvent, - MsgType, - Room, -} from 'matrix-js-sdk/src/matrix'; -import { FeatureSupport, Thread } from 'matrix-js-sdk/src/models/thread'; +import React from "react"; +import { render, fireEvent } from "@testing-library/react"; +import { act } from "react-test-renderer"; +import { EventType, EventStatus, MatrixEvent, MatrixEventEvent, MsgType, Room } from "matrix-js-sdk/src/matrix"; +import { FeatureSupport, Thread } from "matrix-js-sdk/src/models/thread"; -import MessageActionBar from '../../../../src/components/views/messages/MessageActionBar'; +import MessageActionBar from "../../../../src/components/views/messages/MessageActionBar"; import { getMockClientWithEventEmitter, mockClientMethodsUser, mockClientMethodsEvents, makeBeaconInfoEvent, -} from '../../../test-utils'; -import { RoomPermalinkCreator } from '../../../../src/utils/permalinks/Permalinks'; -import RoomContext, { TimelineRenderingType } from '../../../../src/contexts/RoomContext'; -import { IRoomState } from '../../../../src/components/structures/RoomView'; -import dispatcher from '../../../../src/dispatcher/dispatcher'; -import SettingsStore from '../../../../src/settings/SettingsStore'; -import { Action } from '../../../../src/dispatcher/actions'; -import { UserTab } from '../../../../src/components/views/dialogs/UserTab'; +} from "../../../test-utils"; +import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks"; +import RoomContext, { TimelineRenderingType } from "../../../../src/contexts/RoomContext"; +import { IRoomState } from "../../../../src/components/structures/RoomView"; +import dispatcher from "../../../../src/dispatcher/dispatcher"; +import SettingsStore from "../../../../src/settings/SettingsStore"; +import { Action } from "../../../../src/dispatcher/actions"; +import { UserTab } from "../../../../src/components/views/dialogs/UserTab"; -jest.mock('../../../../src/dispatcher/dispatcher'); +jest.mock("../../../../src/dispatcher/dispatcher"); -describe('', () => { - const userId = '@alice:server.org'; - const roomId = '!room:server.org'; +describe("", () => { + const userId = "@alice:server.org"; + const roomId = "!room:server.org"; const alicesMessageEvent = new MatrixEvent({ type: EventType.RoomMessage, sender: userId, room_id: roomId, content: { msgtype: MsgType.Text, - body: 'Hello', + body: "Hello", }, event_id: "$alices_message", }); const bobsMessageEvent = new MatrixEvent({ type: EventType.RoomMessage, - sender: '@bob:server.org', + sender: "@bob:server.org", room_id: roomId, content: { msgtype: MsgType.Text, - body: 'I am bob', + body: "I am bob", }, event_id: "$bobs_message", }); @@ -84,7 +77,7 @@ describe('', () => { const localStorageMock = (() => { let store = {}; return { - getItem: jest.fn().mockImplementation(key => store[key] ?? null), + getItem: jest.fn().mockImplementation((key) => store[key] ?? null), setItem: jest.fn().mockImplementation((key, value) => { store[key] = value; }), @@ -94,13 +87,13 @@ describe('', () => { removeItem: jest.fn().mockImplementation((key) => delete store[key]), }; })(); - Object.defineProperty(window, 'localStorage', { + Object.defineProperty(window, "localStorage", { value: localStorageMock, writable: true, }); const room = new Room(roomId, client, userId); - jest.spyOn(room, 'getPendingEvents').mockReturnValue([]); + jest.spyOn(room, "getPendingEvents").mockReturnValue([]); client.getRoom.mockReturnValue(room); @@ -121,22 +114,23 @@ describe('', () => { render( - ); + , + ); beforeEach(() => { jest.clearAllMocks(); alicesMessageEvent.setStatus(EventStatus.SENT); - jest.spyOn(SettingsStore, 'getValue').mockReturnValue(false); - jest.spyOn(SettingsStore, 'setValue').mockResolvedValue(undefined); + jest.spyOn(SettingsStore, "getValue").mockReturnValue(false); + jest.spyOn(SettingsStore, "setValue").mockResolvedValue(undefined); }); afterAll(() => { - jest.spyOn(SettingsStore, 'getValue').mockRestore(); - jest.spyOn(SettingsStore, 'setValue').mockRestore(); + jest.spyOn(SettingsStore, "getValue").mockRestore(); + jest.spyOn(SettingsStore, "setValue").mockRestore(); }); - it('kills event listeners on unmount', () => { - const offSpy = jest.spyOn(alicesMessageEvent, 'off').mockClear(); + it("kills event listeners on unmount", () => { + const offSpy = jest.spyOn(alicesMessageEvent, "off").mockClear(); const wrapper = getComponent({ mxEvent: alicesMessageEvent }); act(() => { @@ -150,24 +144,24 @@ describe('', () => { expect(client.decryptEventIfNeeded).toHaveBeenCalled(); }); - describe('decryption', () => { - it('decrypts event if needed', () => { + describe("decryption", () => { + it("decrypts event if needed", () => { getComponent({ mxEvent: alicesMessageEvent }); expect(client.decryptEventIfNeeded).toHaveBeenCalled(); }); - it('updates component on decrypted event', () => { + it("updates component on decrypted event", () => { const decryptingEvent = new MatrixEvent({ type: EventType.RoomMessageEncrypted, sender: userId, room_id: roomId, content: {}, }); - jest.spyOn(decryptingEvent, 'isBeingDecrypted').mockReturnValue(true); + jest.spyOn(decryptingEvent, "isBeingDecrypted").mockReturnValue(true); const { queryByLabelText } = getComponent({ mxEvent: decryptingEvent }); // still encrypted event is not actionable => no reply button - expect(queryByLabelText('Reply')).toBeFalsy(); + expect(queryByLabelText("Reply")).toBeFalsy(); act(() => { // ''decrypt'' the event @@ -177,46 +171,46 @@ describe('', () => { }); // new available actions after decryption - expect(queryByLabelText('Reply')).toBeTruthy(); + expect(queryByLabelText("Reply")).toBeTruthy(); }); }); - describe('status', () => { - it('updates component when event status changes', () => { + describe("status", () => { + it("updates component when event status changes", () => { alicesMessageEvent.setStatus(EventStatus.QUEUED); const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent }); // pending event status, cancel action available - expect(queryByLabelText('Delete')).toBeTruthy(); + expect(queryByLabelText("Delete")).toBeTruthy(); act(() => { alicesMessageEvent.setStatus(EventStatus.SENT); }); // event is sent, no longer cancelable - expect(queryByLabelText('Delete')).toBeFalsy(); + expect(queryByLabelText("Delete")).toBeFalsy(); }); }); - describe('redaction', () => { + describe("redaction", () => { // this doesn't do what it's supposed to // because beforeRedaction event is fired... before redaction // event is unchanged at point when this component updates // TODO file bug - xit('updates component on before redaction event', () => { + xit("updates component on before redaction event", () => { const event = new MatrixEvent({ type: EventType.RoomMessage, sender: userId, room_id: roomId, content: { msgtype: MsgType.Text, - body: 'Hello', + body: "Hello", }, }); const { queryByLabelText } = getComponent({ mxEvent: event }); // no pending redaction => no delete button - expect(queryByLabelText('Delete')).toBeFalsy(); + expect(queryByLabelText("Delete")).toBeFalsy(); act(() => { const redactionEvent = new MatrixEvent({ @@ -229,110 +223,110 @@ describe('', () => { }); // updated with local redaction event, delete now available - expect(queryByLabelText('Delete')).toBeTruthy(); + expect(queryByLabelText("Delete")).toBeTruthy(); }); }); - describe('options button', () => { - it('renders options menu', () => { + describe("options button", () => { + it("renders options menu", () => { const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent }); - expect(queryByLabelText('Options')).toBeTruthy(); + expect(queryByLabelText("Options")).toBeTruthy(); }); - it('opens message context menu on click', () => { + it("opens message context menu on click", () => { const { getByTestId, queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent }); act(() => { - fireEvent.click(queryByLabelText('Options')); + fireEvent.click(queryByLabelText("Options")); }); - expect(getByTestId('mx_MessageContextMenu')).toBeTruthy(); + expect(getByTestId("mx_MessageContextMenu")).toBeTruthy(); }); }); - describe('reply button', () => { - it('renders reply button on own actionable event', () => { + describe("reply button", () => { + it("renders reply button on own actionable event", () => { const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent }); - expect(queryByLabelText('Reply')).toBeTruthy(); + expect(queryByLabelText("Reply")).toBeTruthy(); }); - it('renders reply button on others actionable event', () => { + it("renders reply button on others actionable event", () => { const { queryByLabelText } = getComponent({ mxEvent: bobsMessageEvent }, { canSendMessages: true }); - expect(queryByLabelText('Reply')).toBeTruthy(); + expect(queryByLabelText("Reply")).toBeTruthy(); }); - it('does not render reply button on non-actionable event', () => { + it("does not render reply button on non-actionable event", () => { // redacted event is not actionable const { queryByLabelText } = getComponent({ mxEvent: redactedEvent }); - expect(queryByLabelText('Reply')).toBeFalsy(); + expect(queryByLabelText("Reply")).toBeFalsy(); }); - it('does not render reply button when user cannot send messaged', () => { + it("does not render reply button when user cannot send messaged", () => { // redacted event is not actionable const { queryByLabelText } = getComponent({ mxEvent: redactedEvent }, { canSendMessages: false }); - expect(queryByLabelText('Reply')).toBeFalsy(); + expect(queryByLabelText("Reply")).toBeFalsy(); }); - it('dispatches reply event on click', () => { + it("dispatches reply event on click", () => { const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent }); act(() => { - fireEvent.click(queryByLabelText('Reply')); + fireEvent.click(queryByLabelText("Reply")); }); expect(dispatcher.dispatch).toHaveBeenCalledWith({ - action: 'reply_to_event', + action: "reply_to_event", event: alicesMessageEvent, context: TimelineRenderingType.Room, }); }); }); - describe('react button', () => { - it('renders react button on own actionable event', () => { + describe("react button", () => { + it("renders react button on own actionable event", () => { const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent }); - expect(queryByLabelText('React')).toBeTruthy(); + expect(queryByLabelText("React")).toBeTruthy(); }); - it('renders react button on others actionable event', () => { + it("renders react button on others actionable event", () => { const { queryByLabelText } = getComponent({ mxEvent: bobsMessageEvent }); - expect(queryByLabelText('React')).toBeTruthy(); + expect(queryByLabelText("React")).toBeTruthy(); }); - it('does not render react button on non-actionable event', () => { + it("does not render react button on non-actionable event", () => { // redacted event is not actionable const { queryByLabelText } = getComponent({ mxEvent: redactedEvent }); - expect(queryByLabelText('React')).toBeFalsy(); + expect(queryByLabelText("React")).toBeFalsy(); }); - it('does not render react button when user cannot react', () => { + it("does not render react button when user cannot react", () => { // redacted event is not actionable const { queryByLabelText } = getComponent({ mxEvent: redactedEvent }, { canReact: false }); - expect(queryByLabelText('React')).toBeFalsy(); + expect(queryByLabelText("React")).toBeFalsy(); }); - it('opens reaction picker on click', () => { + it("opens reaction picker on click", () => { const { queryByLabelText, getByTestId } = getComponent({ mxEvent: alicesMessageEvent }); act(() => { - fireEvent.click(queryByLabelText('React')); + fireEvent.click(queryByLabelText("React")); }); - expect(getByTestId('mx_EmojiPicker')).toBeTruthy(); + expect(getByTestId("mx_EmojiPicker")).toBeTruthy(); }); }); - describe('cancel button', () => { - it('renders cancel button for an event with a cancelable status', () => { + describe("cancel button", () => { + it("renders cancel button for an event with a cancelable status", () => { alicesMessageEvent.setStatus(EventStatus.QUEUED); const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent }); - expect(queryByLabelText('Delete')).toBeTruthy(); + expect(queryByLabelText("Delete")).toBeTruthy(); }); - it('renders cancel button for an event with a pending edit', () => { + it("renders cancel button for an event with a pending edit", () => { const event = new MatrixEvent({ type: EventType.RoomMessage, sender: userId, room_id: roomId, content: { msgtype: MsgType.Text, - body: 'Hello', + body: "Hello", }, }); event.setStatus(EventStatus.SENT); @@ -342,23 +336,23 @@ describe('', () => { room_id: roomId, content: { msgtype: MsgType.Text, - body: 'replacing event body', + body: "replacing event body", }, }); replacingEvent.setStatus(EventStatus.QUEUED); event.makeReplaced(replacingEvent); const { queryByLabelText } = getComponent({ mxEvent: event }); - expect(queryByLabelText('Delete')).toBeTruthy(); + expect(queryByLabelText("Delete")).toBeTruthy(); }); - it('renders cancel button for an event with a pending redaction', () => { + it("renders cancel button for an event with a pending redaction", () => { const event = new MatrixEvent({ type: EventType.RoomMessage, sender: userId, room_id: roomId, content: { msgtype: MsgType.Text, - body: 'Hello', + body: "Hello", }, }); event.setStatus(EventStatus.SENT); @@ -372,45 +366,45 @@ describe('', () => { event.markLocallyRedacted(redactionEvent); const { queryByLabelText } = getComponent({ mxEvent: event }); - expect(queryByLabelText('Delete')).toBeTruthy(); + expect(queryByLabelText("Delete")).toBeTruthy(); }); - it('renders cancel and retry button for an event with NOT_SENT status', () => { + it("renders cancel and retry button for an event with NOT_SENT status", () => { alicesMessageEvent.setStatus(EventStatus.NOT_SENT); const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent }); - expect(queryByLabelText('Retry')).toBeTruthy(); - expect(queryByLabelText('Delete')).toBeTruthy(); + expect(queryByLabelText("Retry")).toBeTruthy(); + expect(queryByLabelText("Delete")).toBeTruthy(); }); - it.todo('unsends event on cancel click'); - it.todo('retrys event on retry click'); + it.todo("unsends event on cancel click"); + it.todo("retrys event on retry click"); }); - describe('thread button', () => { + describe("thread button", () => { beforeEach(() => { Thread.setServerSideSupport(FeatureSupport.Stable); }); - describe('when threads feature is not enabled', () => { - it('does not render thread button when threads does not have server support', () => { - jest.spyOn(SettingsStore, 'getValue').mockReturnValue(false); + describe("when threads feature is not enabled", () => { + it("does not render thread button when threads does not have server support", () => { + jest.spyOn(SettingsStore, "getValue").mockReturnValue(false); Thread.setServerSideSupport(FeatureSupport.None); const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent }); - expect(queryByLabelText('Reply in thread')).toBeFalsy(); + expect(queryByLabelText("Reply in thread")).toBeFalsy(); }); - it('renders thread button when threads has server support', () => { - jest.spyOn(SettingsStore, 'getValue').mockReturnValue(false); + it("renders thread button when threads has server support", () => { + jest.spyOn(SettingsStore, "getValue").mockReturnValue(false); const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent }); - expect(queryByLabelText('Reply in thread')).toBeTruthy(); + expect(queryByLabelText("Reply in thread")).toBeTruthy(); }); - it('opens user settings on click', () => { - jest.spyOn(SettingsStore, 'getValue').mockReturnValue(false); + it("opens user settings on click", () => { + jest.spyOn(SettingsStore, "getValue").mockReturnValue(false); const { getByLabelText } = getComponent({ mxEvent: alicesMessageEvent }); act(() => { - fireEvent.click(getByLabelText('Reply in thread')); + fireEvent.click(getByLabelText("Reply in thread")); }); expect(dispatcher.dispatch).toHaveBeenCalledWith({ @@ -420,27 +414,27 @@ describe('', () => { }); }); - describe('when threads feature is enabled', () => { + describe("when threads feature is enabled", () => { beforeEach(() => { - jest.spyOn(SettingsStore, 'getValue').mockImplementation(setting => setting === 'feature_thread'); + jest.spyOn(SettingsStore, "getValue").mockImplementation((setting) => setting === "feature_thread"); }); - it('renders thread button on own actionable event', () => { + it("renders thread button on own actionable event", () => { const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent }); - expect(queryByLabelText('Reply in thread')).toBeTruthy(); + expect(queryByLabelText("Reply in thread")).toBeTruthy(); }); - it('does not render thread button for a beacon_info event', () => { + it("does not render thread button for a beacon_info event", () => { const beaconInfoEvent = makeBeaconInfoEvent(userId, roomId); const { queryByLabelText } = getComponent({ mxEvent: beaconInfoEvent }); - expect(queryByLabelText('Reply in thread')).toBeFalsy(); + expect(queryByLabelText("Reply in thread")).toBeFalsy(); }); - it('opens thread on click', () => { + it("opens thread on click", () => { const { getByLabelText } = getComponent({ mxEvent: alicesMessageEvent }); act(() => { - fireEvent.click(getByLabelText('Reply in thread')); + fireEvent.click(getByLabelText("Reply in thread")); }); expect(dispatcher.dispatch).toHaveBeenCalledWith({ @@ -450,26 +444,26 @@ describe('', () => { }); }); - it('opens parent thread for a thread reply message', () => { + it("opens parent thread for a thread reply message", () => { const threadReplyEvent = new MatrixEvent({ type: EventType.RoomMessage, sender: userId, room_id: roomId, content: { msgtype: MsgType.Text, - body: 'this is a thread reply', + body: "this is a thread reply", }, }); // mock the thread stuff - jest.spyOn(threadReplyEvent, 'isThreadRoot', 'get').mockReturnValue(false); + jest.spyOn(threadReplyEvent, "isThreadRoot", "get").mockReturnValue(false); // set alicesMessageEvent as the root event - jest.spyOn(threadReplyEvent, 'getThread').mockReturnValue( - { rootEvent: alicesMessageEvent } as unknown as Thread, - ); + jest.spyOn(threadReplyEvent, "getThread").mockReturnValue({ + rootEvent: alicesMessageEvent, + } as unknown as Thread); const { getByLabelText } = getComponent({ mxEvent: threadReplyEvent }); act(() => { - fireEvent.click(getByLabelText('Reply in thread')); + fireEvent.click(getByLabelText("Reply in thread")); }); expect(dispatcher.dispatch).toHaveBeenCalledWith({ @@ -484,113 +478,115 @@ describe('', () => { }); }); - describe('favourite button', () => { + describe("favourite button", () => { //for multiple event usecase const favButton = (evt: MatrixEvent) => { return getComponent({ mxEvent: evt }).getByTestId(evt.getId()); }; - describe('when favourite_messages feature is enabled', () => { + describe("when favourite_messages feature is enabled", () => { beforeEach(() => { - jest.spyOn(SettingsStore, 'getValue') - .mockImplementation(setting => setting === 'feature_favourite_messages'); + jest.spyOn(SettingsStore, "getValue").mockImplementation( + (setting) => setting === "feature_favourite_messages", + ); localStorageMock.clear(); }); - it('renders favourite button on own actionable event', () => { + it("renders favourite button on own actionable event", () => { const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent }); - expect(queryByLabelText('Favourite')).toBeTruthy(); + expect(queryByLabelText("Favourite")).toBeTruthy(); }); - it('renders favourite button on other actionable events', () => { + it("renders favourite button on other actionable events", () => { const { queryByLabelText } = getComponent({ mxEvent: bobsMessageEvent }); - expect(queryByLabelText('Favourite')).toBeTruthy(); + expect(queryByLabelText("Favourite")).toBeTruthy(); }); - it('does not render Favourite button on non-actionable event', () => { + it("does not render Favourite button on non-actionable event", () => { //redacted event is not actionable const { queryByLabelText } = getComponent({ mxEvent: redactedEvent }); - expect(queryByLabelText('Favourite')).toBeFalsy(); + expect(queryByLabelText("Favourite")).toBeFalsy(); }); - it('remembers favourited state of multiple events, and handles the localStorage of the events accordingly', - () => { - const alicesAction = favButton(alicesMessageEvent); - const bobsAction = favButton(bobsMessageEvent); + it("remembers favourited state of multiple events, and handles the localStorage of the events accordingly", () => { + const alicesAction = favButton(alicesMessageEvent); + const bobsAction = favButton(bobsMessageEvent); - //default state before being clicked - expect(alicesAction.classList).not.toContain('mx_MessageActionBar_favouriteButton_fillstar'); - expect(bobsAction.classList).not.toContain('mx_MessageActionBar_favouriteButton_fillstar'); - expect(localStorageMock.getItem('io_element_favouriteMessages')).toBeNull(); + //default state before being clicked + expect(alicesAction.classList).not.toContain("mx_MessageActionBar_favouriteButton_fillstar"); + expect(bobsAction.classList).not.toContain("mx_MessageActionBar_favouriteButton_fillstar"); + expect(localStorageMock.getItem("io_element_favouriteMessages")).toBeNull(); - //if only alice's event is fired - act(() => { - fireEvent.click(alicesAction); - }); - - expect(alicesAction.classList).toContain('mx_MessageActionBar_favouriteButton_fillstar'); - expect(bobsAction.classList).not.toContain('mx_MessageActionBar_favouriteButton_fillstar'); - expect(localStorageMock.setItem) - .toHaveBeenCalledWith('io_element_favouriteMessages', '["$alices_message"]'); - - //when bob's event is fired,both should be styled and stored in localStorage - act(() => { - fireEvent.click(bobsAction); - }); - - expect(alicesAction.classList).toContain('mx_MessageActionBar_favouriteButton_fillstar'); - expect(bobsAction.classList).toContain('mx_MessageActionBar_favouriteButton_fillstar'); - expect(localStorageMock.setItem) - .toHaveBeenCalledWith('io_element_favouriteMessages', '["$alices_message","$bobs_message"]'); - - //finally, at this point the localStorage should contain the two eventids - expect(localStorageMock.getItem('io_element_favouriteMessages')) - .toEqual('["$alices_message","$bobs_message"]'); - - //if decided to unfavourite bob's event by clicking again - act(() => { - fireEvent.click(bobsAction); - }); - expect(bobsAction.classList).not.toContain('mx_MessageActionBar_favouriteButton_fillstar'); - expect(alicesAction.classList).toContain('mx_MessageActionBar_favouriteButton_fillstar'); - expect(localStorageMock.getItem('io_element_favouriteMessages')).toEqual('["$alices_message"]'); + //if only alice's event is fired + act(() => { + fireEvent.click(alicesAction); }); + + expect(alicesAction.classList).toContain("mx_MessageActionBar_favouriteButton_fillstar"); + expect(bobsAction.classList).not.toContain("mx_MessageActionBar_favouriteButton_fillstar"); + expect(localStorageMock.setItem).toHaveBeenCalledWith( + "io_element_favouriteMessages", + '["$alices_message"]', + ); + + //when bob's event is fired,both should be styled and stored in localStorage + act(() => { + fireEvent.click(bobsAction); + }); + + expect(alicesAction.classList).toContain("mx_MessageActionBar_favouriteButton_fillstar"); + expect(bobsAction.classList).toContain("mx_MessageActionBar_favouriteButton_fillstar"); + expect(localStorageMock.setItem).toHaveBeenCalledWith( + "io_element_favouriteMessages", + '["$alices_message","$bobs_message"]', + ); + + //finally, at this point the localStorage should contain the two eventids + expect(localStorageMock.getItem("io_element_favouriteMessages")).toEqual( + '["$alices_message","$bobs_message"]', + ); + + //if decided to unfavourite bob's event by clicking again + act(() => { + fireEvent.click(bobsAction); + }); + expect(bobsAction.classList).not.toContain("mx_MessageActionBar_favouriteButton_fillstar"); + expect(alicesAction.classList).toContain("mx_MessageActionBar_favouriteButton_fillstar"); + expect(localStorageMock.getItem("io_element_favouriteMessages")).toEqual('["$alices_message"]'); + }); }); - describe('when favourite_messages feature is disabled', () => { - it('does not render', () => { - jest.spyOn(SettingsStore, 'getValue').mockReturnValue(false); + describe("when favourite_messages feature is disabled", () => { + it("does not render", () => { + jest.spyOn(SettingsStore, "getValue").mockReturnValue(false); const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent }); - expect(queryByLabelText('Favourite')).toBeFalsy(); + expect(queryByLabelText("Favourite")).toBeFalsy(); }); }); }); - it.each([ - ["React"], - ["Reply"], - ["Reply in thread"], - ["Favourite"], - ["Edit"], - ])("does not show context menu when right-clicking", (buttonLabel: string) => { - // For favourite button - jest.spyOn(SettingsStore, 'getValue').mockReturnValue(true); + it.each([["React"], ["Reply"], ["Reply in thread"], ["Favourite"], ["Edit"]])( + "does not show context menu when right-clicking", + (buttonLabel: string) => { + // For favourite button + jest.spyOn(SettingsStore, "getValue").mockReturnValue(true); - const event = new MouseEvent("contextmenu", { - bubbles: true, - cancelable: true, - }); - event.stopPropagation = jest.fn(); - event.preventDefault = jest.fn(); + const event = new MouseEvent("contextmenu", { + bubbles: true, + cancelable: true, + }); + event.stopPropagation = jest.fn(); + event.preventDefault = jest.fn(); - const { queryByTestId, queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent }); - act(() => { - fireEvent(queryByLabelText(buttonLabel), event); - }); - expect(event.stopPropagation).toHaveBeenCalled(); - expect(event.preventDefault).toHaveBeenCalled(); - expect(queryByTestId("mx_MessageContextMenu")).toBeFalsy(); - }); + const { queryByTestId, queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent }); + act(() => { + fireEvent(queryByLabelText(buttonLabel), event); + }); + expect(event.stopPropagation).toHaveBeenCalled(); + expect(event.preventDefault).toHaveBeenCalled(); + expect(queryByTestId("mx_MessageContextMenu")).toBeFalsy(); + }, + ); it("does shows context menu when right-clicking options", () => { const { queryByTestId, queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent }); diff --git a/test/components/views/messages/MessageEvent-test.tsx b/test/components/views/messages/MessageEvent-test.tsx index dadddca093..6ec3d490e3 100644 --- a/test/components/views/messages/MessageEvent-test.tsx +++ b/test/components/views/messages/MessageEvent-test.tsx @@ -26,11 +26,11 @@ import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalink jest.mock("../../../../src/components/views/messages/UnknownBody", () => ({ __esModule: true, - default: () => (
), + default: () =>
, })); jest.mock("../../../../src/voice-broadcast/components/VoiceBroadcastBody", () => ({ - VoiceBroadcastBody: () => (
), + VoiceBroadcastBody: () =>
, })); describe("MessageEvent", () => { @@ -39,11 +39,13 @@ describe("MessageEvent", () => { let event: MatrixEvent; const renderMessageEvent = (): RenderResult => { - return render(); + return render( + , + ); }; beforeEach(() => { diff --git a/test/components/views/messages/TextualBody-test.tsx b/test/components/views/messages/TextualBody-test.tsx index 0ca41b8fa7..01e969a031 100644 --- a/test/components/views/messages/TextualBody-test.tsx +++ b/test/components/views/messages/TextualBody-test.tsx @@ -31,7 +31,7 @@ import { MediaEventHelper } from "../../../../src/utils/MediaEventHelper"; describe("", () => { afterEach(() => { - jest.spyOn(MatrixClientPeg, 'get').mockRestore(); + jest.spyOn(MatrixClientPeg, "get").mockRestore(); }); const defaultRoom = mkStubRoom("room_id", "test room", undefined); @@ -58,7 +58,7 @@ describe("", () => { const defaultProps = { mxEvent: defaultEvent, highlights: [], - highlightLink: '', + highlightLink: "", onMessageAllowed: jest.fn(), onHeightChanged: jest.fn(), permalinkCreator: new RoomPermalinkCreator(defaultRoom), @@ -107,7 +107,7 @@ describe("", () => { const wrapper = getComponent({ mxEvent: ev }); expect(wrapper.text()).toBe(ev.getContent().body); const content = wrapper.find(".mx_EventTile_body"); - expect(content.html()).toBe(`${ ev.getContent().body }`); + expect(content.html()).toBe(`${ev.getContent().body}`); }); describe("renders plain-text m.text correctly", () => { @@ -130,7 +130,7 @@ describe("", () => { const wrapper = getComponent({ mxEvent: ev }); expect(wrapper.text()).toBe(ev.getContent().body); const content = wrapper.find(".mx_EventTile_body"); - expect(content.html()).toBe(`${ ev.getContent().body }`); + expect(content.html()).toBe(`${ev.getContent().body}`); }); // If pills were rendered within a Portal/same shadow DOM then it'd be easier to test @@ -149,9 +149,11 @@ describe("", () => { const wrapper = getComponent({ mxEvent: ev }); expect(wrapper.text()).toBe(ev.getContent().body); const content = wrapper.find(".mx_EventTile_body"); - expect(content.html()).toBe('' + - 'Visit ' + - 'https://matrix.org/'); + expect(content.html()).toBe( + '' + + 'Visit ' + + "https://matrix.org/", + ); }); }); @@ -188,8 +190,11 @@ describe("", () => { const wrapper = getComponent({ mxEvent: ev }, matrixClient); expect(wrapper.text()).toBe("foo baz bar del u"); const content = wrapper.find(".mx_EventTile_body"); - expect(content.html()).toBe('' + - ev.getContent().formatted_body + ''); + expect(content.html()).toBe( + '' + + ev.getContent().formatted_body + + "", + ); }); it("spoilers get injected properly into the DOM", () => { @@ -201,7 +206,7 @@ describe("", () => { body: "Hey [Spoiler for movie](mxc://someserver/somefile)", msgtype: "m.text", format: "org.matrix.custom.html", - formatted_body: "Hey the movie was awesome", + formatted_body: 'Hey the movie was awesome', }, event: true, }); @@ -209,12 +214,14 @@ describe("", () => { const wrapper = getComponent({ mxEvent: ev }, matrixClient); expect(wrapper.text()).toBe("Hey (movie) the movie was awesome"); const content = wrapper.find(".mx_EventTile_body"); - expect(content.html()).toBe('' + - 'Hey ' + - '' + - '(movie) ' + - 'the movie was awesome' + - ''); + expect(content.html()).toBe( + '' + + "Hey " + + '' + + '(movie) ' + + 'the movie was awesome' + + "", + ); }); it("linkification is not applied to code blocks", () => { @@ -247,7 +254,7 @@ describe("", () => { body: "Hey User", msgtype: "m.text", format: "org.matrix.custom.html", - formatted_body: "Hey Member", + formatted_body: 'Hey Member', }, event: true, }); @@ -290,8 +297,8 @@ describe("", () => { msgtype: "m.text", format: "org.matrix.custom.html", formatted_body: - "An event link with text", + 'An event link with text', }, event: true, }); @@ -301,9 +308,9 @@ describe("", () => { const content = wrapper.find(".mx_EventTile_body"); expect(content.html()).toBe( '' + - 'An event link with text', + 'An event link with text', ); }); @@ -319,8 +326,8 @@ describe("", () => { msgtype: "m.text", format: "org.matrix.custom.html", formatted_body: - "A room link with vias", + 'A room link with vias', }, event: true, }); @@ -330,17 +337,17 @@ describe("", () => { const content = wrapper.find(".mx_EventTile_body"); expect(content.html()).toBe( '' + - 'A ' + - 'room name with vias', + 'A ' + + 'room name with vias', ); }); - it('renders formatted body without html corretly', () => { + it("renders formatted body without html corretly", () => { const ev = mkEvent({ type: "m.room.message", room: "room_id", @@ -358,15 +365,13 @@ describe("", () => { const content = wrapper.find(".mx_EventTile_body"); expect(content.html()).toBe( - '' + - 'escaped *markdown*' + - '', + '' + "escaped *markdown*" + "", ); }); }); it("renders url previews correctly", () => { - languageHandler.setMissingEntryGenerator(key => key.split('|', 2)[1]); + languageHandler.setMissingEntryGenerator((key) => key.split("|", 2)[1]); const matrixClient = getMockClientWithEventEmitter({ getRoom: () => mkStubRoom("room_id", "room name", undefined), @@ -408,21 +413,24 @@ describe("", () => { }, event: true, }); - jest.spyOn(ev, 'replacingEventDate').mockReturnValue(new Date(1993, 7, 3)); + jest.spyOn(ev, "replacingEventDate").mockReturnValue(new Date(1993, 7, 3)); ev.makeReplaced(ev2); - wrapper.setProps({ - mxEvent: ev, - replacingEventId: ev.getId(), - }, () => { - expect(wrapper.text()).toBe(ev2.getContent()["m.new_content"].body + "(edited)"); + wrapper.setProps( + { + mxEvent: ev, + replacingEventId: ev.getId(), + }, + () => { + expect(wrapper.text()).toBe(ev2.getContent()["m.new_content"].body + "(edited)"); - // XXX: this is to give TextualBody enough time for state to settle - wrapper.setState({}, () => { - widgets = wrapper.find("LinkPreviewGroup"); - // at this point we should have exactly two links (not the matrix.org one anymore) - expect(widgets.at(0).prop("links")).toEqual(["https://vector.im/", "https://riot.im/"]); - }); - }); + // XXX: this is to give TextualBody enough time for state to settle + wrapper.setState({}, () => { + widgets = wrapper.find("LinkPreviewGroup"); + // at this point we should have exactly two links (not the matrix.org one anymore) + expect(widgets.at(0).prop("links")).toEqual(["https://vector.im/", "https://riot.im/"]); + }); + }, + ); }); }); diff --git a/test/components/views/messages/shared/MediaProcessingError-test.tsx b/test/components/views/messages/shared/MediaProcessingError-test.tsx index 114e56f511..4df6712c9a 100644 --- a/test/components/views/messages/shared/MediaProcessingError-test.tsx +++ b/test/components/views/messages/shared/MediaProcessingError-test.tsx @@ -14,20 +14,19 @@ 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 React from "react"; +import { render } from "@testing-library/react"; -import MediaProcessingError from '../../../../../src/components/views/messages/shared/MediaProcessingError'; +import MediaProcessingError from "../../../../../src/components/views/messages/shared/MediaProcessingError"; -describe('', () => { +describe("", () => { const defaultProps = { - className: 'test-classname', - children: 'Something went wrong', + className: "test-classname", + children: "Something went wrong", }; - const getComponent = (props = {}) => - render(); + const getComponent = (props = {}) => render(); - it('renders', () => { + it("renders", () => { const { container } = getComponent(); expect(container).toMatchSnapshot(); }); diff --git a/test/components/views/right_panel/PinnedMessagesCard-test.tsx b/test/components/views/right_panel/PinnedMessagesCard-test.tsx index b5c6902378..e4be6191c3 100644 --- a/test/components/views/right_panel/PinnedMessagesCard-test.tsx +++ b/test/components/views/right_panel/PinnedMessagesCard-test.tsx @@ -32,12 +32,7 @@ import { PollEndEvent, } from "matrix-events-sdk"; -import { - stubClient, - mkStubRoom, - mkEvent, - mkMessage, -} from "../../../test-utils"; +import { stubClient, mkStubRoom, mkEvent, mkMessage } from "../../../test-utils"; import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; import PinnedMessagesCard from "../../../../src/components/views/right_panel/PinnedMessagesCard"; import PinnedEventTile from "../../../../src/components/views/rooms/PinnedEventTile"; @@ -53,31 +48,34 @@ describe("", () => { cli.relations.mockResolvedValue({ originalEvent: {} as unknown as MatrixEvent, events: [] }); const mkRoom = (localPins: MatrixEvent[], nonLocalPins: MatrixEvent[]): Room => { - const room = mkStubRoom("!room:example.org", 'room', cli); + const room = mkStubRoom("!room:example.org", "room", cli); // Deferred since we may be adding or removing pins later const pins = () => [...localPins, ...nonLocalPins]; // Insert pin IDs into room state - mocked(room.currentState).getStateEvents.mockImplementation((): any => mkEvent({ - event: true, - type: EventType.RoomPinnedEvents, - content: { - pinned: pins().map(e => e.getId()), - }, - user: '@user:example.org', - room: '!room:example.org', - })); + mocked(room.currentState).getStateEvents.mockImplementation((): any => + mkEvent({ + event: true, + type: EventType.RoomPinnedEvents, + content: { + pinned: pins().map((e) => e.getId()), + }, + user: "@user:example.org", + room: "!room:example.org", + }), + ); // Insert local pins into local timeline set - room.getUnfilteredTimelineSet = () => ({ - getTimelineForEvent: () => ({ - getEvents: () => localPins, - }), - } as unknown as EventTimelineSet); + room.getUnfilteredTimelineSet = () => + ({ + getTimelineForEvent: () => ({ + getEvents: () => localPins, + }), + } as unknown as EventTimelineSet); // Return all pins over fetchRoomEvent cli.fetchRoomEvent.mockImplementation((roomId, eventId) => { - const event = pins().find(e => e.getId() === eventId)?.event; + const event = pins().find((e) => e.getId() === eventId)?.event; return Promise.resolve(event as IMinimalEvent); }); @@ -87,16 +85,19 @@ describe("", () => { const mountPins = async (room: Room): Promise>> => { let pins; await act(async () => { - pins = mount(, { - wrappingComponent: MatrixClientContext.Provider, - wrappingComponentProps: { value: cli }, - }); + pins = mount( + , + { + wrappingComponent: MatrixClientContext.Provider, + wrappingComponentProps: { value: cli }, + }, + ); // Wait a tick for state updates - await new Promise(resolve => setImmediate(resolve)); + await new Promise((resolve) => setImmediate(resolve)); }); pins.update(); @@ -105,15 +106,16 @@ describe("", () => { const emitPinUpdates = async (pins: ReactWrapper>) => { const room = pins.props().room; - const pinListener = mocked(room.currentState).on.mock.calls - .find(([eventName, listener]) => eventName === RoomStateEvent.Events)[1]; + const pinListener = mocked(room.currentState).on.mock.calls.find( + ([eventName, listener]) => eventName === RoomStateEvent.Events, + )[1]; await act(async () => { // Emit the update // @ts-ignore what is going on here? pinListener(room.currentState.getStateEvents()); // Wait a tick for state updates - await new Promise(resolve => setImmediate(resolve)); + await new Promise((resolve) => setImmediate(resolve)); }); pins.update(); }; @@ -240,12 +242,14 @@ describe("", () => { ["@alice:example.org", 0], ["@bob:example.org", 0], ["@eve:example.org", 1], - ].map(([user, option], i) => mkEvent({ - ...PollResponseEvent.from([answers[option].id], poll.getId()).serialize(), - event: true, - room: "!room:example.org", - user: user as string, - })); + ].map(([user, option], i) => + mkEvent({ + ...PollResponseEvent.from([answers[option].id], poll.getId()).serialize(), + event: true, + room: "!room:example.org", + user: user as string, + }), + ); const end = mkEvent({ ...PollEndEvent.from(poll.getId(), "Closing the poll").serialize(), event: true, @@ -259,9 +263,9 @@ describe("", () => { switch (eventType) { case M_POLL_RESPONSE.name: // Paginate the results, for added challenge - return (from === "page2") ? - { originalEvent: poll, events: responses.slice(2) } : - { originalEvent: poll, events: responses.slice(0, 2), nextBatch: "page2" }; + return from === "page2" + ? { originalEvent: poll, events: responses.slice(2) } + : { originalEvent: poll, events: responses.slice(0, 2), nextBatch: "page2" }; case M_POLL_END.name: return { originalEvent: null, events: [end] }; } diff --git a/test/components/views/right_panel/RoomHeaderButtons-test.tsx b/test/components/views/right_panel/RoomHeaderButtons-test.tsx index 4d8537fdba..76e33f9b2c 100644 --- a/test/components/views/right_panel/RoomHeaderButtons-test.tsx +++ b/test/components/views/right_panel/RoomHeaderButtons-test.tsx @@ -25,7 +25,7 @@ import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; import SettingsStore from "../../../../src/settings/SettingsStore"; import { stubClient } from "../../../test-utils"; -describe("RoomHeaderButtons-test.tsx", function() { +describe("RoomHeaderButtons-test.tsx", function () { const ROOM_ID = "!roomId:example.org"; let room: Room; let client: MatrixClient; @@ -45,10 +45,7 @@ describe("RoomHeaderButtons-test.tsx", function() { }); function getComponent(room?: Room) { - return render(); + return render(); } function getThreadButton(container) { @@ -56,9 +53,7 @@ describe("RoomHeaderButtons-test.tsx", function() { } function isIndicatorOfType(container, type: "red" | "gray") { - return container.querySelector(".mx_RightPanel_threadsButton .mx_Indicator") - .className - .includes(type); + return container.querySelector(".mx_RightPanel_threadsButton .mx_Indicator").className.includes(type); } it("shows the thread button", () => { diff --git a/test/components/views/right_panel/UserInfo-test.tsx b/test/components/views/right_panel/UserInfo-test.tsx index f76661fc68..2998efe5b3 100644 --- a/test/components/views/right_panel/UserInfo-test.tsx +++ b/test/components/views/right_panel/UserInfo-test.tsx @@ -14,24 +14,24 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; // eslint-disable-next-line deprecate/import -import { mount } from 'enzyme'; -import { mocked } from 'jest-mock'; +import { mount } from "enzyme"; +import { mocked } from "jest-mock"; import { act } from "react-dom/test-utils"; -import { Room, User, MatrixClient } from 'matrix-js-sdk/src/matrix'; -import { Phase, VerificationRequest } from 'matrix-js-sdk/src/crypto/verification/request/VerificationRequest'; +import { Room, User, MatrixClient } from "matrix-js-sdk/src/matrix"; +import { Phase, VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; -import UserInfo from '../../../../src/components/views/right_panel/UserInfo'; -import { RightPanelPhases } from '../../../../src/stores/right-panel/RightPanelStorePhases'; -import { MatrixClientPeg } from '../../../../src/MatrixClientPeg'; -import MatrixClientContext from '../../../../src/contexts/MatrixClientContext'; -import VerificationPanel from '../../../../src/components/views/right_panel/VerificationPanel'; -import EncryptionInfo from '../../../../src/components/views/right_panel/EncryptionInfo'; +import UserInfo from "../../../../src/components/views/right_panel/UserInfo"; +import { RightPanelPhases } from "../../../../src/stores/right-panel/RightPanelStorePhases"; +import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; +import MatrixClientContext from "../../../../src/contexts/MatrixClientContext"; +import VerificationPanel from "../../../../src/components/views/right_panel/VerificationPanel"; +import EncryptionInfo from "../../../../src/components/views/right_panel/EncryptionInfo"; const findByTestId = (component, id) => component.find(`[data-test-id="${id}"]`); -jest.mock('../../../../src/utils/DMRoomMap', () => { +jest.mock("../../../../src/utils/DMRoomMap", () => { const mock = { getUserIdForRoomId: jest.fn(), getDMRoomsForUserId: jest.fn(), @@ -43,8 +43,8 @@ jest.mock('../../../../src/utils/DMRoomMap', () => { }; }); -describe('', () => { - const defaultUserId = '@test:test'; +describe("", () => { + const defaultUserId = "@test:test"; const defaultUser = new User(defaultUserId); const mockClient = mocked({ @@ -57,7 +57,7 @@ describe('', () => { isSynapseAdministrator: jest.fn().mockResolvedValue(false), isRoomEncrypted: jest.fn().mockReturnValue(false), doesServerSupportUnstableFeature: jest.fn().mockReturnValue(false), - mxcUrlToHttp: jest.fn().mockReturnValue('mock-mxcUrlToHttp'), + mxcUrlToHttp: jest.fn().mockReturnValue("mock-mxcUrlToHttp"), removeListener: jest.fn(), currentState: { on: jest.fn(), @@ -65,7 +65,9 @@ describe('', () => { } as unknown as MatrixClient); const verificationRequest = { - pending: true, on: jest.fn(), phase: Phase.Ready, + pending: true, + on: jest.fn(), + phase: Phase.Ready, channel: { transactionId: 1 }, otherPartySupportsMethod: jest.fn(), } as unknown as VerificationRequest; @@ -77,51 +79,49 @@ describe('', () => { onClose: jest.fn(), }; - const getComponent = (props = {}) => mount( - , - { + const getComponent = (props = {}) => + mount(, { wrappingComponent: MatrixClientContext.Provider, wrappingComponentProps: { value: mockClient }, - }, - ); + }); beforeAll(() => { - jest.spyOn(MatrixClientPeg, 'get').mockReturnValue(mockClient); + jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient); }); beforeEach(() => { mockClient.getUser.mockClear().mockReturnValue({} as unknown as User); }); - it('closes on close button click', () => { + it("closes on close button click", () => { const onClose = jest.fn(); const component = getComponent({ onClose }); act(() => { - findByTestId(component, 'base-card-close-button').at(0).simulate('click'); + findByTestId(component, "base-card-close-button").at(0).simulate("click"); }); expect(onClose).toHaveBeenCalled(); }); - describe('without a room', () => { - it('does not render space header', () => { + describe("without a room", () => { + it("does not render space header", () => { const component = getComponent(); - expect(findByTestId(component, 'space-header').length).toBeFalsy(); + expect(findByTestId(component, "space-header").length).toBeFalsy(); }); - it('renders user info', () => { + it("renders user info", () => { const component = getComponent(); expect(component.find("BasicUserInfo").length).toBeTruthy(); }); - it('renders encryption info panel without pending verification', () => { + it("renders encryption info panel without pending verification", () => { const phase = RightPanelPhases.EncryptionPanel; const component = getComponent({ phase }); expect(component.find(EncryptionInfo).length).toBeTruthy(); }); - it('renders encryption verification panel with pending verification', () => { + it("renders encryption verification panel with pending verification", () => { const phase = RightPanelPhases.EncryptionPanel; const component = getComponent({ phase, verificationRequest }); @@ -129,22 +129,22 @@ describe('', () => { expect(component.find(VerificationPanel).length).toBeTruthy(); }); - it('renders close button correctly when encryption panel with a pending verification request', () => { + it("renders close button correctly when encryption panel with a pending verification request", () => { const phase = RightPanelPhases.EncryptionPanel; const component = getComponent({ phase, verificationRequest }); - expect(findByTestId(component, 'base-card-close-button').at(0).props().title).toEqual('Cancel'); + expect(findByTestId(component, "base-card-close-button").at(0).props().title).toEqual("Cancel"); }); }); - describe('with a room', () => { + describe("with a room", () => { const room = { - roomId: '!fkfk', + roomId: "!fkfk", getType: jest.fn().mockReturnValue(undefined), isSpaceRoom: jest.fn().mockReturnValue(false), getMember: jest.fn().mockReturnValue(undefined), - getMxcAvatarUrl: jest.fn().mockReturnValue('mock-avatar-url'), - name: 'test room', + getMxcAvatarUrl: jest.fn().mockReturnValue("mock-avatar-url"), + name: "test room", on: jest.fn(), currentState: { getStateEvents: jest.fn(), @@ -152,32 +152,33 @@ describe('', () => { }, } as unknown as Room; - it('renders user info', () => { + it("renders user info", () => { const component = getComponent(); expect(component.find("BasicUserInfo").length).toBeTruthy(); }); - it('does not render space header when room is not a space room', () => { + it("does not render space header when room is not a space room", () => { const component = getComponent({ room }); - expect(findByTestId(component, 'space-header').length).toBeFalsy(); + expect(findByTestId(component, "space-header").length).toBeFalsy(); }); - it('renders space header when room is a space room', () => { + it("renders space header when room is a space room", () => { const spaceRoom = { - ...room, isSpaceRoom: jest.fn().mockReturnValue(true), + ...room, + isSpaceRoom: jest.fn().mockReturnValue(true), }; const component = getComponent({ room: spaceRoom }); - expect(findByTestId(component, 'space-header').length).toBeTruthy(); + expect(findByTestId(component, "space-header").length).toBeTruthy(); }); - it('renders encryption info panel without pending verification', () => { + it("renders encryption info panel without pending verification", () => { const phase = RightPanelPhases.EncryptionPanel; const component = getComponent({ phase, room }); expect(component.find(EncryptionInfo).length).toBeTruthy(); }); - it('renders encryption verification panel with pending verification', () => { + it("renders encryption verification panel with pending verification", () => { const phase = RightPanelPhases.EncryptionPanel; const component = getComponent({ phase, verificationRequest, room }); diff --git a/test/components/views/rooms/BasicMessageComposer-test.tsx b/test/components/views/rooms/BasicMessageComposer-test.tsx index 4f759db93f..4a0da2d19c 100644 --- a/test/components/views/rooms/BasicMessageComposer-test.tsx +++ b/test/components/views/rooms/BasicMessageComposer-test.tsx @@ -14,12 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; // eslint-disable-next-line deprecate/import -import { mount, ReactWrapper } from 'enzyme'; -import { MatrixClient, Room } from 'matrix-js-sdk/src/matrix'; +import { mount, ReactWrapper } from "enzyme"; +import { MatrixClient, Room } from "matrix-js-sdk/src/matrix"; -import BasicMessageComposer from '../../../../src/components/views/rooms/BasicMessageComposer'; +import BasicMessageComposer from "../../../../src/components/views/rooms/BasicMessageComposer"; import * as TestUtils from "../../../test-utils"; import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; import EditorModel from "../../../../src/editor/model"; @@ -40,7 +40,7 @@ describe("BasicMessageComposer", () => { wrapper.find(".mx_BasicMessageComposer_input").simulate("paste", { clipboardData: { - getData: type => { + getData: (type) => { if (type === "text/plain") { return "https://element.io"; } @@ -56,11 +56,9 @@ describe("BasicMessageComposer", () => { function render(model: EditorModel): ReactWrapper { const client: MatrixClient = MatrixClientPeg.get(); - const roomId = '!1234567890:domain'; + const roomId = "!1234567890:domain"; const userId = client.getUserId(); const room = new Room(roomId, client, userId); - return mount(( - - )); + return mount(); } diff --git a/test/components/views/rooms/EventTile-test.tsx b/test/components/views/rooms/EventTile-test.tsx index dd0cda23b4..d0fe524d02 100644 --- a/test/components/views/rooms/EventTile-test.tsx +++ b/test/components/views/rooms/EventTile-test.tsx @@ -41,10 +41,7 @@ describe("EventTile", () => { // Give a way for a test to update the event prop. // changeEvent = setEvent; - return ; + return ; } function getComponent( @@ -58,7 +55,8 @@ describe("EventTile", () => { - , + + , , ); } @@ -75,7 +73,7 @@ describe("EventTile", () => { jest.spyOn(client, "getRoom").mockReturnValue(room); jest.spyOn(client, "decryptEventIfNeeded").mockResolvedValue(); - jest.spyOn(SettingsStore, "getValue").mockImplementation(name => name === "feature_thread"); + jest.spyOn(SettingsStore, "getValue").mockImplementation((name) => name === "feature_thread"); mxEvent = mkMessage({ room: room.roomId, @@ -91,16 +89,22 @@ describe("EventTile", () => { }); it("removes the thread summary when thread is deleted", async () => { - const { rootEvent, events: [, reply] } = mkThread({ + const { + rootEvent, + events: [, reply], + } = mkThread({ room, client, authorId: "@alice:example.org", participantUserIds: ["@alice:example.org"], length: 2, // root + 1 answer }); - getComponent({ - mxEvent: rootEvent, - }, TimelineRenderingType.Room); + getComponent( + { + mxEvent: rootEvent, + }, + TimelineRenderingType.Room, + ); await waitFor(() => expect(screen.queryByTestId("thread-summary")).not.toBeNull()); diff --git a/test/components/views/rooms/MemberList-test.tsx b/test/components/views/rooms/MemberList-test.tsx index 959fb8df99..c5b12166bb 100644 --- a/test/components/views/rooms/MemberList-test.tsx +++ b/test/components/views/rooms/MemberList-test.tsx @@ -15,26 +15,26 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import ReactTestUtils from 'react-dom/test-utils'; -import ReactDOM from 'react-dom'; -import { Room } from 'matrix-js-sdk/src/models/room'; -import { RoomMember } from 'matrix-js-sdk/src/models/room-member'; +import React from "react"; +import ReactTestUtils from "react-dom/test-utils"; +import ReactDOM from "react-dom"; +import { Room } from "matrix-js-sdk/src/models/room"; +import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { User } from "matrix-js-sdk/src/models/user"; import { compare } from "matrix-js-sdk/src/utils"; -import { MatrixClientPeg } from '../../../../src/MatrixClientPeg'; -import * as TestUtils from '../../../test-utils'; +import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; +import * as TestUtils from "../../../test-utils"; import MemberList from "../../../../src/components/views/rooms/MemberList"; -import MemberTile from '../../../../src/components/views/rooms/MemberTile'; -import { SDKContext } from '../../../../src/contexts/SDKContext'; -import { TestSdkContext } from '../../../TestSdkContext'; +import MemberTile from "../../../../src/components/views/rooms/MemberTile"; +import { SDKContext } from "../../../../src/contexts/SDKContext"; +import { TestSdkContext } from "../../../TestSdkContext"; function generateRoomId() { - return '!' + Math.random().toString().slice(2, 10) + ':domain'; + return "!" + Math.random().toString().slice(2, 10) + ":domain"; } -describe('MemberList', () => { +describe("MemberList", () => { function createRoom(opts = {}) { const room = new Room(generateRoomId(), null, client.getUserId()); if (opts) { @@ -53,12 +53,12 @@ describe('MemberList', () => { let moderatorUsers = []; let defaultUsers = []; - beforeEach(function() { + beforeEach(function () { TestUtils.stubClient(); client = MatrixClientPeg.get(); client.hasLazyLoadMembersEnabled = () => false; - parentDiv = document.createElement('div'); + parentDiv = document.createElement("div"); document.body.appendChild(parentDiv); // Make room @@ -76,7 +76,7 @@ describe('MemberList', () => { adminUser.powerLevel = 100; adminUser.user = new User(adminUser.userId); adminUser.user.currentlyActive = true; - adminUser.user.presence = 'online'; + adminUser.user.presence = "online"; adminUser.user.lastPresenceTs = 1000; adminUser.user.lastActiveAgo = 10; adminUsers.push(adminUser); @@ -86,7 +86,7 @@ describe('MemberList', () => { moderatorUser.powerLevel = 50; moderatorUser.user = new User(moderatorUser.userId); moderatorUser.user.currentlyActive = true; - moderatorUser.user.presence = 'online'; + moderatorUser.user.presence = "online"; moderatorUser.user.lastPresenceTs = 1000; moderatorUser.user.lastActiveAgo = 10; moderatorUsers.push(moderatorUser); @@ -96,7 +96,7 @@ describe('MemberList', () => { defaultUser.powerLevel = 0; defaultUser.user = new User(defaultUser.userId); defaultUser.user.currentlyActive = true; - defaultUser.user.presence = 'online'; + defaultUser.user.presence = "online"; defaultUser.user.lastPresenceTs = 1000; defaultUser.user.lastActiveAgo = 10; defaultUsers.push(defaultUser); @@ -109,7 +109,7 @@ describe('MemberList', () => { memberListRoom.currentState = { members: {}, getMember: jest.fn(), - getStateEvents: (eventType, stateKey) => stateKey === undefined ? [] : null, // ignore 3pid invites + getStateEvents: (eventType, stateKey) => (stateKey === undefined ? [] : null), // ignore 3pid invites }; for (const member of [...adminUsers, ...moderatorUsers, ...defaultUsers]) { memberListRoom.currentState.members[member.userId] = member; @@ -121,17 +121,15 @@ describe('MemberList', () => { const context = new TestSdkContext(); context.client = client; root = ReactDOM.render( - ( - - - - ), + + + , parentDiv, ); }); @@ -166,15 +164,15 @@ describe('MemberList', () => { let groupChange = false; if (isPresenceEnabled) { - const convertPresence = (p) => p === 'unavailable' ? 'online' : p; - const presenceIndex = p => { - const order = ['active', 'online', 'offline']; + const convertPresence = (p) => (p === "unavailable" ? "online" : p); + const presenceIndex = (p) => { + const order = ["active", "online", "offline"]; const idx = order.indexOf(convertPresence(p)); return idx === -1 ? order.length : idx; // unknown states at the end }; - const idxA = presenceIndex(userA.currentlyActive ? 'active' : userA.presence); - const idxB = presenceIndex(userB.currentlyActive ? 'active' : userB.presence); + const idxA = presenceIndex(userA.currentlyActive ? "active" : userA.presence); + const idxB = presenceIndex(userB.currentlyActive ? "active" : userB.presence); console.log("Comparing presence groups..."); expect(idxB).toBeGreaterThanOrEqual(idxA); groupChange = idxA !== idxB; @@ -203,8 +201,8 @@ describe('MemberList', () => { } if (!groupChange) { - const nameA = memberA.name[0] === '@' ? memberA.name.slice(1) : memberA.name; - const nameB = memberB.name[0] === '@' ? memberB.name.slice(1) : memberB.name; + const nameA = memberA.name[0] === "@" ? memberA.name.slice(1) : memberA.name; + const nameB = memberB.name[0] === "@" ? memberB.name.slice(1) : memberB.name; const nameCompare = compare(nameB, nameA); console.log("Comparing name"); expect(nameCompare).toBeGreaterThanOrEqual(0); @@ -215,7 +213,7 @@ describe('MemberList', () => { } function itDoesOrderMembersCorrectly(enablePresence) { - describe('does order members correctly', () => { + describe("does order members correctly", () => { // Note: even if presence is disabled, we still expect that the presence // tests will pass. All expectOrderedByPresenceAndPowerLevel does is ensure // the order is perceived correctly, regardless of what we did to the members. @@ -223,22 +221,22 @@ describe('MemberList', () => { // Each of the 4 tests here is done to prove that the member list can meet // all 4 criteria independently. Together, they should work. - it('by presence state', () => { + it("by presence state", () => { // Intentionally pick users that will confuse the power level sorting const activeUsers = [defaultUsers[0]]; const onlineUsers = [adminUsers[0]]; const offlineUsers = [...moderatorUsers, ...adminUsers.slice(1), ...defaultUsers.slice(1)]; activeUsers.forEach((u) => { u.user.currentlyActive = true; - u.user.presence = 'online'; + u.user.presence = "online"; }); onlineUsers.forEach((u) => { u.user.currentlyActive = false; - u.user.presence = 'online'; + u.user.presence = "online"; }); offlineUsers.forEach((u) => { u.user.currentlyActive = false; - u.user.presence = 'offline'; + u.user.presence = "offline"; }); // Bypass all the event listeners and skip to the good part @@ -249,7 +247,7 @@ describe('MemberList', () => { expectOrderedByPresenceAndPowerLevel(tiles, enablePresence); }); - it('by power level', () => { + it("by power level", () => { // We already have admin, moderator, and default users so leave them alone // Bypass all the event listeners and skip to the good part @@ -260,7 +258,7 @@ describe('MemberList', () => { expectOrderedByPresenceAndPowerLevel(tiles, enablePresence); }); - it('by last active timestamp', () => { + it("by last active timestamp", () => { // Intentionally pick users that will confuse the power level sorting // lastActiveAgoTs == lastPresenceTs - lastActiveAgo const activeUsers = [defaultUsers[0]]; @@ -290,7 +288,7 @@ describe('MemberList', () => { expectOrderedByPresenceAndPowerLevel(tiles, enablePresence); }); - it('by name', () => { + it("by name", () => { // Intentionally put everyone on the same level to force a name comparison const allUsers = [...adminUsers, ...moderatorUsers, ...defaultUsers]; allUsers.forEach((u) => { @@ -311,12 +309,11 @@ describe('MemberList', () => { }); } - describe('when presence is enabled', () => { + describe("when presence is enabled", () => { itDoesOrderMembersCorrectly(true); }); - describe('when presence is not enabled', () => { + describe("when presence is not enabled", () => { itDoesOrderMembersCorrectly(false); }); }); - diff --git a/test/components/views/rooms/MessageComposer-test.tsx b/test/components/views/rooms/MessageComposer-test.tsx index 6cf71c653e..a20695320b 100644 --- a/test/components/views/rooms/MessageComposer-test.tsx +++ b/test/components/views/rooms/MessageComposer-test.tsx @@ -21,8 +21,9 @@ import { MatrixEvent, MsgType, RoomMember } from "matrix-js-sdk/src/matrix"; import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread"; import { createTestClient, mkEvent, mkStubRoom, stubClient } from "../../../test-utils"; -import MessageComposer, { MessageComposer as MessageComposerClass } - from "../../../../src/components/views/rooms/MessageComposer"; +import MessageComposer, { + MessageComposer as MessageComposerClass, +} from "../../../../src/components/views/rooms/MessageComposer"; import MatrixClientContext from "../../../../src/contexts/MatrixClientContext"; import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; import RoomContext from "../../../../src/contexts/RoomContext"; @@ -65,15 +66,20 @@ describe("MessageComposer", () => { }); it("Does not render a SendMessageComposer or MessageComposerButtons when room is tombstoned", () => { - const wrapper = wrapAndRender({ room }, true, false, mkEvent({ - event: true, - type: "m.room.tombstone", - room: room.roomId, - user: "@user1:server", - skey: "", - content: {}, - ts: Date.now(), - })); + const wrapper = wrapAndRender( + { room }, + true, + false, + mkEvent({ + event: true, + type: "m.room.tombstone", + room: room.roomId, + user: "@user1:server", + skey: "", + content: {}, + ts: Date.now(), + }), + ); expect(wrapper.find("SendMessageComposer")).toHaveLength(0); expect(wrapper.find("MessageComposerButtons")).toHaveLength(0); @@ -150,11 +156,14 @@ describe("MessageComposer", () => { beforeEach(async () => { // simulate settings update await SettingsStore.setValue(setting, null, SettingLevel.DEVICE, !value); - dis.dispatch({ - action: Action.SettingUpdated, - settingName: setting, - newValue: !value, - }, true); + dis.dispatch( + { + action: Action.SettingUpdated, + settingName: setting, + newValue: !value, + }, + true, + ); wrapper.update(); }); @@ -339,7 +348,7 @@ describe("MessageComposer", () => { }); }); - it('should render SendWysiwygComposer', () => { + it("should render SendWysiwygComposer", () => { const room = mkStubRoom("!roomId:server", "Room 1", cli); SettingsStore.setValue("feature_wysiwyg_composer", null, SettingLevel.DEVICE, true); @@ -362,7 +371,7 @@ function wrapAndRender( currentState: undefined, roomId, client: mockClient, - getMember: function(userId: string): RoomMember { + getMember: function (userId: string): RoomMember { return new RoomMember(roomId, userId); }, }; diff --git a/test/components/views/rooms/MessageComposerButtons-test.tsx b/test/components/views/rooms/MessageComposerButtons-test.tsx index f41901dd7a..e841cef600 100644 --- a/test/components/views/rooms/MessageComposerButtons-test.tsx +++ b/test/components/views/rooms/MessageComposerButtons-test.tsx @@ -53,11 +53,7 @@ describe("MessageComposerButtons", () => { false, ); - expect(buttonLabels(buttons)).toEqual([ - "Emoji", - "Attachment", - "More options", - ]); + expect(buttonLabels(buttons)).toEqual(["Emoji", "Attachment", "More options"]); }); it("Renders other buttons in menu in wide mode", () => { @@ -76,12 +72,7 @@ describe("MessageComposerButtons", () => { "Emoji", "Attachment", "More options", - [ - "Sticker", - "Voice Message", - "Poll", - "Location", - ], + ["Sticker", "Voice Message", "Poll", "Location"], ]); }); @@ -97,10 +88,7 @@ describe("MessageComposerButtons", () => { true, ); - expect(buttonLabels(buttons)).toEqual([ - "Emoji", - "More options", - ]); + expect(buttonLabels(buttons)).toEqual(["Emoji", "More options"]); }); it("Renders other buttons in menu (except voice messages) in narrow mode", () => { @@ -115,20 +103,11 @@ describe("MessageComposerButtons", () => { true, ); - expect(buttonLabels(buttons)).toEqual([ - "Emoji", - "More options", - [ - "Attachment", - "Sticker", - "Poll", - "Location", - ], - ]); + expect(buttonLabels(buttons)).toEqual(["Emoji", "More options", ["Attachment", "Sticker", "Poll", "Location"]]); }); - describe('polls button', () => { - it('should render when asked to', () => { + describe("polls button", () => { + it("should render when asked to", () => { const buttons = wrapAndRender( { expect(buttonLabels(buttons)).toEqual([ "Emoji", "More options", - [ - "Attachment", - "Sticker", - "Poll", - "Location", - ], + ["Attachment", "Sticker", "Poll", "Location"], ]); }); - it('should not render when asked not to', () => { + it("should not render when asked not to", () => { const buttons = wrapAndRender( { "Emoji", "Attachment", "More options", - [ - "Sticker", - "Voice Message", - "Voice broadcast", - "Poll", - "Location", - ], + ["Sticker", "Voice Message", "Voice broadcast", "Poll", "Location"], ]); }); }); @@ -209,13 +177,13 @@ describe("MessageComposerButtons", () => { function wrapAndRender(component: React.ReactElement, narrow: boolean): ReactWrapper { const mockClient = createTestClient(); - jest.spyOn(MatrixClientPeg, 'get').mockReturnValue(mockClient); + jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient); const roomId = "myroomid"; const mockRoom: any = { currentState: undefined, roomId, client: mockClient, - getMember: function(userId: string): RoomMember { + getMember: function (userId: string): RoomMember { return new RoomMember(roomId, userId); }, }; @@ -223,9 +191,7 @@ function wrapAndRender(component: React.ReactElement, narrow: boolean): ReactWra return mount( - - { component } - + {component} , ); } @@ -274,23 +240,17 @@ function createRoomState(room: Room, narrow: boolean): IRoomState { function buttonLabels(buttons: ReactWrapper): any[] { // Note: Depends on the fact that the mini buttons use aria-label // and the labels under More options use textContent - const mainButtons = ( - buttons - .find('div.mx_MessageComposer_button[aria-label]') - .map((button: ReactWrapper) => button.prop("aria-label") as string) - .filter(x => x) - ); + const mainButtons = buttons + .find("div.mx_MessageComposer_button[aria-label]") + .map((button: ReactWrapper) => button.prop("aria-label") as string) + .filter((x) => x); - const extraButtons = ( - buttons - .find('.mx_MessageComposer_Menu div.mx_AccessibleButton[role="menuitem"]') - .map((button: ReactWrapper) => button.text()) - .filter(x => x) - ); + const extraButtons = buttons + .find('.mx_MessageComposer_Menu div.mx_AccessibleButton[role="menuitem"]') + .map((button: ReactWrapper) => button.text()) + .filter((x) => x); - const list: any[] = [ - ...mainButtons, - ]; + const list: any[] = [...mainButtons]; if (extraButtons.length > 0) { list.push(extraButtons); diff --git a/test/components/views/rooms/NewRoomIntro-test.tsx b/test/components/views/rooms/NewRoomIntro-test.tsx index fc3b9c2b1f..ef15d6eb99 100644 --- a/test/components/views/rooms/NewRoomIntro-test.tsx +++ b/test/components/views/rooms/NewRoomIntro-test.tsx @@ -29,7 +29,7 @@ import DMRoomMap from "../../../../src/utils/DMRoomMap"; import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; import { DirectoryMember } from "../../../../src/utils/direct-messages"; -const renderNewRoomIntro = (client: MatrixClient, room: Room|LocalRoom) => { +const renderNewRoomIntro = (client: MatrixClient, room: Room | LocalRoom) => { render( diff --git a/test/components/views/rooms/NotificationBadge/NotificationBadge-test.tsx b/test/components/views/rooms/NotificationBadge/NotificationBadge-test.tsx index e0c503d6c5..b322fcc892 100644 --- a/test/components/views/rooms/NotificationBadge/NotificationBadge-test.tsx +++ b/test/components/views/rooms/NotificationBadge/NotificationBadge-test.tsx @@ -17,9 +17,7 @@ limitations under the License. import { fireEvent, render } from "@testing-library/react"; import React from "react"; -import { - StatelessNotificationBadge, -} from "../../../../../src/components/views/rooms/NotificationBadge/StatelessNotificationBadge"; +import { StatelessNotificationBadge } from "../../../../../src/components/views/rooms/NotificationBadge/StatelessNotificationBadge"; import SettingsStore from "../../../../../src/settings/SettingsStore"; import { NotificationColor } from "../../../../../src/stores/notifications/NotificationColor"; @@ -28,14 +26,16 @@ describe("NotificationBadge", () => { it("lets you click it", () => { const cb = jest.fn(); - const { container } = render(); + const { container } = render( + , + ); fireEvent.click(container.firstChild); expect(cb).toHaveBeenCalledTimes(1); @@ -52,11 +52,9 @@ describe("NotificationBadge", () => { return name === "feature_hidebold"; }); - const { container } = render(); + const { container } = render( + , + ); expect(container.firstChild).toBeNull(); }); diff --git a/test/components/views/rooms/NotificationBadge/StatelessNotificationBadge-test.tsx b/test/components/views/rooms/NotificationBadge/StatelessNotificationBadge-test.tsx index c3afd105a5..5eecff326f 100644 --- a/test/components/views/rooms/NotificationBadge/StatelessNotificationBadge-test.tsx +++ b/test/components/views/rooms/NotificationBadge/StatelessNotificationBadge-test.tsx @@ -17,22 +17,14 @@ limitations under the License. import React from "react"; import { render } from "@testing-library/react"; -import { - StatelessNotificationBadge, -} from "../../../../../src/components/views/rooms/NotificationBadge/StatelessNotificationBadge"; +import { StatelessNotificationBadge } from "../../../../../src/components/views/rooms/NotificationBadge/StatelessNotificationBadge"; import { NotificationColor } from "../../../../../src/stores/notifications/NotificationColor"; describe("StatelessNotificationBadge", () => { it("is highlighted when unsent", () => { const { container } = render( - , + , ); - expect( - container.querySelector(".mx_NotificationBadge_highlighted"), - ).not.toBe(null); + expect(container.querySelector(".mx_NotificationBadge_highlighted")).not.toBe(null); }); }); diff --git a/test/components/views/rooms/NotificationBadge/UnreadNotificationBadge-test.tsx b/test/components/views/rooms/NotificationBadge/UnreadNotificationBadge-test.tsx index 20289dc6b9..acffe31ff3 100644 --- a/test/components/views/rooms/NotificationBadge/UnreadNotificationBadge-test.tsx +++ b/test/components/views/rooms/NotificationBadge/UnreadNotificationBadge-test.tsx @@ -22,16 +22,14 @@ import { NotificationCountType, Room } from "matrix-js-sdk/src/models/room"; import { mocked } from "jest-mock"; import { EventStatus } from "matrix-js-sdk/src/models/event-status"; -import { - UnreadNotificationBadge, -} from "../../../../../src/components/views/rooms/NotificationBadge/UnreadNotificationBadge"; +import { UnreadNotificationBadge } from "../../../../../src/components/views/rooms/NotificationBadge/UnreadNotificationBadge"; import { mkMessage, stubClient } from "../../../../test-utils/test-utils"; import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg"; import * as RoomNotifs from "../../../../../src/RoomNotifs"; jest.mock("../../../../../src/RoomNotifs"); -jest.mock('../../../../../src/RoomNotifs', () => ({ - ...(jest.requireActual('../../../../../src/RoomNotifs') as Object), +jest.mock("../../../../../src/RoomNotifs", () => ({ + ...(jest.requireActual("../../../../../src/RoomNotifs") as Object), getRoomNotifsState: jest.fn(), })); @@ -122,9 +120,7 @@ describe("UnreadNotificationBadge", () => { }); it("hides counter for muted rooms", () => { - jest.spyOn(RoomNotifs, "getRoomNotifsState") - .mockReset() - .mockReturnValue(RoomNotifs.RoomNotifState.Mute); + jest.spyOn(RoomNotifs, "getRoomNotifsState").mockReset().mockReturnValue(RoomNotifs.RoomNotifState.Mute); const { container } = render(getComponent()); expect(container.querySelector(".mx_NotificationBadge")).toBeNull(); diff --git a/test/components/views/rooms/ReadReceiptGroup-test.tsx b/test/components/views/rooms/ReadReceiptGroup-test.tsx index 3d1bafedbc..b0110a2cc2 100644 --- a/test/components/views/rooms/ReadReceiptGroup-test.tsx +++ b/test/components/views/rooms/ReadReceiptGroup-test.tsx @@ -19,32 +19,28 @@ import { determineAvatarPosition, readReceiptTooltip } from "../../../../src/com describe("ReadReceiptGroup", () => { describe("TooltipText", () => { it("returns '...and more' with hasMore", () => { - expect(readReceiptTooltip(["Alice", "Bob", "Charlie", "Dan", "Eve"], true)) - .toEqual("Alice, Bob, Charlie, Dan, Eve and more"); - expect(readReceiptTooltip(["Alice", "Bob", "Charlie", "Dan"], true)) - .toEqual("Alice, Bob, Charlie, Dan and more"); - expect(readReceiptTooltip(["Alice", "Bob", "Charlie"], true)) - .toEqual("Alice, Bob, Charlie and more"); - expect(readReceiptTooltip(["Alice", "Bob"], true)) - .toEqual("Alice, Bob and more"); - expect(readReceiptTooltip(["Alice"], true)) - .toEqual("Alice and more"); - expect(readReceiptTooltip([], false)) - .toEqual(null); + expect(readReceiptTooltip(["Alice", "Bob", "Charlie", "Dan", "Eve"], true)).toEqual( + "Alice, Bob, Charlie, Dan, Eve and more", + ); + expect(readReceiptTooltip(["Alice", "Bob", "Charlie", "Dan"], true)).toEqual( + "Alice, Bob, Charlie, Dan and more", + ); + expect(readReceiptTooltip(["Alice", "Bob", "Charlie"], true)).toEqual("Alice, Bob, Charlie and more"); + expect(readReceiptTooltip(["Alice", "Bob"], true)).toEqual("Alice, Bob and more"); + expect(readReceiptTooltip(["Alice"], true)).toEqual("Alice and more"); + expect(readReceiptTooltip([], false)).toEqual(null); }); it("returns a pretty list without hasMore", () => { - expect(readReceiptTooltip(["Alice", "Bob", "Charlie", "Dan", "Eve"], false)) - .toEqual("Alice, Bob, Charlie, Dan and Eve"); - expect(readReceiptTooltip(["Alice", "Bob", "Charlie", "Dan"], false)) - .toEqual("Alice, Bob, Charlie and Dan"); - expect(readReceiptTooltip(["Alice", "Bob", "Charlie"], false)) - .toEqual("Alice, Bob and Charlie"); - expect(readReceiptTooltip(["Alice", "Bob"], false)) - .toEqual("Alice and Bob"); - expect(readReceiptTooltip(["Alice"], false)) - .toEqual("Alice"); - expect(readReceiptTooltip([], false)) - .toEqual(null); + expect(readReceiptTooltip(["Alice", "Bob", "Charlie", "Dan", "Eve"], false)).toEqual( + "Alice, Bob, Charlie, Dan and Eve", + ); + expect(readReceiptTooltip(["Alice", "Bob", "Charlie", "Dan"], false)).toEqual( + "Alice, Bob, Charlie and Dan", + ); + expect(readReceiptTooltip(["Alice", "Bob", "Charlie"], false)).toEqual("Alice, Bob and Charlie"); + expect(readReceiptTooltip(["Alice", "Bob"], false)).toEqual("Alice and Bob"); + expect(readReceiptTooltip(["Alice"], false)).toEqual("Alice"); + expect(readReceiptTooltip([], false)).toEqual(null); }); }); describe("AvatarPosition", () => { @@ -53,44 +49,28 @@ describe("ReadReceiptGroup", () => { // We want to fill slots so the first avatar is in the right-most slot without leaving any slots at the left // unoccupied. it("to handle the non-overflowing case correctly", () => { - expect(determineAvatarPosition(0, 4)) - .toEqual({ hidden: false, position: 0 }); + expect(determineAvatarPosition(0, 4)).toEqual({ hidden: false, position: 0 }); - expect(determineAvatarPosition(0, 4)) - .toEqual({ hidden: false, position: 0 }); - expect(determineAvatarPosition(1, 4)) - .toEqual({ hidden: false, position: 1 }); + expect(determineAvatarPosition(0, 4)).toEqual({ hidden: false, position: 0 }); + expect(determineAvatarPosition(1, 4)).toEqual({ hidden: false, position: 1 }); - expect(determineAvatarPosition(0, 4)) - .toEqual({ hidden: false, position: 0 }); - expect(determineAvatarPosition(1, 4)) - .toEqual({ hidden: false, position: 1 }); - expect(determineAvatarPosition(2, 4)) - .toEqual({ hidden: false, position: 2 }); + expect(determineAvatarPosition(0, 4)).toEqual({ hidden: false, position: 0 }); + expect(determineAvatarPosition(1, 4)).toEqual({ hidden: false, position: 1 }); + expect(determineAvatarPosition(2, 4)).toEqual({ hidden: false, position: 2 }); - expect(determineAvatarPosition(0, 4)) - .toEqual({ hidden: false, position: 0 }); - expect(determineAvatarPosition(1, 4)) - .toEqual({ hidden: false, position: 1 }); - expect(determineAvatarPosition(2, 4)) - .toEqual({ hidden: false, position: 2 }); - expect(determineAvatarPosition(3, 4)) - .toEqual({ hidden: false, position: 3 }); + expect(determineAvatarPosition(0, 4)).toEqual({ hidden: false, position: 0 }); + expect(determineAvatarPosition(1, 4)).toEqual({ hidden: false, position: 1 }); + expect(determineAvatarPosition(2, 4)).toEqual({ hidden: false, position: 2 }); + expect(determineAvatarPosition(3, 4)).toEqual({ hidden: false, position: 3 }); }); it("to handle the overflowing case correctly", () => { - expect(determineAvatarPosition(0, 4)) - .toEqual({ hidden: false, position: 0 }); - expect(determineAvatarPosition(1, 4)) - .toEqual({ hidden: false, position: 1 }); - expect(determineAvatarPosition(2, 4)) - .toEqual({ hidden: false, position: 2 }); - expect(determineAvatarPosition(3, 4)) - .toEqual({ hidden: false, position: 3 }); - expect(determineAvatarPosition(4, 4)) - .toEqual({ hidden: true, position: 0 }); - expect(determineAvatarPosition(5, 4)) - .toEqual({ hidden: true, position: 0 }); + expect(determineAvatarPosition(0, 4)).toEqual({ hidden: false, position: 0 }); + expect(determineAvatarPosition(1, 4)).toEqual({ hidden: false, position: 1 }); + expect(determineAvatarPosition(2, 4)).toEqual({ hidden: false, position: 2 }); + expect(determineAvatarPosition(3, 4)).toEqual({ hidden: false, position: 3 }); + expect(determineAvatarPosition(4, 4)).toEqual({ hidden: true, position: 0 }); + expect(determineAvatarPosition(5, 4)).toEqual({ hidden: true, position: 0 }); }); }); }); diff --git a/test/components/views/rooms/RoomHeader-test.tsx b/test/components/views/rooms/RoomHeader-test.tsx index 887a58fba3..5857e28295 100644 --- a/test/components/views/rooms/RoomHeader-test.tsx +++ b/test/components/views/rooms/RoomHeader-test.tsx @@ -14,9 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; // eslint-disable-next-line deprecate/import -import { mount, ReactWrapper } from 'enzyme'; +import { mount, ReactWrapper } from "enzyme"; import { render, screen, act, fireEvent, waitFor, getByRole } from "@testing-library/react"; import { mocked, Mocked } from "jest-mock"; import { EventType, RoomType } from "matrix-js-sdk/src/@types/event"; @@ -26,7 +26,7 @@ import { PendingEventOrdering } from "matrix-js-sdk/src/client"; import { CallType } from "matrix-js-sdk/src/webrtc/call"; import { ClientWidgetApi, Widget } from "matrix-widget-api"; import EventEmitter from "events"; -import { ISearchResults } from 'matrix-js-sdk/src/@types/search'; +import { ISearchResults } from "matrix-js-sdk/src/@types/search"; import type { MatrixClient } from "matrix-js-sdk/src/client"; import type { MatrixEvent } from "matrix-js-sdk/src/models/event"; @@ -39,14 +39,14 @@ import { resetAsyncStoreWithClient, mockPlatformPeg, } from "../../../test-utils"; -import { MatrixClientPeg } from '../../../../src/MatrixClientPeg'; -import DMRoomMap from '../../../../src/utils/DMRoomMap'; +import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; +import DMRoomMap from "../../../../src/utils/DMRoomMap"; import RoomHeader, { IProps as RoomHeaderProps } from "../../../../src/components/views/rooms/RoomHeader"; -import { SearchScope } from '../../../../src/components/views/rooms/SearchBar'; -import { E2EStatus } from '../../../../src/utils/ShieldUtils'; -import { mkEvent } from '../../../test-utils'; +import { SearchScope } from "../../../../src/components/views/rooms/SearchBar"; +import { E2EStatus } from "../../../../src/utils/ShieldUtils"; +import { mkEvent } from "../../../test-utils"; import { IRoomState } from "../../../../src/components/structures/RoomView"; -import RoomContext from '../../../../src/contexts/RoomContext'; +import RoomContext from "../../../../src/contexts/RoomContext"; import SdkConfig from "../../../../src/SdkConfig"; import SettingsStore from "../../../../src/settings/SettingsStore"; import { ElementCall, JitsiCall } from "../../../../src/models/Call"; @@ -60,8 +60,8 @@ import WidgetUtils from "../../../../src/utils/WidgetUtils"; import { ElementWidgetActions } from "../../../../src/stores/widgets/ElementWidgetActions"; import MediaDeviceHandler, { MediaDeviceKindEnum } from "../../../../src/MediaDeviceHandler"; -describe('RoomHeader (Enzyme)', () => { - it('shows the room avatar in a room with only ourselves', () => { +describe("RoomHeader (Enzyme)", () => { + it("shows the room avatar in a room with only ourselves", () => { // When we render a non-DM room with 1 person in it const room = createRoom({ name: "X Room", isDm: false, userIds: [] }); const rendered = mountHeader(room); @@ -75,10 +75,9 @@ describe('RoomHeader (Enzyme)', () => { expect(image.prop("src")).toEqual("data:image/png;base64,00"); }); - it('shows the room avatar in a room with 2 people', () => { + it("shows the room avatar in a room with 2 people", () => { // When we render a non-DM room with 2 people in it - const room = createRoom( - { name: "Y Room", isDm: false, userIds: ["other"] }); + const room = createRoom({ name: "Y Room", isDm: false, userIds: ["other"] }); const rendered = mountHeader(room); // Then the room's avatar is the initial of its name @@ -90,7 +89,7 @@ describe('RoomHeader (Enzyme)', () => { expect(image.prop("src")).toEqual("data:image/png;base64,00"); }); - it('shows the room avatar in a room with >2 people', () => { + it("shows the room avatar in a room with >2 people", () => { // When we render a non-DM room with 3 people in it const room = createRoom({ name: "Z Room", isDm: false, userIds: ["other1", "other2"] }); const rendered = mountHeader(room); @@ -104,7 +103,7 @@ describe('RoomHeader (Enzyme)', () => { expect(image.prop("src")).toEqual("data:image/png;base64,00"); }); - it('shows the room avatar in a DM with only ourselves', () => { + it("shows the room avatar in a DM with only ourselves", () => { // When we render a non-DM room with 1 person in it const room = createRoom({ name: "Z Room", isDm: true, userIds: [] }); const rendered = mountHeader(room); @@ -118,7 +117,7 @@ describe('RoomHeader (Enzyme)', () => { expect(image.prop("src")).toEqual("data:image/png;base64,00"); }); - it('shows the user avatar in a DM with 2 people', () => { + it("shows the user avatar in a DM with 2 people", () => { // Note: this is the interesting case - this is the ONLY // time we should use the user's avatar. @@ -134,10 +133,12 @@ describe('RoomHeader (Enzyme)', () => { expect(rendered.find(".mx_BaseAvatar_initial")).toHaveLength(0); }); - it('shows the room avatar in a DM with >2 people', () => { + it("shows the room avatar in a DM with >2 people", () => { // When we render a DM room with 3 people in it const room = createRoom({ - name: "Z Room", isDm: true, userIds: ["other1", "other2"], + name: "Z Room", + isDm: true, + userIds: ["other1", "other2"], }); const rendered = mountHeader(room); @@ -160,17 +161,21 @@ describe('RoomHeader (Enzyme)', () => { it("hides call buttons when the room is tombstoned", () => { const room = createRoom({ name: "Room", isDm: false, userIds: [] }); - const wrapper = mountHeader(room, {}, { - tombstone: mkEvent({ - event: true, - type: "m.room.tombstone", - room: room.roomId, - user: "@user1:server", - skey: "", - content: {}, - ts: Date.now(), - }), - }); + const wrapper = mountHeader( + room, + {}, + { + tombstone: mkEvent({ + event: true, + type: "m.room.tombstone", + room: room.roomId, + user: "@user1:server", + skey: "", + content: {}, + ts: Date.now(), + }), + }, + ); expect(wrapper.find('[aria-label="Voice call"]').hostNodes()).toHaveLength(0); expect(wrapper.find('[aria-label="Video call"]').hostNodes()).toHaveLength(0); @@ -211,7 +216,7 @@ function createRoom(info: IRoomCreationInfo) { stubClient(); const client: MatrixClient = MatrixClientPeg.get(); - const roomId = '!1234567890:domain'; + const roomId = "!1234567890:domain"; const userId = client.getUserId(); if (info.isDm) { client.getAccountData = (eventType) => { @@ -246,11 +251,11 @@ function mountHeader(room: Room, propsOverride = {}, roomContext?: Partial { }, + onSearchClick: () => {}, onInviteClick: null, - onForgetClick: () => { }, - onCallPlaced: (_type) => { }, - onAppsClick: () => { }, + onForgetClick: () => {}, + onCallPlaced: (_type) => {}, + onAppsClick: () => {}, e2eStatus: E2EStatus.Normal, appsShown: true, searchInfo: { @@ -265,11 +270,11 @@ function mountHeader(room: Room, propsOverride = {}, roomContext?: Partial - - )); + , + ); } function mkCreationEvent(roomId: string, userId: string): MatrixEvent { @@ -289,9 +294,7 @@ function mkCreationEvent(roomId: string, userId: string): MatrixEvent { }); } -function mkNameEvent( - roomId: string, userId: string, name: string, -): MatrixEvent { +function mkNameEvent(roomId: string, userId: string, name: string): MatrixEvent { return mkEvent({ event: true, type: "m.room.name", @@ -308,17 +311,15 @@ function mkJoinEvent(roomId: string, userId: string) { room: roomId, user: userId, content: { - "membership": "join", - "avatar_url": "mxc://example.org/" + userId, + membership: "join", + avatar_url: "mxc://example.org/" + userId, }, }); ret.event.state_key = userId; return ret; } -function mkDirectEvent( - roomId: string, userId: string, otherUsers: string[], -): MatrixEvent { +function mkDirectEvent(roomId: string, userId: string, otherUsers: string[]): MatrixEvent { const content = {}; for (const otherUserId of otherUsers) { content[otherUserId] = [roomId]; @@ -363,7 +364,7 @@ describe("RoomHeader (React Testing Library)", () => { }); room.currentState.setStateEvents([mkCreationEvent(room.roomId, "@alice:example.org")]); - client.getRoom.mockImplementation(roomId => roomId === room.roomId ? room : null); + client.getRoom.mockImplementation((roomId) => (roomId === room.roomId ? room : null)); client.getRooms.mockReturnValue([room]); client.reEmitter.reEmit(room, [RoomStateEvent.Events]); client.sendStateEvent.mockImplementation(async (roomId, eventType, content, stateKey = "") => { @@ -384,13 +385,13 @@ describe("RoomHeader (React Testing Library)", () => { bob = mkRoomMember(room.roomId, "@bob:example.org"); carol = mkRoomMember(room.roomId, "@carol:example.org"); - client.getRoom.mockImplementation(roomId => roomId === room.roomId ? room : null); + client.getRoom.mockImplementation((roomId) => (roomId === room.roomId ? room : null)); client.getRooms.mockReturnValue([room]); client.reEmitter.reEmit(room, [RoomStateEvent.Events]); - await Promise.all([CallStore.instance, WidgetStore.instance].map( - store => setupAsyncStoreWithClient(store, client), - )); + await Promise.all( + [CallStore.instance, WidgetStore.instance].map((store) => setupAsyncStoreWithClient(store, client)), + ); jest.spyOn(MediaDeviceHandler, "getDevices").mockResolvedValue({ [MediaDeviceKindEnum.AudioInput]: [], @@ -412,13 +413,11 @@ describe("RoomHeader (React Testing Library)", () => { const mockRoomMembers = (members: RoomMember[]) => { jest.spyOn(room, "getJoinedMembers").mockReturnValue(members); jest.spyOn(room, "getMember").mockImplementation( - userId => members.find(member => member.userId === userId) ?? null, + (userId) => members.find((member) => member.userId === userId) ?? null, ); }; const mockEnabledSettings = (settings: string[]) => { - jest.spyOn(SettingsStore, "getValue").mockImplementation( - settingName => settings.includes(settingName), - ); + jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName) => settings.includes(settingName)); }; const mockEventPowerLevels = (events: { [eventType: string]: number }) => { room.currentState.setStateEvents([ @@ -435,7 +434,7 @@ describe("RoomHeader (React Testing Library)", () => { const mockLegacyCall = () => { jest.spyOn(LegacyCallHandler.instance, "getCallForRoom").mockReturnValue({} as unknown as MatrixCall); }; - const withCall = async (fn: (call: ElementCall) => (void | Promise)): Promise => { + const withCall = async (fn: (call: ElementCall) => void | Promise): Promise => { await ElementCall.create(room); const call = CallStore.instance.getCall(room.roomId); if (!(call instanceof ElementCall)) throw new Error("Failed to create call"); @@ -468,10 +467,10 @@ describe("RoomHeader (React Testing Library)", () => { { }} + onSearchClick={() => {}} onInviteClick={null} - onForgetClick={() => { }} - onAppsClick={() => { }} + onForgetClick={() => {}} + onAppsClick={() => {}} e2eStatus={E2EStatus.Normal} appsShown={true} searchInfo={{ @@ -505,13 +504,13 @@ describe("RoomHeader (React Testing Library)", () => { }); it( - "hides the voice call button and disables the video call button if configured to use Element Call exclusively " - + "and there's an ongoing call", + "hides the voice call button and disables the video call button if configured to use Element Call exclusively " + + "and there's an ongoing call", async () => { mockEnabledSettings(["showCallButtonsInComposer", "feature_group_calls"]); - SdkConfig.put( - { element_call: { url: "https://call.element.io", use_exclusively: true, brand: "Element Call" } }, - ); + SdkConfig.put({ + element_call: { url: "https://call.element.io", use_exclusively: true, brand: "Element Call" }, + }); await ElementCall.create(room); renderHeader(); @@ -521,13 +520,13 @@ describe("RoomHeader (React Testing Library)", () => { ); it( - "hides the voice call button and starts an Element call when the video call button is pressed if configured to " - + "use Element Call exclusively", + "hides the voice call button and starts an Element call when the video call button is pressed if configured to " + + "use Element Call exclusively", async () => { mockEnabledSettings(["showCallButtonsInComposer", "feature_group_calls"]); - SdkConfig.put( - { element_call: { url: "https://call.element.io", use_exclusively: true, brand: "Element Call" } }, - ); + SdkConfig.put({ + element_call: { url: "https://call.element.io", use_exclusively: true, brand: "Element Call" }, + }); renderHeader(); expect(screen.queryByRole("button", { name: "Voice call" })).toBeNull(); @@ -535,23 +534,25 @@ describe("RoomHeader (React Testing Library)", () => { const dispatcherSpy = jest.fn(); const dispatcherRef = defaultDispatcher.register(dispatcherSpy); fireEvent.click(screen.getByRole("button", { name: "Video call" })); - await waitFor(() => expect(dispatcherSpy).toHaveBeenCalledWith({ - action: Action.ViewRoom, - room_id: room.roomId, - view_call: true, - })); + await waitFor(() => + expect(dispatcherSpy).toHaveBeenCalledWith({ + action: Action.ViewRoom, + room_id: room.roomId, + view_call: true, + }), + ); defaultDispatcher.unregister(dispatcherRef); }, ); it( - "hides the voice call button and disables the video call button if configured to use Element Call exclusively " - + "and the user lacks permission", + "hides the voice call button and disables the video call button if configured to use Element Call exclusively " + + "and the user lacks permission", () => { mockEnabledSettings(["showCallButtonsInComposer", "feature_group_calls"]); - SdkConfig.put( - { element_call: { url: "https://call.element.io", use_exclusively: true, brand: "Element Call" } }, - ); + SdkConfig.put({ + element_call: { url: "https://call.element.io", use_exclusively: true, brand: "Element Call" }, + }); mockEventPowerLevels({ [ElementCall.CALL_EVENT_TYPE.name]: 100 }); renderHeader(); @@ -596,8 +597,8 @@ describe("RoomHeader (React Testing Library)", () => { }); it( - "starts a legacy 1:1 call when call buttons are pressed in the new group call experience if there's 1 other " - + "member", + "starts a legacy 1:1 call when call buttons are pressed in the new group call experience if there's 1 other " + + "member", async () => { mockEnabledSettings(["showCallButtonsInComposer", "feature_group_calls"]); mockRoomMembers([alice, bob]); @@ -617,8 +618,8 @@ describe("RoomHeader (React Testing Library)", () => { ); it( - "creates a Jitsi widget when call buttons are pressed in the new group call experience if the user lacks " - + "permission to start Element calls", + "creates a Jitsi widget when call buttons are pressed in the new group call experience if the user lacks " + + "permission to start Element calls", async () => { mockEnabledSettings(["showCallButtonsInComposer", "feature_group_calls"]); mockRoomMembers([alice, bob, carol]); @@ -639,8 +640,8 @@ describe("RoomHeader (React Testing Library)", () => { ); it( - "creates a Jitsi widget when the voice call button is pressed and shows a menu when the video call button is " - + "pressed in the new group call experience", + "creates a Jitsi widget when the voice call button is pressed and shows a menu when the video call button is " + + "pressed in the new group call experience", async () => { mockEnabledSettings(["showCallButtonsInComposer", "feature_group_calls"]); mockRoomMembers([alice, bob, carol]); @@ -664,18 +665,20 @@ describe("RoomHeader (React Testing Library)", () => { const dispatcherRef = defaultDispatcher.register(dispatcherSpy); fireEvent.click(screen.getByRole("button", { name: "Video call" })); fireEvent.click(getByRole(screen.getByRole("menu"), "menuitem", { name: /element/i })); - await waitFor(() => expect(dispatcherSpy).toHaveBeenCalledWith({ - action: Action.ViewRoom, - room_id: room.roomId, - view_call: true, - })); + await waitFor(() => + expect(dispatcherSpy).toHaveBeenCalledWith({ + action: Action.ViewRoom, + room_id: room.roomId, + view_call: true, + }), + ); defaultDispatcher.unregister(dispatcherRef); }, ); it( - "disables the voice call button and starts an Element call when the video call button is pressed in the new " - + "group call experience if the user lacks permission to edit widgets", + "disables the voice call button and starts an Element call when the video call button is pressed in the new " + + "group call experience if the user lacks permission to edit widgets", async () => { mockEnabledSettings(["showCallButtonsInComposer", "feature_group_calls"]); mockRoomMembers([alice, bob, carol]); @@ -687,11 +690,13 @@ describe("RoomHeader (React Testing Library)", () => { const dispatcherSpy = jest.fn(); const dispatcherRef = defaultDispatcher.register(dispatcherSpy); fireEvent.click(screen.getByRole("button", { name: "Video call" })); - await waitFor(() => expect(dispatcherSpy).toHaveBeenCalledWith({ - action: Action.ViewRoom, - room_id: room.roomId, - view_call: true, - })); + await waitFor(() => + expect(dispatcherSpy).toHaveBeenCalledWith({ + action: Action.ViewRoom, + room_id: room.roomId, + view_call: true, + }), + ); defaultDispatcher.unregister(dispatcherRef); }, ); @@ -785,28 +790,32 @@ describe("RoomHeader (React Testing Library)", () => { const dispatcherSpy = jest.fn(); const dispatcherRef = defaultDispatcher.register(dispatcherSpy); fireEvent.click(screen.getByRole("button", { name: /close/i })); - await waitFor(() => expect(dispatcherSpy).toHaveBeenCalledWith({ - action: Action.ViewRoom, - room_id: room.roomId, - view_call: false, - })); + await waitFor(() => + expect(dispatcherSpy).toHaveBeenCalledWith({ + action: Action.ViewRoom, + room_id: room.roomId, + view_call: false, + }), + ); defaultDispatcher.unregister(dispatcherRef); }); it("shows a reduce button when viewing a call that returns to the timeline when pressed", async () => { mockEnabledSettings(["feature_group_calls"]); - await withCall(async call => { + await withCall(async (call) => { renderHeader({ viewingCall: true, activeCall: call }); const dispatcherSpy = jest.fn(); const dispatcherRef = defaultDispatcher.register(dispatcherSpy); fireEvent.click(screen.getByRole("button", { name: /timeline/i })); - await waitFor(() => expect(dispatcherSpy).toHaveBeenCalledWith({ - action: Action.ViewRoom, - room_id: room.roomId, - view_call: false, - })); + await waitFor(() => + expect(dispatcherSpy).toHaveBeenCalledWith({ + action: Action.ViewRoom, + room_id: room.roomId, + view_call: false, + }), + ); defaultDispatcher.unregister(dispatcherRef); }); }); @@ -814,7 +823,7 @@ describe("RoomHeader (React Testing Library)", () => { it("shows a layout button when viewing a call that shows a menu when pressed", async () => { mockEnabledSettings(["feature_group_calls"]); - await withCall(async call => { + await withCall(async (call) => { await call.connect(); const messaging = WidgetMessagingStore.instance.getMessagingForUid(WidgetUtils.getWidgetUid(call.widget)); renderHeader({ viewingCall: true, activeCall: call }); diff --git a/test/components/views/rooms/RoomList-test.tsx b/test/components/views/rooms/RoomList-test.tsx index cb5ddb1ffa..e780f0a9ef 100644 --- a/test/components/views/rooms/RoomList-test.tsx +++ b/test/components/views/rooms/RoomList-test.tsx @@ -14,33 +14,29 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import ReactTestUtils from 'react-dom/test-utils'; -import ReactDOM from 'react-dom'; -import { - PendingEventOrdering, - Room, - RoomMember, -} from 'matrix-js-sdk/src/matrix'; +import React from "react"; +import ReactTestUtils from "react-dom/test-utils"; +import ReactDOM from "react-dom"; +import { PendingEventOrdering, Room, RoomMember } from "matrix-js-sdk/src/matrix"; -import * as TestUtils from '../../../test-utils'; -import { MatrixClientPeg } from '../../../../src/MatrixClientPeg'; -import dis from '../../../../src/dispatcher/dispatcher'; -import DMRoomMap from '../../../../src/utils/DMRoomMap'; +import * as TestUtils from "../../../test-utils"; +import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; +import dis from "../../../../src/dispatcher/dispatcher"; +import DMRoomMap from "../../../../src/utils/DMRoomMap"; import { DefaultTagID } from "../../../../src/stores/room-list/models"; import RoomListStore, { RoomListStoreClass } from "../../../../src/stores/room-list/RoomListStore"; import RoomListLayoutStore from "../../../../src/stores/room-list/RoomListLayoutStore"; import RoomList from "../../../../src/components/views/rooms/RoomList"; import RoomSublist from "../../../../src/components/views/rooms/RoomSublist"; import { RoomTile } from "../../../../src/components/views/rooms/RoomTile"; -import { getMockClientWithEventEmitter, mockClientMethodsUser } from '../../../test-utils'; -import ResizeNotifier from '../../../../src/utils/ResizeNotifier'; +import { getMockClientWithEventEmitter, mockClientMethodsUser } from "../../../test-utils"; +import ResizeNotifier from "../../../../src/utils/ResizeNotifier"; function generateRoomId() { - return '!' + Math.random().toString().slice(2, 10) + ':domain'; + return "!" + Math.random().toString().slice(2, 10) + ":domain"; } -describe('RoomList', () => { +describe("RoomList", () => { function createRoom(opts) { const room = new Room(generateRoomId(), MatrixClientPeg.get(), client.getUserId(), { // The room list now uses getPendingEvents(), so we need a detached ordering. @@ -54,9 +50,9 @@ describe('RoomList', () => { let parentDiv = null; let root = null; - const myUserId = '@me:domain'; + const myUserId = "@me:domain"; - const movingRoomId = '!someroomid'; + const movingRoomId = "!someroomid"; let movingRoom: Room | undefined; let otherRoom: Room | undefined; @@ -77,10 +73,10 @@ describe('RoomList', () => { onResize: jest.fn(), resizeNotifier: {} as unknown as ResizeNotifier, isMinimized: false, - activeSpace: '', + activeSpace: "", }; - beforeEach(async function(done) { + beforeEach(async function (done) { RoomListStoreClass.TEST_MODE = true; jest.clearAllMocks(); @@ -88,43 +84,42 @@ describe('RoomList', () => { DMRoomMap.makeShared(); - parentDiv = document.createElement('div'); + parentDiv = document.createElement("div"); document.body.appendChild(parentDiv); const WrappedRoomList = TestUtils.wrapInMatrixClientContext(RoomList); - root = ReactDOM.render( - , - parentDiv, - ); + root = ReactDOM.render(, parentDiv); ReactTestUtils.findRenderedComponentWithType(root, RoomList); - movingRoom = createRoom({ name: 'Moving room' }); + movingRoom = createRoom({ name: "Moving room" }); expect(movingRoom.roomId).not.toBe(null); // Mock joined member myMember = new RoomMember(movingRoomId, myUserId); - myMember.membership = 'join'; - movingRoom.updateMyMembership('join'); - movingRoom.getMember = (userId) => ({ - [client.credentials.userId]: myMember, - }[userId]); + myMember.membership = "join"; + movingRoom.updateMyMembership("join"); + movingRoom.getMember = (userId) => + ({ + [client.credentials.userId]: myMember, + }[userId]); - otherRoom = createRoom({ name: 'Other room' }); + otherRoom = createRoom({ name: "Other room" }); myOtherMember = new RoomMember(otherRoom.roomId, myUserId); - myOtherMember.membership = 'join'; - otherRoom.updateMyMembership('join'); - otherRoom.getMember = (userId) => ({ - [client.credentials.userId]: myOtherMember, - }[userId]); + myOtherMember.membership = "join"; + otherRoom.updateMyMembership("join"); + otherRoom.getMember = (userId) => + ({ + [client.credentials.userId]: myOtherMember, + }[userId]); // Mock the matrix client const mockRooms = [ movingRoom, otherRoom, - createRoom({ tags: { 'm.favourite': { order: 0.1 } }, name: 'Some other room' }), - createRoom({ tags: { 'm.favourite': { order: 0.2 } }, name: 'Some other room 2' }), - createRoom({ tags: { 'm.lowpriority': {} }, name: 'Some unimportant room' }), - createRoom({ tags: { 'custom.tag': {} }, name: 'Some room customly tagged' }), + createRoom({ tags: { "m.favourite": { order: 0.1 } }, name: "Some other room" }), + createRoom({ tags: { "m.favourite": { order: 0.2 } }, name: "Some other room 2" }), + createRoom({ tags: { "m.lowpriority": {} }, name: "Some unimportant room" }), + createRoom({ tags: { "custom.tag": {} }, name: "Some room customly tagged" }), ]; client.getRooms.mockReturnValue(mockRooms); client.getVisibleRooms.mockReturnValue(mockRooms); @@ -166,9 +161,14 @@ describe('RoomList', () => { expectedRoomTile = roomTiles.find((tile) => tile.props.room === room); } catch (err) { // truncate the error message because it's spammy - err.message = 'Error finding RoomTile for ' + room.roomId + ' in ' + - subListTest + ': ' + - err.message.split('componentType')[0] + '...'; + err.message = + "Error finding RoomTile for " + + room.roomId + + " in " + + subListTest + + ": " + + err.message.split("componentType")[0] + + "..."; throw err; } @@ -193,77 +193,81 @@ describe('RoomList', () => { // Mock inverse m.direct // @ts-ignore forcing private property DMRoomMap.shared().roomToUser = { - [movingRoom.roomId]: '@someotheruser:domain', + [movingRoom.roomId]: "@someotheruser:domain", }; } - dis.dispatch({ action: 'MatrixActions.sync', prevState: null, state: 'PREPARED', matrixClient: client }); + dis.dispatch({ action: "MatrixActions.sync", prevState: null, state: "PREPARED", matrixClient: client }); expectRoomInSubList(movingRoom, srcSubListTest); - dis.dispatch({ action: 'RoomListActions.tagRoom.pending', request: { - oldTagId, newTagId, room: movingRoom, - } }); + dis.dispatch({ + action: "RoomListActions.tagRoom.pending", + request: { + oldTagId, + newTagId, + room: movingRoom, + }, + }); expectRoomInSubList(movingRoom, destSubListTest); } function itDoesCorrectOptimisticUpdatesForDraggedRoomTiles() { // TODO: Re-enable dragging tests when we support dragging again. - describe.skip('does correct optimistic update when dragging from', () => { - it('rooms to people', () => { + describe.skip("does correct optimistic update when dragging from", () => { + it("rooms to people", () => { expectCorrectMove(undefined, DefaultTagID.DM); }); - it('rooms to favourites', () => { - expectCorrectMove(undefined, 'm.favourite'); + it("rooms to favourites", () => { + expectCorrectMove(undefined, "m.favourite"); }); - it('rooms to low priority', () => { - expectCorrectMove(undefined, 'm.lowpriority'); + it("rooms to low priority", () => { + expectCorrectMove(undefined, "m.lowpriority"); }); // XXX: Known to fail - the view does not update immediately to reflect the change. // Whe running the app live, it updates when some other event occurs (likely the // m.direct arriving) that these tests do not fire. - xit('people to rooms', () => { + xit("people to rooms", () => { expectCorrectMove(DefaultTagID.DM, undefined); }); - it('people to favourites', () => { - expectCorrectMove(DefaultTagID.DM, 'm.favourite'); + it("people to favourites", () => { + expectCorrectMove(DefaultTagID.DM, "m.favourite"); }); - it('people to lowpriority', () => { - expectCorrectMove(DefaultTagID.DM, 'm.lowpriority'); + it("people to lowpriority", () => { + expectCorrectMove(DefaultTagID.DM, "m.lowpriority"); }); - it('low priority to rooms', () => { - expectCorrectMove('m.lowpriority', undefined); + it("low priority to rooms", () => { + expectCorrectMove("m.lowpriority", undefined); }); - it('low priority to people', () => { - expectCorrectMove('m.lowpriority', DefaultTagID.DM); + it("low priority to people", () => { + expectCorrectMove("m.lowpriority", DefaultTagID.DM); }); - it('low priority to low priority', () => { - expectCorrectMove('m.lowpriority', 'm.lowpriority'); + it("low priority to low priority", () => { + expectCorrectMove("m.lowpriority", "m.lowpriority"); }); - it('favourites to rooms', () => { - expectCorrectMove('m.favourite', undefined); + it("favourites to rooms", () => { + expectCorrectMove("m.favourite", undefined); }); - it('favourites to people', () => { - expectCorrectMove('m.favourite', DefaultTagID.DM); + it("favourites to people", () => { + expectCorrectMove("m.favourite", DefaultTagID.DM); }); - it('favourites to low priority', () => { - expectCorrectMove('m.favourite', 'm.lowpriority'); + it("favourites to low priority", () => { + expectCorrectMove("m.favourite", "m.lowpriority"); }); }); } itDoesCorrectOptimisticUpdatesForDraggedRoomTiles(); }); - diff --git a/test/components/views/rooms/RoomListHeader-test.tsx b/test/components/views/rooms/RoomListHeader-test.tsx index f2702fc1c3..107fd56a90 100644 --- a/test/components/views/rooms/RoomListHeader-test.tsx +++ b/test/components/views/rooms/RoomListHeader-test.tsx @@ -14,13 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import { MatrixClient } from 'matrix-js-sdk/src/client'; -import { Room } from 'matrix-js-sdk/src/matrix'; +import React from "react"; +import { MatrixClient } from "matrix-js-sdk/src/client"; +import { Room } from "matrix-js-sdk/src/matrix"; import { EventType } from "matrix-js-sdk/src/@types/event"; import { act } from "react-dom/test-utils"; -import { mocked } from 'jest-mock'; -import { render, screen, fireEvent, RenderResult } from '@testing-library/react'; +import { mocked } from "jest-mock"; +import { render, screen, fireEvent, RenderResult } from "@testing-library/react"; import SpaceStore from "../../../../src/stores/spaces/SpaceStore"; import { MetaSpace } from "../../../../src/stores/spaces"; @@ -31,17 +31,17 @@ import DMRoomMap from "../../../../src/utils/DMRoomMap"; import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; import SettingsStore from "../../../../src/settings/SettingsStore"; import { SettingLevel } from "../../../../src/settings/SettingLevel"; -import { shouldShowComponent } from '../../../../src/customisations/helpers/UIComponents'; -import { UIComponent } from '../../../../src/settings/UIFeature'; +import { shouldShowComponent } from "../../../../src/customisations/helpers/UIComponents"; +import { UIComponent } from "../../../../src/settings/UIFeature"; const RoomListHeader = testUtils.wrapInMatrixClientContext(_RoomListHeader); -jest.mock('../../../../src/customisations/helpers/UIComponents', () => ({ +jest.mock("../../../../src/customisations/helpers/UIComponents", () => ({ shouldShowComponent: jest.fn(), })); const blockUIComponent = (component: UIComponent): void => { - mocked(shouldShowComponent).mockImplementation(feature => feature !== component); + mocked(shouldShowComponent).mockImplementation((feature) => feature !== component); }; const setupSpace = (client: MatrixClient): Room => { @@ -96,7 +96,7 @@ const checkMenuLabels = (items: NodeListOf, labelArray: Array) }; labelArray.forEach((label, index) => { - console.log('index', index, 'label', label); + console.log("index", index, "label", label); checkLabel(items[index], label); }); }; @@ -140,14 +140,7 @@ describe("RoomListHeader", () => { const menu = screen.getByRole("menu"); const items = menu.querySelectorAll(".mx_IconizedContextMenu_item"); - checkMenuLabels(items, [ - "Space home", - "Manage & explore rooms", - "Preferences", - "Settings", - "Room", - "Space", - ]); + checkMenuLabels(items, ["Space home", "Manage & explore rooms", "Preferences", "Settings", "Room", "Space"]); }); it("renders a plus menu for spaces", async () => { @@ -157,12 +150,7 @@ describe("RoomListHeader", () => { const menu = screen.getByRole("menu"); const items = menu.querySelectorAll(".mx_IconizedContextMenu_item"); - checkMenuLabels(items, [ - "New room", - "Explore rooms", - "Add existing room", - "Add space", - ]); + checkMenuLabels(items, ["New room", "Explore rooms", "Add existing room", "Add space"]); }); it("closes menu if space changes from under it", async () => { @@ -182,9 +170,9 @@ describe("RoomListHeader", () => { expect(screen.queryByRole("menu")).toBeFalsy(); }); - describe('UIComponents', () => { - describe('Main menu', () => { - it('does not render Add Space when user does not have permission to add spaces', async () => { + describe("UIComponents", () => { + describe("Main menu", () => { + it("does not render Add Space when user does not have permission to add spaces", async () => { // User does not have permission to add spaces, anywhere blockUIComponent(UIComponent.CreateSpaces); @@ -203,7 +191,7 @@ describe("RoomListHeader", () => { ]); }); - it('does not render Add Room when user does not have permission to add rooms', async () => { + it("does not render Add Room when user does not have permission to add rooms", async () => { // User does not have permission to add rooms blockUIComponent(UIComponent.CreateRooms); @@ -223,8 +211,8 @@ describe("RoomListHeader", () => { }); }); - describe('Plus menu', () => { - it('does not render Add Space when user does not have permission to add spaces', async () => { + describe("Plus menu", () => { + it("does not render Add Space when user does not have permission to add spaces", async () => { // User does not have permission to add spaces, anywhere blockUIComponent(UIComponent.CreateSpaces); @@ -242,7 +230,7 @@ describe("RoomListHeader", () => { ]); }); - it('disables Add Room when user does not have permission to add rooms', async () => { + it("disables Add Room when user does not have permission to add rooms", async () => { // User does not have permission to add rooms blockUIComponent(UIComponent.CreateRooms); @@ -252,12 +240,7 @@ describe("RoomListHeader", () => { const menu = screen.getByRole("menu"); const items = menu.querySelectorAll(".mx_IconizedContextMenu_item"); - checkMenuLabels(items, [ - "New room", - "Explore rooms", - "Add existing room", - "Add space", - ]); + checkMenuLabels(items, ["New room", "Explore rooms", "Add existing room", "Add space"]); // "Add existing room" is disabled checkIsDisabled(items[2]); @@ -265,11 +248,12 @@ describe("RoomListHeader", () => { }); }); - describe('adding children to space', () => { - it('if user cannot add children to space, MainMenu adding buttons are hidden', async () => { + describe("adding children to space", () => { + it("if user cannot add children to space, MainMenu adding buttons are hidden", async () => { const testSpace = setupSpace(client); mocked(testSpace.currentState.maySendStateEvent).mockImplementation( - (stateEventType, userId) => stateEventType !== EventType.SpaceChild); + (stateEventType, userId) => stateEventType !== EventType.SpaceChild, + ); await setupMainMenu(client, testSpace); @@ -285,22 +269,18 @@ describe("RoomListHeader", () => { ]); }); - it('if user cannot add children to space, PlusMenu add buttons are disabled', async () => { + it("if user cannot add children to space, PlusMenu add buttons are disabled", async () => { const testSpace = setupSpace(client); mocked(testSpace.currentState.maySendStateEvent).mockImplementation( - (stateEventType, userId) => stateEventType !== EventType.SpaceChild); + (stateEventType, userId) => stateEventType !== EventType.SpaceChild, + ); await setupPlusMenu(client, testSpace); const menu = screen.getByRole("menu"); const items = menu.querySelectorAll(".mx_IconizedContextMenu_item"); - checkMenuLabels(items, [ - "New room", - "Explore rooms", - "Add existing room", - "Add space", - ]); + checkMenuLabels(items, ["New room", "Explore rooms", "Add existing room", "Add space"]); // "Add existing room" is disabled checkIsDisabled(items[2]); diff --git a/test/components/views/rooms/RoomPreviewBar-test.tsx b/test/components/views/rooms/RoomPreviewBar-test.tsx index e7c8fa7281..f0e2819b73 100644 --- a/test/components/views/rooms/RoomPreviewBar-test.tsx +++ b/test/components/views/rooms/RoomPreviewBar-test.tsx @@ -14,62 +14,62 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; import { render, fireEvent, RenderResult, waitFor } from "@testing-library/react"; -import { Room, RoomMember, MatrixError, IContent } from 'matrix-js-sdk/src/matrix'; +import { Room, RoomMember, MatrixError, IContent } from "matrix-js-sdk/src/matrix"; -import { stubClient } from '../../../test-utils'; -import { MatrixClientPeg } from '../../../../src/MatrixClientPeg'; -import DMRoomMap from '../../../../src/utils/DMRoomMap'; -import RoomPreviewBar from '../../../../src/components/views/rooms/RoomPreviewBar'; +import { stubClient } from "../../../test-utils"; +import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; +import DMRoomMap from "../../../../src/utils/DMRoomMap"; +import RoomPreviewBar from "../../../../src/components/views/rooms/RoomPreviewBar"; import defaultDispatcher from "../../../../src/dispatcher/dispatcher"; -jest.mock('../../../../src/IdentityAuthClient', () => { +jest.mock("../../../../src/IdentityAuthClient", () => { return jest.fn().mockImplementation(() => { - return { getAccessToken: jest.fn().mockResolvedValue('mock-token') }; + return { getAccessToken: jest.fn().mockResolvedValue("mock-token") }; }); }); jest.useRealTimers(); const createRoom = (roomId: string, userId: string): Room => { - const newRoom = new Room( - roomId, - MatrixClientPeg.get(), - userId, - {}, - ); + const newRoom = new Room(roomId, MatrixClientPeg.get(), userId, {}); DMRoomMap.makeShared().start(); return newRoom; }; -const makeMockRoomMember = ( - { userId, isKicked, membership, content, memberContent }: - {userId?: string; - isKicked?: boolean; - membership?: 'invite' | 'ban'; - content?: Partial; - memberContent?: Partial; - }, -) => ({ +const makeMockRoomMember = ({ userId, - rawDisplayName: `${userId} name`, - isKicked: jest.fn().mockReturnValue(!!isKicked), - getContent: jest.fn().mockReturnValue(content || {}), + isKicked, membership, - events: { - member: { - getSender: jest.fn().mockReturnValue('@kicker:test.com'), - getContent: jest.fn().mockReturnValue({ reason: 'test reason', ...memberContent }), + content, + memberContent, +}: { + userId?: string; + isKicked?: boolean; + membership?: "invite" | "ban"; + content?: Partial; + memberContent?: Partial; +}) => + ({ + userId, + rawDisplayName: `${userId} name`, + isKicked: jest.fn().mockReturnValue(!!isKicked), + getContent: jest.fn().mockReturnValue(content || {}), + membership, + events: { + member: { + getSender: jest.fn().mockReturnValue("@kicker:test.com"), + getContent: jest.fn().mockReturnValue({ reason: "test reason", ...memberContent }), + }, }, - }, -}) as unknown as RoomMember; + } as unknown as RoomMember); -describe('', () => { - const roomId = 'RoomPreviewBar-test-room'; - const userId = '@tester:test.com'; - const inviterUserId = '@inviter:test.com'; - const otherUserId = '@othertester:test.com'; +describe("", () => { + const roomId = "RoomPreviewBar-test-room"; + const userId = "@tester:test.com"; + const inviterUserId = "@inviter:test.com"; + const otherUserId = "@othertester:test.com"; const getComponent = (props = {}) => { const defaultProps = { @@ -78,15 +78,15 @@ describe('', () => { return render(); }; - const isSpinnerRendered = (wrapper: RenderResult) => !!wrapper.container.querySelector('.mx_Spinner'); + const isSpinnerRendered = (wrapper: RenderResult) => !!wrapper.container.querySelector(".mx_Spinner"); const getMessage = (wrapper: RenderResult) => - wrapper.container.querySelector('.mx_RoomPreviewBar_message'); + wrapper.container.querySelector(".mx_RoomPreviewBar_message"); const getActions = (wrapper: RenderResult) => - wrapper.container.querySelector('.mx_RoomPreviewBar_actions'); + wrapper.container.querySelector(".mx_RoomPreviewBar_actions"); const getPrimaryActionButton = (wrapper: RenderResult) => - getActions(wrapper).querySelector('.mx_AccessibleButton_kind_primary'); + getActions(wrapper).querySelector(".mx_AccessibleButton_kind_primary"); const getSecondaryActionButton = (wrapper: RenderResult) => - getActions(wrapper).querySelector('.mx_AccessibleButton_kind_secondary'); + getActions(wrapper).querySelector(".mx_AccessibleButton_kind_secondary"); beforeEach(() => { stubClient(); @@ -98,29 +98,29 @@ describe('', () => { container && document.body.removeChild(container); }); - it('renders joining message', () => { + it("renders joining message", () => { const component = getComponent({ joining: true }); expect(isSpinnerRendered(component)).toBeTruthy(); - expect(getMessage(component).textContent).toEqual('Joining …'); + expect(getMessage(component).textContent).toEqual("Joining …"); }); - it('renders rejecting message', () => { + it("renders rejecting message", () => { const component = getComponent({ rejecting: true }); expect(isSpinnerRendered(component)).toBeTruthy(); - expect(getMessage(component).textContent).toEqual('Rejecting invite …'); + expect(getMessage(component).textContent).toEqual("Rejecting invite …"); }); - it('renders loading message', () => { + it("renders loading message", () => { const component = getComponent({ loading: true }); expect(isSpinnerRendered(component)).toBeTruthy(); - expect(getMessage(component).textContent).toEqual('Loading …'); + expect(getMessage(component).textContent).toEqual("Loading …"); }); - it('renders not logged in message', () => { + it("renders not logged in message", () => { MatrixClientPeg.get().isGuest = jest.fn().mockReturnValue(true); const component = getComponent({ loading: true }); expect(isSpinnerRendered(component)).toBeFalsy(); - expect(getMessage(component).textContent).toEqual('Join the conversation with an account'); + expect(getMessage(component).textContent).toEqual("Join the conversation with an account"); }); it("should send room oob data to start login", async () => { @@ -136,52 +136,56 @@ describe('', () => { const dispatcherSpy = jest.fn(); const dispatcherRef = defaultDispatcher.register(dispatcherSpy); - expect(getMessage(component).textContent).toEqual('Join the conversation with an account'); + expect(getMessage(component).textContent).toEqual("Join the conversation with an account"); fireEvent.click(getPrimaryActionButton(component)); - await waitFor(() => expect(dispatcherSpy).toHaveBeenCalledWith(expect.objectContaining({ - screenAfterLogin: { - screen: 'room', - params: expect.objectContaining({ - room_name: "Room Name", - room_avatar_url: "mxc://foo/bar", - inviter_name: "Charlie", + await waitFor(() => + expect(dispatcherSpy).toHaveBeenCalledWith( + expect.objectContaining({ + screenAfterLogin: { + screen: "room", + params: expect.objectContaining({ + room_name: "Room Name", + room_avatar_url: "mxc://foo/bar", + inviter_name: "Charlie", + }), + }, }), - }, - }))); + ), + ); defaultDispatcher.unregister(dispatcherRef); }); - it('renders kicked message', () => { + it("renders kicked message", () => { const room = createRoom(roomId, otherUserId); - jest.spyOn(room, 'getMember').mockReturnValue(makeMockRoomMember({ isKicked: true })); + jest.spyOn(room, "getMember").mockReturnValue(makeMockRoomMember({ isKicked: true })); const component = getComponent({ loading: true, room }); expect(getMessage(component)).toMatchSnapshot(); }); - it('renders banned message', () => { + it("renders banned message", () => { const room = createRoom(roomId, otherUserId); - jest.spyOn(room, 'getMember').mockReturnValue(makeMockRoomMember({ membership: 'ban' })); + jest.spyOn(room, "getMember").mockReturnValue(makeMockRoomMember({ membership: "ban" })); const component = getComponent({ loading: true, room }); expect(getMessage(component)).toMatchSnapshot(); }); - describe('with an error', () => { - it('renders room not found error', () => { + describe("with an error", () => { + it("renders room not found error", () => { const error = new MatrixError({ - errcode: 'M_NOT_FOUND', + errcode: "M_NOT_FOUND", error: "Room not found", }); const component = getComponent({ error }); expect(getMessage(component)).toMatchSnapshot(); }); - it('renders other errors', () => { + it("renders other errors", () => { const error = new MatrixError({ - errcode: 'Something_else', + errcode: "Something_else", }); const component = getComponent({ error }); @@ -189,33 +193,35 @@ describe('', () => { }); }); - it('renders viewing room message when room an be previewed', () => { + it("renders viewing room message when room an be previewed", () => { const component = getComponent({ canPreview: true }); expect(getMessage(component)).toMatchSnapshot(); }); - it('renders viewing room message when room can not be previewed', () => { + it("renders viewing room message when room can not be previewed", () => { const component = getComponent({ canPreview: false }); expect(getMessage(component)).toMatchSnapshot(); }); - describe('with an invite', () => { + describe("with an invite", () => { const inviterName = inviterUserId; const userMember = makeMockRoomMember({ userId }); const userMemberWithDmInvite = makeMockRoomMember({ - userId, membership: 'invite', memberContent: { is_direct: true }, + userId, + membership: "invite", + memberContent: { is_direct: true }, }); const inviterMember = makeMockRoomMember({ userId: inviterUserId, content: { - "reason": 'test', - 'io.element.html_reason': '

hello

', + "reason": "test", + "io.element.html_reason": "

hello

", }, }); - describe('without an invited email', () => { - describe('for a non-dm room', () => { + describe("without an invited email", () => { + describe("for a non-dm room", () => { const mockGetMember = (id) => { if (id === userId) return userMember; return inviterMember; @@ -226,44 +232,48 @@ describe('', () => { beforeEach(() => { room = createRoom(roomId, userId); - jest.spyOn(room, 'getMember').mockImplementation(mockGetMember); - jest.spyOn(room.currentState, 'getMember').mockImplementation(mockGetMember); + jest.spyOn(room, "getMember").mockImplementation(mockGetMember); + jest.spyOn(room.currentState, "getMember").mockImplementation(mockGetMember); onJoinClick.mockClear(); onRejectClick.mockClear(); }); - it('renders invite message', () => { + it("renders invite message", () => { const component = getComponent({ inviterName, room }); expect(getMessage(component)).toMatchSnapshot(); }); - it('renders join and reject action buttons correctly', () => { + it("renders join and reject action buttons correctly", () => { const component = getComponent({ inviterName, room, onJoinClick, onRejectClick }); expect(getActions(component)).toMatchSnapshot(); }); - it('renders reject and ignore action buttons when handler is provided', () => { + it("renders reject and ignore action buttons when handler is provided", () => { const onRejectAndIgnoreClick = jest.fn(); const component = getComponent({ - inviterName, room, onJoinClick, onRejectClick, onRejectAndIgnoreClick, + inviterName, + room, + onJoinClick, + onRejectClick, + onRejectAndIgnoreClick, }); expect(getActions(component)).toMatchSnapshot(); }); - it('renders join and reject action buttons in reverse order when room can previewed', () => { + it("renders join and reject action buttons in reverse order when room can previewed", () => { // when room is previewed action buttons are rendered left to right, with primary on the right const component = getComponent({ inviterName, room, onJoinClick, onRejectClick, canPreview: true }); expect(getActions(component)).toMatchSnapshot(); }); - it('joins room on primary button click', () => { + it("joins room on primary button click", () => { const component = getComponent({ inviterName, room, onJoinClick, onRejectClick }); fireEvent.click(getPrimaryActionButton(component)); expect(onJoinClick).toHaveBeenCalled(); }); - it('rejects invite on secondary button click', () => { + it("rejects invite on secondary button click", () => { const component = getComponent({ inviterName, room, onJoinClick, onRejectClick }); fireEvent.click(getSecondaryActionButton(component)); @@ -271,7 +281,7 @@ describe('', () => { }); }); - describe('for a dm room', () => { + describe("for a dm room", () => { const mockGetMember = (id) => { if (id === userId) return userMemberWithDmInvite; return inviterMember; @@ -282,32 +292,36 @@ describe('', () => { beforeEach(() => { room = createRoom(roomId, userId); - jest.spyOn(room, 'getMember').mockImplementation(mockGetMember); - jest.spyOn(room.currentState, 'getMember').mockImplementation(mockGetMember); + jest.spyOn(room, "getMember").mockImplementation(mockGetMember); + jest.spyOn(room.currentState, "getMember").mockImplementation(mockGetMember); onJoinClick.mockClear(); onRejectClick.mockClear(); }); - it('renders invite message to a non-dm room', () => { + it("renders invite message to a non-dm room", () => { const component = getComponent({ inviterName, room }); expect(getMessage(component)).toMatchSnapshot(); }); - it('renders join and reject action buttons with correct labels', () => { + it("renders join and reject action buttons with correct labels", () => { const onRejectAndIgnoreClick = jest.fn(); const component = getComponent({ - inviterName, room, onJoinClick, onRejectAndIgnoreClick, onRejectClick, + inviterName, + room, + onJoinClick, + onRejectAndIgnoreClick, + onRejectClick, }); expect(getActions(component)).toMatchSnapshot(); }); }); }); - describe('with an invited email', () => { - const invitedEmail = 'test@test.com'; + describe("with an invited email", () => { + const invitedEmail = "test@test.com"; const mockThreePids = [ - { medium: 'email', address: invitedEmail }, - { medium: 'not-email', address: 'address 2' }, + { medium: "email", address: invitedEmail }, + { medium: "not-email", address: "address 2" }, ]; const testJoinButton = (props) => async () => { @@ -321,74 +335,76 @@ describe('', () => { expect(onJoinClick).toHaveBeenCalled(); }; - describe('when client fails to get 3PIDs', () => { + describe("when client fails to get 3PIDs", () => { beforeEach(() => { - MatrixClientPeg.get().getThreePids = jest.fn().mockRejectedValue({ errCode: 'TEST_ERROR' }); + MatrixClientPeg.get().getThreePids = jest.fn().mockRejectedValue({ errCode: "TEST_ERROR" }); }); - it('renders error message', async () => { + it("renders error message", async () => { const component = getComponent({ inviterName, invitedEmail }); await new Promise(setImmediate); expect(getMessage(component)).toMatchSnapshot(); }); - it('renders join button', testJoinButton({ inviterName, invitedEmail })); + it("renders join button", testJoinButton({ inviterName, invitedEmail })); }); - describe('when invitedEmail is not associated with current account', () => { + describe("when invitedEmail is not associated with current account", () => { beforeEach(() => { - MatrixClientPeg.get().getThreePids = jest.fn().mockResolvedValue( - { threepids: mockThreePids.slice(1) }, - ); + MatrixClientPeg.get().getThreePids = jest + .fn() + .mockResolvedValue({ threepids: mockThreePids.slice(1) }); }); - it('renders invite message with invited email', async () => { + it("renders invite message with invited email", async () => { const component = getComponent({ inviterName, invitedEmail }); await new Promise(setImmediate); expect(getMessage(component)).toMatchSnapshot(); }); - it('renders join button', testJoinButton({ inviterName, invitedEmail })); + it("renders join button", testJoinButton({ inviterName, invitedEmail })); }); - describe('when client has no identity server connected', () => { + describe("when client has no identity server connected", () => { beforeEach(() => { MatrixClientPeg.get().getThreePids = jest.fn().mockResolvedValue({ threepids: mockThreePids }); MatrixClientPeg.get().getIdentityServerUrl = jest.fn().mockReturnValue(false); }); - it('renders invite message with invited email', async () => { + it("renders invite message with invited email", async () => { const component = getComponent({ inviterName, invitedEmail }); await new Promise(setImmediate); expect(getMessage(component)).toMatchSnapshot(); }); - it('renders join button', testJoinButton({ inviterName, invitedEmail })); + it("renders join button", testJoinButton({ inviterName, invitedEmail })); }); - describe('when client has an identity server connected', () => { + describe("when client has an identity server connected", () => { beforeEach(() => { MatrixClientPeg.get().getThreePids = jest.fn().mockResolvedValue({ threepids: mockThreePids }); - MatrixClientPeg.get().getIdentityServerUrl = jest.fn().mockReturnValue('identity.test'); - MatrixClientPeg.get().lookupThreePid = jest.fn().mockResolvedValue('identity.test'); + MatrixClientPeg.get().getIdentityServerUrl = jest.fn().mockReturnValue("identity.test"); + MatrixClientPeg.get().lookupThreePid = jest.fn().mockResolvedValue("identity.test"); }); - it('renders email mismatch message when invite email mxid doesnt match', async () => { - MatrixClientPeg.get().lookupThreePid = jest.fn().mockReturnValue('not userid'); + it("renders email mismatch message when invite email mxid doesnt match", async () => { + MatrixClientPeg.get().lookupThreePid = jest.fn().mockReturnValue("not userid"); const component = getComponent({ inviterName, invitedEmail }); await new Promise(setImmediate); expect(getMessage(component)).toMatchSnapshot(); expect(MatrixClientPeg.get().lookupThreePid).toHaveBeenCalledWith( - 'email', invitedEmail, 'mock-token', + "email", + invitedEmail, + "mock-token", ); await testJoinButton({ inviterName, invitedEmail })(); }); - it('renders invite message when invite email mxid match', async () => { + it("renders invite message when invite email mxid match", async () => { MatrixClientPeg.get().lookupThreePid = jest.fn().mockReturnValue(userId); const component = getComponent({ inviterName, invitedEmail }); await new Promise(setImmediate); diff --git a/test/components/views/rooms/RoomPreviewCard-test.tsx b/test/components/views/rooms/RoomPreviewCard-test.tsx index a453f70dcc..19ad62e0d2 100644 --- a/test/components/views/rooms/RoomPreviewCard-test.tsx +++ b/test/components/views/rooms/RoomPreviewCard-test.tsx @@ -48,14 +48,14 @@ describe("RoomPreviewCard", () => { pendingEventOrdering: PendingEventOrdering.Detached, }); alice = mkRoomMember(room.roomId, "@alice:example.org"); - jest.spyOn(room, "getMember").mockImplementation(userId => userId === alice.userId ? alice : null); + jest.spyOn(room, "getMember").mockImplementation((userId) => (userId === alice.userId ? alice : null)); - client.getRoom.mockImplementation(roomId => roomId === room.roomId ? room : null); + client.getRoom.mockImplementation((roomId) => (roomId === room.roomId ? room : null)); client.getRooms.mockReturnValue([room]); client.reEmitter.reEmit(room, [RoomStateEvent.Events]); enabledFeatures = []; - jest.spyOn(SettingsStore, "getValue").mockImplementation(settingName => + jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName) => enabledFeatures.includes(settingName) ? true : undefined, ); }); @@ -66,13 +66,7 @@ describe("RoomPreviewCard", () => { }); const renderPreview = async (): Promise => { - render( - { }} - onRejectButtonClicked={() => { }} - />, - ); + render( {}} onRejectButtonClicked={() => {}} />); await act(() => Promise.resolve()); // Allow effects to settle }; diff --git a/test/components/views/rooms/RoomTile-test.tsx b/test/components/views/rooms/RoomTile-test.tsx index 4a3aa95937..5017f1d18c 100644 --- a/test/components/views/rooms/RoomTile-test.tsx +++ b/test/components/views/rooms/RoomTile-test.tsx @@ -45,8 +45,9 @@ import { VoiceBroadcastInfoState } from "../../../../src/voice-broadcast"; import { mkVoiceBroadcastInfoStateEvent } from "../../../voice-broadcast/utils/test-utils"; describe("RoomTile", () => { - jest.spyOn(PlatformPeg, "get") - .mockReturnValue({ overrideBrowserShortcuts: () => false } as unknown as BasePlatform); + jest.spyOn(PlatformPeg, "get").mockReturnValue({ + overrideBrowserShortcuts: () => false, + } as unknown as BasePlatform); useMockedCalls(); const setUpVoiceBroadcast = (state: VoiceBroadcastInfoState): void => { @@ -64,12 +65,7 @@ describe("RoomTile", () => { const renderRoomTile = (): void => { renderResult = render( - , + , ); }; @@ -93,7 +89,7 @@ describe("RoomTile", () => { pendingEventOrdering: PendingEventOrdering.Detached, }); - client.getRoom.mockImplementation(roomId => roomId === room.roomId ? room : null); + client.getRoom.mockImplementation((roomId) => (roomId === room.roomId ? room : null)); client.getRooms.mockReturnValue([room]); client.reEmitter.reEmit(room, [RoomStateEvent.Events]); @@ -141,7 +137,7 @@ describe("RoomTile", () => { // Insert an await point in the connection method so we can inspect // the intermediate connecting state let completeConnection: () => void; - const connectionCompleted = new Promise(resolve => completeConnection = resolve); + const connectionCompleted = new Promise((resolve) => (completeConnection = resolve)); jest.spyOn(call, "performConnection").mockReturnValue(connectionCompleted); await Promise.all([ @@ -154,32 +150,32 @@ describe("RoomTile", () => { call.connect(), ]); - await Promise.all([ - screen.findByText("Video"), - call.disconnect(), - ]); + await Promise.all([screen.findByText("Video"), call.disconnect()]); }); it("tracks participants", () => { - const alice: [RoomMember, Set] = [ - mkRoomMember(room.roomId, "@alice:example.org"), new Set(["a"]), - ]; + const alice: [RoomMember, Set] = [mkRoomMember(room.roomId, "@alice:example.org"), new Set(["a"])]; const bob: [RoomMember, Set] = [ - mkRoomMember(room.roomId, "@bob:example.org"), new Set(["b1", "b2"]), - ]; - const carol: [RoomMember, Set] = [ - mkRoomMember(room.roomId, "@carol:example.org"), new Set(["c"]), + mkRoomMember(room.roomId, "@bob:example.org"), + new Set(["b1", "b2"]), ]; + const carol: [RoomMember, Set] = [mkRoomMember(room.roomId, "@carol:example.org"), new Set(["c"])]; expect(screen.queryByLabelText(/participant/)).toBe(null); - act(() => { call.participants = new Map([alice]); }); + act(() => { + call.participants = new Map([alice]); + }); expect(screen.getByLabelText("1 participant").textContent).toBe("1"); - act(() => { call.participants = new Map([alice, bob, carol]); }); + act(() => { + call.participants = new Map([alice, bob, carol]); + }); expect(screen.getByLabelText("4 participants").textContent).toBe("4"); - act(() => { call.participants = new Map(); }); + act(() => { + call.participants = new Map(); + }); expect(screen.queryByLabelText(/participant/)).toBe(null); }); diff --git a/test/components/views/rooms/SearchBar-test.tsx b/test/components/views/rooms/SearchBar-test.tsx index e1d07e73fd..2dc44c1cb5 100644 --- a/test/components/views/rooms/SearchBar-test.tsx +++ b/test/components/views/rooms/SearchBar-test.tsx @@ -14,8 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import { fireEvent, render } from '@testing-library/react'; +import React from "react"; +import { fireEvent, render } from "@testing-library/react"; import SearchBar, { SearchScope } from "../../../../src/components/views/rooms/SearchBar"; import { KeyBindingAction } from "../../../../src/accessibility/KeyboardShortcuts"; @@ -31,8 +31,7 @@ const searchProps = { jest.mock("../../../../src/KeyBindingsManager", () => ({ __esModule: true, - getKeyBindingsManager: jest.fn(() => ( - { getAccessibilityAction: jest.fn(() => mockCurrentEvent) })), + getKeyBindingsManager: jest.fn(() => ({ getAccessibilityAction: jest.fn(() => mockCurrentEvent) })), })); describe("SearchBar", () => { @@ -91,7 +90,7 @@ describe("SearchBar", () => { expect(searchProps.onCancelClick).toHaveBeenCalledTimes(1); fireEvent.focus(input!); - fireEvent.keyDown(input!, { key: 'Escape', code: 'Escape', charCode: 27 }); + fireEvent.keyDown(input!, { key: "Escape", code: "Escape", charCode: 27 }); expect(searchProps.onCancelClick).toHaveBeenCalledTimes(2); }); diff --git a/test/components/views/rooms/SearchResultTile-test.tsx b/test/components/views/rooms/SearchResultTile-test.tsx index 5293e4dff2..bb214e2ada 100644 --- a/test/components/views/rooms/SearchResultTile-test.tsx +++ b/test/components/views/rooms/SearchResultTile-test.tsx @@ -32,46 +32,53 @@ describe("SearchResultTile", () => { it("Sets up appropriate callEventGrouper for m.call. events", () => { const { container } = render( This is an example text message
", - msgtype: "m.text", + searchResult={SearchResult.fromJson( + { + rank: 0.00424866, + result: { + content: { + body: "This is an example text message", + format: "org.matrix.custom.html", + formatted_body: "This is an example text message", + msgtype: "m.text", + }, + event_id: "$144429830826TWwbB:localhost", + origin_server_ts: 1432735824653, + room_id: "!qPewotXpIctQySfjSy:localhost", + sender: "@example:example.org", + type: "m.room.message", + unsigned: { + age: 1234, + }, }, - event_id: "$144429830826TWwbB:localhost", - origin_server_ts: 1432735824653, - room_id: "!qPewotXpIctQySfjSy:localhost", - sender: "@example:example.org", - type: "m.room.message", - unsigned: { - age: 1234, + context: { + end: "", + start: "", + profile_info: {}, + events_before: [ + { + type: EventType.CallInvite, + sender: "@user1:server", + room_id: "!qPewotXpIctQySfjSy:localhost", + origin_server_ts: 1432735824652, + content: { call_id: "call.1" }, + event_id: "$1:server", + }, + ], + events_after: [ + { + type: EventType.CallAnswer, + sender: "@user2:server", + room_id: "!qPewotXpIctQySfjSy:localhost", + origin_server_ts: 1432735824654, + content: { call_id: "call.1" }, + event_id: "$2:server", + }, + ], }, }, - context: { - end: "", - start: "", - profile_info: {}, - events_before: [{ - type: EventType.CallInvite, - sender: "@user1:server", - room_id: "!qPewotXpIctQySfjSy:localhost", - origin_server_ts: 1432735824652, - content: { call_id: "call.1" }, - event_id: "$1:server", - }], - events_after: [{ - type: EventType.CallAnswer, - sender: "@user2:server", - room_id: "!qPewotXpIctQySfjSy:localhost", - origin_server_ts: 1432735824654, - content: { call_id: "call.1" }, - event_id: "$2:server", - }], - }, - }, o => new MatrixEvent(o))} + (o) => new MatrixEvent(o), + )} />, ); diff --git a/test/components/views/rooms/SendMessageComposer-test.tsx b/test/components/views/rooms/SendMessageComposer-test.tsx index 4aaf870119..1005758ae9 100644 --- a/test/components/views/rooms/SendMessageComposer-test.tsx +++ b/test/components/views/rooms/SendMessageComposer-test.tsx @@ -30,8 +30,8 @@ import { createPartCreator, createRenderer } from "../../../editor/mock"; import { createTestClient, mkEvent, mkStubRoom } from "../../../test-utils"; import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; import defaultDispatcher from "../../../../src/dispatcher/dispatcher"; -import DocumentOffset from '../../../../src/editor/offset'; -import { Layout } from '../../../../src/settings/enums/Layout'; +import DocumentOffset from "../../../../src/editor/offset"; +import { Layout } from "../../../../src/settings/enums/Layout"; import { IRoomState } from "../../../../src/components/structures/RoomView"; import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks"; import { mockPlatformPeg } from "../../../test-utils/platform"; @@ -42,7 +42,7 @@ jest.mock("../../../../src/utils/local-room", () => ({ doMaybeLocalRoomAction: jest.fn(), })); -describe('', () => { +describe("", () => { const defaultRoomContext: IRoomState = { roomLoading: true, peekLoading: false, @@ -156,16 +156,16 @@ describe('', () => { describe("functions correctly mounted", () => { const mockClient = createTestClient(); - jest.spyOn(MatrixClientPeg, 'get').mockReturnValue(mockClient); - const mockRoom = mkStubRoom('myfakeroom', 'myfakeroom', mockClient) as any; + jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient); + const mockRoom = mkStubRoom("myfakeroom", "myfakeroom", mockClient) as any; const mockEvent = mkEvent({ type: "m.room.message", - room: 'myfakeroom', - user: 'myfakeuser', - content: { "msgtype": "m.text", "body": "Replying to this" }, + room: "myfakeroom", + user: "myfakeuser", + content: { msgtype: "m.text", body: "Replying to this" }, event: true, }); - mockRoom.findEventById = jest.fn(eventId => { + mockRoom.findEventById = jest.fn((eventId) => { return eventId === mockEvent.getId() ? mockEvent : null; }); @@ -216,7 +216,7 @@ describe('', () => { // ensure the right state was persisted to localStorage unmount(); expect(JSON.parse(localStorage.getItem(key))).toStrictEqual({ - parts: [{ "type": "plain", "text": "Test Text" }], + parts: [{ type: "plain", text: "Test Text" }], replyEventId: mockEvent.getId(), }); @@ -247,9 +247,9 @@ describe('', () => { expect(localStorage.getItem(key)).toBeNull(); // ensure the right state was persisted to localStorage - window.dispatchEvent(new Event('beforeunload')); + window.dispatchEvent(new Event("beforeunload")); expect(JSON.parse(localStorage.getItem(key))).toStrictEqual({ - parts: [{ "type": "plain", "text": "Hello World" }], + parts: [{ type: "plain", text: "Hello World" }], }); }); @@ -272,19 +272,17 @@ describe('', () => { expect(container.textContent).toBe(""); const str = sessionStorage.getItem(`mx_cider_history_${mockRoom.roomId}[0]`); expect(JSON.parse(str)).toStrictEqual({ - parts: [{ "type": "plain", "text": "This is a message" }], + parts: [{ type: "plain", text: "This is a message" }], replyEventId: mockEvent.getId(), }); }); it("correctly sends a message", () => { - mocked(doMaybeLocalRoomAction).mockImplementation(( - roomId: string, - fn: (actualRoomId: string) => Promise, - _client?: MatrixClient, - ) => { - return fn(roomId); - }); + mocked(doMaybeLocalRoomAction).mockImplementation( + (roomId: string, fn: (actualRoomId: string) => Promise, _client?: MatrixClient) => { + return fn(roomId); + }, + ); mockPlatformPeg({ overrideBrowserShortcuts: jest.fn().mockReturnValue(false) }); const { container } = getComponent(); @@ -292,14 +290,10 @@ describe('', () => { addTextToComposer(container, "test message"); fireEvent.keyDown(container.querySelector(".mx_SendMessageComposer"), { key: "Enter" }); - expect(mockClient.sendMessage).toHaveBeenCalledWith( - "myfakeroom", - null, - { - "body": "test message", - "msgtype": MsgType.Text, - }, - ); + expect(mockClient.sendMessage).toHaveBeenCalledWith("myfakeroom", null, { + body: "test message", + msgtype: MsgType.Text, + }); }); }); @@ -339,4 +333,3 @@ describe('', () => { }); }); }); - diff --git a/test/components/views/rooms/VoiceRecordComposerTile-test.tsx b/test/components/views/rooms/VoiceRecordComposerTile-test.tsx index eb9f72d783..e9f7615cca 100644 --- a/test/components/views/rooms/VoiceRecordComposerTile-test.tsx +++ b/test/components/views/rooms/VoiceRecordComposerTile-test.tsx @@ -65,13 +65,11 @@ describe("", () => { recorder: mockRecorder, }); - mocked(doMaybeLocalRoomAction).mockImplementation(( - roomId: string, - fn: (actualRoomId: string) => Promise, - _client?: MatrixClient, - ) => { - return fn(roomId); - }); + mocked(doMaybeLocalRoomAction).mockImplementation( + (roomId: string, fn: (actualRoomId: string) => Promise, _client?: MatrixClient) => { + return fn(roomId); + }, + ); }); describe("send", () => { @@ -81,25 +79,21 @@ describe("", () => { "body": "Voice message", "file": undefined, "info": { - "duration": 1337000, - "mimetype": "audio/ogg", - "size": undefined, + duration: 1337000, + mimetype: "audio/ogg", + size: undefined, }, "msgtype": MsgType.Audio, "org.matrix.msc1767.audio": { - "duration": 1337000, - "waveform": [ - 1434, - 2560, - 3686, - ], + duration: 1337000, + waveform: [1434, 2560, 3686], }, "org.matrix.msc1767.file": { - "file": undefined, - "mimetype": "audio/ogg", - "name": "Voice message.ogg", - "size": undefined, - "url": "mxc://example.com/voice", + file: undefined, + mimetype: "audio/ogg", + name: "Voice message.ogg", + size: undefined, + url: "mxc://example.com/voice", }, "org.matrix.msc1767.text": "Voice message", "org.matrix.msc3245.voice": {}, diff --git a/test/components/views/rooms/wysiwyg_composer/EditWysiwygComposer-test.tsx b/test/components/views/rooms/wysiwyg_composer/EditWysiwygComposer-test.tsx index d177561f05..cc53c88dc0 100644 --- a/test/components/views/rooms/wysiwyg_composer/EditWysiwygComposer-test.tsx +++ b/test/components/views/rooms/wysiwyg_composer/EditWysiwygComposer-test.tsx @@ -24,11 +24,10 @@ import defaultDispatcher from "../../../../../src/dispatcher/dispatcher"; import { Action } from "../../../../../src/dispatcher/actions"; import { IRoomState } from "../../../../../src/components/structures/RoomView"; import { createTestClient, flushPromises, getRoomContext, mkEvent, mkStubRoom } from "../../../../test-utils"; -import { EditWysiwygComposer } - from "../../../../../src/components/views/rooms/wysiwyg_composer"; +import { EditWysiwygComposer } from "../../../../../src/components/views/rooms/wysiwyg_composer"; import EditorStateTransfer from "../../../../../src/utils/EditorStateTransfer"; -describe('EditWysiwygComposer', () => { +describe("EditWysiwygComposer", () => { afterEach(() => { jest.resetAllMocks(); }); @@ -36,18 +35,18 @@ describe('EditWysiwygComposer', () => { const mockClient = createTestClient(); const mockEvent = mkEvent({ type: "m.room.message", - room: 'myfakeroom', - user: 'myfakeuser', + room: "myfakeroom", + user: "myfakeuser", content: { - "msgtype": "m.text", - "body": "Replying to this", - "format": "org.matrix.custom.html", - "formatted_body": 'Replying to this new content', + msgtype: "m.text", + body: "Replying to this", + format: "org.matrix.custom.html", + formatted_body: "Replying to this new content", }, event: true, }); - const mockRoom = mkStubRoom('myfakeroom', 'myfakeroom', mockClient) as any; - mockRoom.findEventById = jest.fn(eventId => { + const mockRoom = mkStubRoom("myfakeroom", "myfakeroom", mockClient) as any; + mockRoom.findEventById = jest.fn((eventId) => { return eventId === mockEvent.getId() ? mockEvent : null; }); @@ -65,48 +64,48 @@ describe('EditWysiwygComposer', () => { ); }; - describe('Initialize with content', () => { - it('Should initialize useWysiwyg with html content', async () => { + describe("Initialize with content", () => { + it("Should initialize useWysiwyg with html content", async () => { // When customRender(false, editorStateTransfer); - await waitFor(() => expect(screen.getByRole('textbox')).toHaveAttribute('contentEditable', "true")); + await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true")); // Then await waitFor(() => - expect(screen.getByRole('textbox')).toContainHTML(mockEvent.getContent()['formatted_body'])); + expect(screen.getByRole("textbox")).toContainHTML(mockEvent.getContent()["formatted_body"]), + ); }); - it('Should initialize useWysiwyg with plain text content', async () => { + it("Should initialize useWysiwyg with plain text content", async () => { // When const mockEvent = mkEvent({ type: "m.room.message", - room: 'myfakeroom', - user: 'myfakeuser', + room: "myfakeroom", + user: "myfakeuser", content: { - "msgtype": "m.text", - "body": "Replying to this", + msgtype: "m.text", + body: "Replying to this", }, event: true, }); const editorStateTransfer = new EditorStateTransfer(mockEvent); customRender(false, editorStateTransfer); - await waitFor(() => expect(screen.getByRole('textbox')).toHaveAttribute('contentEditable', "true")); + await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true")); // Then - await waitFor(() => - expect(screen.getByRole('textbox')).toContainHTML(mockEvent.getContent()['body'])); + await waitFor(() => expect(screen.getByRole("textbox")).toContainHTML(mockEvent.getContent()["body"])); }); - it('Should ignore when formatted_body is not filled', async () => { + it("Should ignore when formatted_body is not filled", async () => { // When const mockEvent = mkEvent({ type: "m.room.message", - room: 'myfakeroom', - user: 'myfakeuser', + room: "myfakeroom", + user: "myfakeuser", content: { - "msgtype": "m.text", - "body": "Replying to this", - "format": "org.matrix.custom.html", + msgtype: "m.text", + body: "Replying to this", + format: "org.matrix.custom.html", }, event: true, }); @@ -115,40 +114,40 @@ describe('EditWysiwygComposer', () => { customRender(false, editorStateTransfer); // Then - await waitFor(() => expect(screen.getByRole('textbox')).toHaveAttribute('contentEditable', "true")); + await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true")); }); - it('Should strip tag from initial content', async () => { + it("Should strip tag from initial content", async () => { // When const mockEvent = mkEvent({ type: "m.room.message", - room: 'myfakeroom', - user: 'myfakeuser', + room: "myfakeroom", + user: "myfakeuser", content: { - "msgtype": "m.text", - "body": "Replying to this", - "format": "org.matrix.custom.html", - "formatted_body": 'ReplyMy content', + msgtype: "m.text", + body: "Replying to this", + format: "org.matrix.custom.html", + formatted_body: "ReplyMy content", }, event: true, }); const editorStateTransfer = new EditorStateTransfer(mockEvent); customRender(false, editorStateTransfer); - await waitFor(() => expect(screen.getByRole('textbox')).toHaveAttribute('contentEditable', "true")); + await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true")); // Then await waitFor(() => { - expect(screen.getByRole('textbox')).not.toContainHTML("Reply"); - expect(screen.getByRole('textbox')).toContainHTML("My content"); + expect(screen.getByRole("textbox")).not.toContainHTML("Reply"); + expect(screen.getByRole("textbox")).toContainHTML("My content"); }); }); }); - describe('Edit and save actions', () => { + describe("Edit and save actions", () => { beforeEach(async () => { customRender(); - await waitFor(() => expect(screen.getByRole('textbox')).toHaveAttribute('contentEditable', "true")); + await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true")); }); const spyDispatcher = jest.spyOn(defaultDispatcher, "dispatch"); @@ -156,9 +155,9 @@ describe('EditWysiwygComposer', () => { spyDispatcher.mockRestore(); }); - it('Should cancel edit on cancel button click', async () => { + it("Should cancel edit on cancel button click", async () => { // When - screen.getByText('Cancel').click(); + screen.getByText("Cancel").click(); // Then expect(spyDispatcher).toBeCalledWith({ @@ -172,43 +171,43 @@ describe('EditWysiwygComposer', () => { }); }); - it('Should send message on save button click', async () => { + it("Should send message on save button click", async () => { // When const spyDispatcher = jest.spyOn(defaultDispatcher, "dispatch"); - fireEvent.input(screen.getByRole('textbox'), { - data: 'foo bar', - inputType: 'insertText', + fireEvent.input(screen.getByRole("textbox"), { + data: "foo bar", + inputType: "insertText", }); - await waitFor(() => expect(screen.getByText('Save')).not.toHaveAttribute('disabled')); + await waitFor(() => expect(screen.getByText("Save")).not.toHaveAttribute("disabled")); // Then - screen.getByText('Save').click(); + screen.getByText("Save").click(); const expectedContent = { "body": ` * foo bar`, "format": "org.matrix.custom.html", "formatted_body": ` * foo bar`, "m.new_content": { - "body": "foo bar", - "format": "org.matrix.custom.html", - "formatted_body": "foo bar", - "msgtype": "m.text", + body: "foo bar", + format: "org.matrix.custom.html", + formatted_body: "foo bar", + msgtype: "m.text", }, "m.relates_to": { - "event_id": mockEvent.getId(), - "rel_type": "m.replace", + event_id: mockEvent.getId(), + rel_type: "m.replace", }, "msgtype": "m.text", }; expect(mockClient.sendMessage).toBeCalledWith(mockEvent.getRoomId(), null, expectedContent); - expect(spyDispatcher).toBeCalledWith({ action: 'message_sent' }); + expect(spyDispatcher).toBeCalledWith({ action: "message_sent" }); }); }); - it('Should focus when receiving an Action.FocusEditMessageComposer action', async () => { + it("Should focus when receiving an Action.FocusEditMessageComposer action", async () => { // Given we don't have focus customRender(); - screen.getByLabelText('Bold').focus(); - expect(screen.getByRole('textbox')).not.toHaveFocus(); + screen.getByLabelText("Bold").focus(); + expect(screen.getByRole("textbox")).not.toHaveFocus(); // When we send the right action defaultDispatcher.dispatch({ @@ -217,14 +216,14 @@ describe('EditWysiwygComposer', () => { }); // Then the component gets the focus - await waitFor(() => expect(screen.getByRole('textbox')).toHaveFocus()); + await waitFor(() => expect(screen.getByRole("textbox")).toHaveFocus()); }); - it('Should not focus when disabled', async () => { + it("Should not focus when disabled", async () => { // Given we don't have focus and we are disabled customRender(true); - screen.getByLabelText('Bold').focus(); - expect(screen.getByRole('textbox')).not.toHaveFocus(); + screen.getByLabelText("Bold").focus(); + expect(screen.getByRole("textbox")).not.toHaveFocus(); // When we send an action that would cause us to get focus act(() => { @@ -243,7 +242,6 @@ describe('EditWysiwygComposer', () => { await flushPromises(); // Then we don't get it because we are disabled - expect(screen.getByRole('textbox')).not.toHaveFocus(); + expect(screen.getByRole("textbox")).not.toHaveFocus(); }); }); - diff --git a/test/components/views/rooms/wysiwyg_composer/SendWysiwygComposer-test.tsx b/test/components/views/rooms/wysiwyg_composer/SendWysiwygComposer-test.tsx index 6cb183bb0a..669c611f8c 100644 --- a/test/components/views/rooms/wysiwyg_composer/SendWysiwygComposer-test.tsx +++ b/test/components/views/rooms/wysiwyg_composer/SendWysiwygComposer-test.tsx @@ -30,12 +30,16 @@ import { ComposerInsertPayload, ComposerType } from "../../../../../src/dispatch import { setSelection } from "../../../../../src/components/views/rooms/wysiwyg_composer/utils/selection"; jest.mock("../../../../../src/components/views/rooms/EmojiButton", () => ({ - EmojiButton: ({ addEmoji }: {addEmoji: (emoji: string) => void}) => { - return ; + EmojiButton: ({ addEmoji }: { addEmoji: (emoji: string) => void }) => { + return ( + + ); }, })); -describe('SendWysiwygComposer', () => { +describe("SendWysiwygComposer", () => { afterEach(() => { jest.resetAllMocks(); }); @@ -43,13 +47,13 @@ describe('SendWysiwygComposer', () => { const mockClient = createTestClient(); const mockEvent = mkEvent({ type: "m.room.message", - room: 'myfakeroom', - user: 'myfakeuser', - content: { "msgtype": "m.text", "body": "Replying to this" }, + room: "myfakeroom", + user: "myfakeuser", + content: { msgtype: "m.text", body: "Replying to this" }, event: true, }); - const mockRoom = mkStubRoom('myfakeroom', 'myfakeroom', mockClient) as any; - mockRoom.findEventById = jest.fn(eventId => { + const mockRoom = mkStubRoom("myfakeroom", "myfakeroom", mockClient) as any; + mockRoom.findEventById = jest.fn((eventId) => { return eventId === mockEvent.getId() ? mockEvent : null; }); @@ -79,46 +83,54 @@ describe('SendWysiwygComposer', () => { onSend = (): void => void 0, disabled = false, isRichTextEnabled = true, - placeholder?: string) => { + placeholder?: string, + ) => { return render( - + , ); }; - it('Should render WysiwygComposer when isRichTextEnabled is at true', () => { + it("Should render WysiwygComposer when isRichTextEnabled is at true", () => { // When customRender(jest.fn(), jest.fn(), false, true); // Then - expect(screen.getByTestId('WysiwygComposer')).toBeTruthy(); + expect(screen.getByTestId("WysiwygComposer")).toBeTruthy(); }); - it('Should render PlainTextComposer when isRichTextEnabled is at false', () => { + it("Should render PlainTextComposer when isRichTextEnabled is at false", () => { // When customRender(jest.fn(), jest.fn(), false, false); // Then - expect(screen.getByTestId('PlainTextComposer')).toBeTruthy(); + expect(screen.getByTestId("PlainTextComposer")).toBeTruthy(); }); describe.each([ - { isRichTextEnabled: true, emptyContent: '
' }, - { isRichTextEnabled: false, emptyContent: '' }, + { isRichTextEnabled: true, emptyContent: "
" }, + { isRichTextEnabled: false, emptyContent: "" }, ])( - 'Should focus when receiving an Action.FocusSendMessageComposer action', + "Should focus when receiving an Action.FocusSendMessageComposer action", ({ isRichTextEnabled, emptyContent }) => { afterEach(() => { jest.resetAllMocks(); }); - it('Should focus when receiving an Action.FocusSendMessageComposer action', async () => { + it("Should focus when receiving an Action.FocusSendMessageComposer action", async () => { // Given we don't have focus customRender(jest.fn(), jest.fn(), false, isRichTextEnabled); - await waitFor(() => expect(screen.getByRole('textbox')).toHaveAttribute('contentEditable', "true")); + await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true")); // When we send the right action defaultDispatcher.dispatch({ @@ -127,18 +139,18 @@ describe('SendWysiwygComposer', () => { }); // Then the component gets the focus - await waitFor(() => expect(screen.getByRole('textbox')).toHaveFocus()); + await waitFor(() => expect(screen.getByRole("textbox")).toHaveFocus()); }); - it('Should focus and clear when receiving an Action.ClearAndFocusSendMessageComposer', async () => { + it("Should focus and clear when receiving an Action.ClearAndFocusSendMessageComposer", async () => { // Given we don't have focus const onChange = jest.fn(); customRender(onChange, jest.fn(), false, isRichTextEnabled); - await waitFor(() => expect(screen.getByRole('textbox')).toHaveAttribute('contentEditable', "true")); + await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true")); - fireEvent.input(screen.getByRole('textbox'), { - data: 'foo bar', - inputType: 'insertText', + fireEvent.input(screen.getByRole("textbox"), { + data: "foo bar", + inputType: "insertText", }); // When we send the right action @@ -149,15 +161,15 @@ describe('SendWysiwygComposer', () => { // Then the component gets the focus await waitFor(() => { - expect(screen.getByRole('textbox')).toHaveTextContent(/^$/); - expect(screen.getByRole('textbox')).toHaveFocus(); + expect(screen.getByRole("textbox")).toHaveTextContent(/^$/); + expect(screen.getByRole("textbox")).toHaveFocus(); }); }); - it('Should focus when receiving a reply_to_event action', async () => { + it("Should focus when receiving a reply_to_event action", async () => { // Given we don't have focus customRender(jest.fn(), jest.fn(), false, isRichTextEnabled); - await waitFor(() => expect(screen.getByRole('textbox')).toHaveAttribute('contentEditable', "true")); + await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true")); // When we send the right action defaultDispatcher.dispatch({ @@ -166,13 +178,13 @@ describe('SendWysiwygComposer', () => { }); // Then the component gets the focus - await waitFor(() => expect(screen.getByRole('textbox')).toHaveFocus()); + await waitFor(() => expect(screen.getByRole("textbox")).toHaveFocus()); }); - it('Should not focus when disabled', async () => { + it("Should not focus when disabled", async () => { // Given we don't have focus and we are disabled customRender(jest.fn(), jest.fn(), true, isRichTextEnabled); - expect(screen.getByRole('textbox')).not.toHaveFocus(); + expect(screen.getByRole("textbox")).not.toHaveFocus(); // When we send an action that would cause us to get focus defaultDispatcher.dispatch({ @@ -189,114 +201,114 @@ describe('SendWysiwygComposer', () => { await flushPromises(); // Then we don't get it because we are disabled - expect(screen.getByRole('textbox')).not.toHaveFocus(); + expect(screen.getByRole("textbox")).not.toHaveFocus(); }); - }); + }, + ); - describe.each([ - { isRichTextEnabled: true }, - { isRichTextEnabled: false }, - ])('Placeholder when %s', + describe.each([{ isRichTextEnabled: true }, { isRichTextEnabled: false }])( + "Placeholder when %s", ({ isRichTextEnabled }) => { afterEach(() => { jest.resetAllMocks(); }); - it('Should not has placeholder', async () => { + it("Should not has placeholder", async () => { // When customRender(jest.fn(), jest.fn(), false, isRichTextEnabled); - await waitFor(() => expect(screen.getByRole('textbox')).toHaveAttribute('contentEditable', "true")); + await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true")); // Then - expect(screen.getByRole('textbox')).not.toHaveClass("mx_WysiwygComposer_Editor_content_placeholder"); + expect(screen.getByRole("textbox")).not.toHaveClass("mx_WysiwygComposer_Editor_content_placeholder"); }); - it('Should has placeholder', async () => { + it("Should has placeholder", async () => { // When - customRender(jest.fn(), jest.fn(), false, isRichTextEnabled, 'my placeholder'); - await waitFor(() => expect(screen.getByRole('textbox')).toHaveAttribute('contentEditable', "true")); + customRender(jest.fn(), jest.fn(), false, isRichTextEnabled, "my placeholder"); + await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true")); // Then - expect(screen.getByRole('textbox')).toHaveClass("mx_WysiwygComposer_Editor_content_placeholder"); + expect(screen.getByRole("textbox")).toHaveClass("mx_WysiwygComposer_Editor_content_placeholder"); }); - it('Should display or not placeholder when editor content change', async () => { + it("Should display or not placeholder when editor content change", async () => { // When - customRender(jest.fn(), jest.fn(), false, isRichTextEnabled, 'my placeholder'); - await waitFor(() => expect(screen.getByRole('textbox')).toHaveAttribute('contentEditable', "true")); - screen.getByRole('textbox').innerHTML = 'f'; - fireEvent.input(screen.getByRole('textbox'), { - data: 'f', - inputType: 'insertText', + customRender(jest.fn(), jest.fn(), false, isRichTextEnabled, "my placeholder"); + await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true")); + screen.getByRole("textbox").innerHTML = "f"; + fireEvent.input(screen.getByRole("textbox"), { + data: "f", + inputType: "insertText", }); // Then await waitFor(() => - expect(screen.getByRole('textbox')) - .not.toHaveClass("mx_WysiwygComposer_Editor_content_placeholder"), + expect(screen.getByRole("textbox")).not.toHaveClass( + "mx_WysiwygComposer_Editor_content_placeholder", + ), ); // When - screen.getByRole('textbox').innerHTML = ''; - fireEvent.input(screen.getByRole('textbox'), { - inputType: 'deleteContentBackward', + screen.getByRole("textbox").innerHTML = ""; + fireEvent.input(screen.getByRole("textbox"), { + inputType: "deleteContentBackward", }); // Then await waitFor(() => - expect(screen.getByRole('textbox')).toHaveClass("mx_WysiwygComposer_Editor_content_placeholder"), + expect(screen.getByRole("textbox")).toHaveClass("mx_WysiwygComposer_Editor_content_placeholder"), ); }); - }); + }, + ); - describe.each([ - { isRichTextEnabled: true }, - { isRichTextEnabled: false }, - ])('Emoji when %s', ({ isRichTextEnabled }) => { - let emojiButton: HTMLElement; + describe.each([{ isRichTextEnabled: true }, { isRichTextEnabled: false }])( + "Emoji when %s", + ({ isRichTextEnabled }) => { + let emojiButton: HTMLElement; - beforeEach(async () => { - customRender(jest.fn(), jest.fn(), false, isRichTextEnabled); - await waitFor(() => expect(screen.getByRole('textbox')).toHaveAttribute('contentEditable', "true")); - emojiButton = screen.getByLabelText('Emoji'); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - it('Should add an emoji in an empty composer', async () => { - // When - emojiButton.click(); - - // Then - await waitFor(() => expect(screen.getByRole('textbox')).toHaveTextContent(/🦫/)); - }); - - it('Should add an emoji in the middle of a word', async () => { - // When - screen.getByRole('textbox').focus(); - screen.getByRole('textbox').innerHTML = 'word'; - fireEvent.input(screen.getByRole('textbox'), { - data: 'word', - inputType: 'insertText', + beforeEach(async () => { + customRender(jest.fn(), jest.fn(), false, isRichTextEnabled); + await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true")); + emojiButton = screen.getByLabelText("Emoji"); }); - const textNode = screen.getByRole('textbox').firstChild; - setSelection({ - anchorNode: textNode, - anchorOffset: 2, - focusNode: textNode, - focusOffset: 2, + afterEach(() => { + jest.resetAllMocks(); }); - // the event is not automatically fired by jest - document.dispatchEvent(new CustomEvent('selectionchange')); - emojiButton.click(); + it("Should add an emoji in an empty composer", async () => { + // When + emojiButton.click(); - // Then - await waitFor(() => expect(screen.getByRole('textbox')).toHaveTextContent(/wo🦫rd/)); - }); - }); + // Then + await waitFor(() => expect(screen.getByRole("textbox")).toHaveTextContent(/🦫/)); + }); + + it("Should add an emoji in the middle of a word", async () => { + // When + screen.getByRole("textbox").focus(); + screen.getByRole("textbox").innerHTML = "word"; + fireEvent.input(screen.getByRole("textbox"), { + data: "word", + inputType: "insertText", + }); + + const textNode = screen.getByRole("textbox").firstChild; + setSelection({ + anchorNode: textNode, + anchorOffset: 2, + focusNode: textNode, + focusOffset: 2, + }); + // the event is not automatically fired by jest + document.dispatchEvent(new CustomEvent("selectionchange")); + + emojiButton.click(); + + // Then + await waitFor(() => expect(screen.getByRole("textbox")).toHaveTextContent(/wo🦫rd/)); + }); + }, + ); }); - diff --git a/test/components/views/rooms/wysiwyg_composer/components/FormattingButtons-test.tsx b/test/components/views/rooms/wysiwyg_composer/components/FormattingButtons-test.tsx index 1c3dab6874..d143e43a62 100644 --- a/test/components/views/rooms/wysiwyg_composer/components/FormattingButtons-test.tsx +++ b/test/components/views/rooms/wysiwyg_composer/components/FormattingButtons-test.tsx @@ -14,15 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; import { render, screen } from "@testing-library/react"; -import userEvent from '@testing-library/user-event'; -import { AllActionStates, FormattingFunctions } from '@matrix-org/matrix-wysiwyg'; +import userEvent from "@testing-library/user-event"; +import { AllActionStates, FormattingFunctions } from "@matrix-org/matrix-wysiwyg"; -import { FormattingButtons } - from "../../../../../../src/components/views/rooms/wysiwyg_composer/components/FormattingButtons"; +import { FormattingButtons } from "../../../../../../src/components/views/rooms/wysiwyg_composer/components/FormattingButtons"; -describe('FormattingButtons', () => { +describe("FormattingButtons", () => { const wysiwyg = { bold: jest.fn(), italic: jest.fn(), @@ -32,37 +31,37 @@ describe('FormattingButtons', () => { } as unknown as FormattingFunctions; const actionStates = { - bold: 'reversed', - italic: 'reversed', - underline: 'enabled', - strikeThrough: 'enabled', - inlineCode: 'enabled', + bold: "reversed", + italic: "reversed", + underline: "enabled", + strikeThrough: "enabled", + inlineCode: "enabled", } as AllActionStates; afterEach(() => { jest.resetAllMocks(); }); - it('Should have the correspond CSS classes', () => { + it("Should have the correspond CSS classes", () => { // When render(); // Then - expect(screen.getByLabelText('Bold')).toHaveClass('mx_FormattingButtons_active'); - expect(screen.getByLabelText('Italic')).toHaveClass('mx_FormattingButtons_active'); - expect(screen.getByLabelText('Underline')).not.toHaveClass('mx_FormattingButtons_active'); - expect(screen.getByLabelText('Strikethrough')).not.toHaveClass('mx_FormattingButtons_active'); - expect(screen.getByLabelText('Code')).not.toHaveClass('mx_FormattingButtons_active'); + expect(screen.getByLabelText("Bold")).toHaveClass("mx_FormattingButtons_active"); + expect(screen.getByLabelText("Italic")).toHaveClass("mx_FormattingButtons_active"); + expect(screen.getByLabelText("Underline")).not.toHaveClass("mx_FormattingButtons_active"); + expect(screen.getByLabelText("Strikethrough")).not.toHaveClass("mx_FormattingButtons_active"); + expect(screen.getByLabelText("Code")).not.toHaveClass("mx_FormattingButtons_active"); }); - it('Should call wysiwyg function on button click', () => { + it("Should call wysiwyg function on button click", () => { // When render(); - screen.getByLabelText('Bold').click(); - screen.getByLabelText('Italic').click(); - screen.getByLabelText('Underline').click(); - screen.getByLabelText('Strikethrough').click(); - screen.getByLabelText('Code').click(); + screen.getByLabelText("Bold").click(); + screen.getByLabelText("Italic").click(); + screen.getByLabelText("Underline").click(); + screen.getByLabelText("Strikethrough").click(); + screen.getByLabelText("Code").click(); // Then expect(wysiwyg.bold).toHaveBeenCalledTimes(1); @@ -72,29 +71,29 @@ describe('FormattingButtons', () => { expect(wysiwyg.inlineCode).toHaveBeenCalledTimes(1); }); - it('Should display the tooltip on mouse over', async () => { + it("Should display the tooltip on mouse over", async () => { // When const user = userEvent.setup(); render(); - await user.hover(screen.getByLabelText('Bold')); + await user.hover(screen.getByLabelText("Bold")); // Then - expect(await screen.findByText('Bold')).toBeTruthy(); + expect(await screen.findByText("Bold")).toBeTruthy(); }); - it('Should not have hover style when active', async () => { + it("Should not have hover style when active", async () => { // When const user = userEvent.setup(); render(); - await user.hover(screen.getByLabelText('Bold')); + await user.hover(screen.getByLabelText("Bold")); // Then - expect(screen.getByLabelText('Bold')).not.toHaveClass('mx_FormattingButtons_Button_hover'); + expect(screen.getByLabelText("Bold")).not.toHaveClass("mx_FormattingButtons_Button_hover"); // When - await user.hover(screen.getByLabelText('Underline')); + await user.hover(screen.getByLabelText("Underline")); // Then - expect(screen.getByLabelText('Underline')).toHaveClass('mx_FormattingButtons_Button_hover'); + expect(screen.getByLabelText("Underline")).toHaveClass("mx_FormattingButtons_Button_hover"); }); }); diff --git a/test/components/views/rooms/wysiwyg_composer/components/PlainTextComposer-test.tsx b/test/components/views/rooms/wysiwyg_composer/components/PlainTextComposer-test.tsx index 9c2e10100f..bb41b18dc8 100644 --- a/test/components/views/rooms/wysiwyg_composer/components/PlainTextComposer-test.tsx +++ b/test/components/views/rooms/wysiwyg_composer/components/PlainTextComposer-test.tsx @@ -14,84 +14,89 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; -import { PlainTextComposer } - from "../../../../../../src/components/views/rooms/wysiwyg_composer/components/PlainTextComposer"; +import { PlainTextComposer } from "../../../../../../src/components/views/rooms/wysiwyg_composer/components/PlainTextComposer"; -describe('PlainTextComposer', () => { +describe("PlainTextComposer", () => { const customRender = ( onChange = (_content: string) => void 0, onSend = () => void 0, disabled = false, - initialContent?: string) => { + initialContent?: string, + ) => { return render( - , + , ); }; - it('Should have contentEditable at false when disabled', () => { + it("Should have contentEditable at false when disabled", () => { // When customRender(jest.fn(), jest.fn(), true); // Then - expect(screen.getByRole('textbox')).toHaveAttribute('contentEditable', "false"); + expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "false"); }); - it('Should have focus', () => { + it("Should have focus", () => { // When customRender(jest.fn(), jest.fn(), false); // Then - expect(screen.getByRole('textbox')).toHaveFocus(); + expect(screen.getByRole("textbox")).toHaveFocus(); }); - it('Should call onChange handler', async () => { + it("Should call onChange handler", async () => { // When - const content = 'content'; + const content = "content"; const onChange = jest.fn(); customRender(onChange, jest.fn()); - await userEvent.type(screen.getByRole('textbox'), content); + await userEvent.type(screen.getByRole("textbox"), content); // Then expect(onChange).toBeCalledWith(content); }); - it('Should call onSend when Enter is pressed', async () => { + it("Should call onSend when Enter is pressed", async () => { //When const onSend = jest.fn(); customRender(jest.fn(), onSend); - await userEvent.type(screen.getByRole('textbox'), '{enter}'); + await userEvent.type(screen.getByRole("textbox"), "{enter}"); // Then it sends a message expect(onSend).toBeCalledTimes(1); }); - it('Should clear textbox content when clear is called', async () => { + it("Should clear textbox content when clear is called", async () => { //When let composer; render( - { (ref, composerFunctions) => { + {(ref, composerFunctions) => { composer = composerFunctions; return null; - } } + }} , ); - await userEvent.type(screen.getByRole('textbox'), 'content'); - expect(screen.getByRole('textbox').innerHTML).toBe('content'); + await userEvent.type(screen.getByRole("textbox"), "content"); + expect(screen.getByRole("textbox").innerHTML).toBe("content"); composer.clear(); // Then - expect(screen.getByRole('textbox').innerHTML).toBeFalsy(); + expect(screen.getByRole("textbox").innerHTML).toBeFalsy(); }); - it('Should have data-is-expanded when it has two lines', async () => { + it("Should have data-is-expanded when it has two lines", async () => { let resizeHandler: ResizeObserverCallback = jest.fn(); let editor: Element | null = null; - jest.spyOn(global, 'ResizeObserver').mockImplementation((handler) => { + jest.spyOn(global, "ResizeObserver").mockImplementation((handler) => { resizeHandler = handler; return { observe: (element) => { @@ -100,21 +105,18 @@ describe('PlainTextComposer', () => { unobserve: jest.fn(), disconnect: jest.fn(), }; - }, - ); - jest.spyOn(global, 'requestAnimationFrame').mockImplementation(cb => { + }); + jest.spyOn(global, "requestAnimationFrame").mockImplementation((cb) => { cb(0); return 0; }); //When - render( - , - ); + render(); // Then - expect(screen.getByTestId('WysiwygComposerEditor').attributes['data-is-expanded'].value).toBe('false'); - expect(editor).toBe(screen.getByRole('textbox')); + expect(screen.getByTestId("WysiwygComposerEditor").attributes["data-is-expanded"].value).toBe("false"); + expect(editor).toBe(screen.getByRole("textbox")); // When resizeHandler( @@ -124,7 +126,7 @@ describe('PlainTextComposer', () => { jest.runAllTimers(); // Then - expect(screen.getByTestId('WysiwygComposerEditor').attributes['data-is-expanded'].value).toBe('true'); + expect(screen.getByTestId("WysiwygComposerEditor").attributes["data-is-expanded"].value).toBe("true"); (global.ResizeObserver as jest.Mock).mockRestore(); (global.requestAnimationFrame as jest.Mock).mockRestore(); diff --git a/test/components/views/rooms/wysiwyg_composer/components/WysiwygComposer-test.tsx b/test/components/views/rooms/wysiwyg_composer/components/WysiwygComposer-test.tsx index 7dad006dcc..43dce76c7f 100644 --- a/test/components/views/rooms/wysiwyg_composer/components/WysiwygComposer-test.tsx +++ b/test/components/views/rooms/wysiwyg_composer/components/WysiwygComposer-test.tsx @@ -18,35 +18,35 @@ import "@testing-library/jest-dom"; import React from "react"; import { fireEvent, render, screen, waitFor } from "@testing-library/react"; -import { WysiwygComposer } - from "../../../../../../src/components/views/rooms/wysiwyg_composer/components/WysiwygComposer"; +import { WysiwygComposer } from "../../../../../../src/components/views/rooms/wysiwyg_composer/components/WysiwygComposer"; import SettingsStore from "../../../../../../src/settings/SettingsStore"; -describe('WysiwygComposer', () => { +describe("WysiwygComposer", () => { const customRender = ( onChange = (_content: string) => void 0, onSend = () => void 0, disabled = false, - initialContent?: string) => { + initialContent?: string, + ) => { return render( , ); }; - it('Should have contentEditable at false when disabled', () => { + it("Should have contentEditable at false when disabled", () => { // When customRender(jest.fn(), jest.fn(), true); // Then - expect(screen.getByRole('textbox')).toHaveAttribute('contentEditable', "false"); + expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "false"); }); - describe('Standard behavior', () => { + describe("Standard behavior", () => { const onChange = jest.fn(); const onSend = jest.fn(); beforeEach(async () => { customRender(onChange, onSend); - await waitFor(() => expect(screen.getByRole('textbox')).toHaveAttribute('contentEditable', "true")); + await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true")); }); afterEach(() => { @@ -54,39 +54,42 @@ describe('WysiwygComposer', () => { onSend.mockReset(); }); - it('Should have contentEditable at true', async () => { + it("Should have contentEditable at true", async () => { // Then - await waitFor(() => expect(screen.getByRole('textbox')).toHaveAttribute('contentEditable', "true")); + await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true")); }); - it('Should have focus', async () => { + it("Should have focus", async () => { // Then - await waitFor(() => expect(screen.getByRole('textbox')).toHaveFocus()); + await waitFor(() => expect(screen.getByRole("textbox")).toHaveFocus()); }); - it('Should call onChange handler', async () => { + it("Should call onChange handler", async () => { // When - fireEvent.input(screen.getByRole('textbox'), { - data: 'foo bar', - inputType: 'insertText', + fireEvent.input(screen.getByRole("textbox"), { + data: "foo bar", + inputType: "insertText", }); // Then - await waitFor(() => expect(onChange).toBeCalledWith('foo bar')); + await waitFor(() => expect(onChange).toBeCalledWith("foo bar")); }); - it('Should call onSend when Enter is pressed ', async () => { - //When - fireEvent(screen.getByRole('textbox'), new InputEvent('input', { - inputType: "insertParagraph", - })); + it("Should call onSend when Enter is pressed ", async () => { + //When + fireEvent( + screen.getByRole("textbox"), + new InputEvent("input", { + inputType: "insertParagraph", + }), + ); // Then it sends a message await waitFor(() => expect(onSend).toBeCalledTimes(1)); }); }); - describe('When settings require Ctrl+Enter to send', () => { + describe("When settings require Ctrl+Enter to send", () => { const onChange = jest.fn(); const onSend = jest.fn(); beforeEach(async () => { @@ -94,7 +97,7 @@ describe('WysiwygComposer', () => { if (name === "MessageComposerInput.ctrlEnterToSend") return true; }); customRender(onChange, onSend); - await waitFor(() => expect(screen.getByRole('textbox')).toHaveAttribute('contentEditable', "true")); + await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true")); }); afterEach(() => { @@ -102,25 +105,30 @@ describe('WysiwygComposer', () => { onSend.mockReset(); }); - it('Should not call onSend when Enter is pressed', async () => { + it("Should not call onSend when Enter is pressed", async () => { // When - fireEvent(screen.getByRole('textbox'), new InputEvent('input', { - inputType: "insertParagraph", - })); + fireEvent( + screen.getByRole("textbox"), + new InputEvent("input", { + inputType: "insertParagraph", + }), + ); // Then it does not send a message await waitFor(() => expect(onSend).toBeCalledTimes(0)); }); - it('Should send a message when Ctrl+Enter is pressed', async () => { + it("Should send a message when Ctrl+Enter is pressed", async () => { // When - fireEvent(screen.getByRole('textbox'), new InputEvent('input', { - inputType: "sendMessage", - })); + fireEvent( + screen.getByRole("textbox"), + new InputEvent("input", { + inputType: "sendMessage", + }), + ); // Then it sends a message await waitFor(() => expect(onSend).toBeCalledTimes(1)); }); }); }); - diff --git a/test/components/views/rooms/wysiwyg_composer/utils/createMessageContent-test.ts b/test/components/views/rooms/wysiwyg_composer/utils/createMessageContent-test.ts index 4c7028749c..e654186617 100644 --- a/test/components/views/rooms/wysiwyg_composer/utils/createMessageContent-test.ts +++ b/test/components/views/rooms/wysiwyg_composer/utils/createMessageContent-test.ts @@ -16,21 +16,20 @@ limitations under the License. import { mkEvent } from "../../../../../test-utils"; import { RoomPermalinkCreator } from "../../../../../../src/utils/permalinks/Permalinks"; -import { createMessageContent } - from "../../../../../../src/components/views/rooms/wysiwyg_composer/utils/createMessageContent"; +import { createMessageContent } from "../../../../../../src/components/views/rooms/wysiwyg_composer/utils/createMessageContent"; -describe('createMessageContent', () => { +describe("createMessageContent", () => { const permalinkCreator = { forEvent(eventId: string): string { return "$$permalink$$"; }, } as RoomPermalinkCreator; - const message = 'hello world'; + const message = "hello world"; const mockEvent = mkEvent({ type: "m.room.message", - room: 'myfakeroom', - user: 'myfakeuser', - content: { "msgtype": "m.text", "body": "Replying to this" }, + room: "myfakeroom", + user: "myfakeuser", + content: { msgtype: "m.text", body: "Replying to this" }, event: true, }); @@ -44,14 +43,14 @@ describe('createMessageContent', () => { // Then expect(content).toEqual({ - "body": "hello world", - "format": "org.matrix.custom.html", - "formatted_body": message, - "msgtype": "m.text", + body: "hello world", + format: "org.matrix.custom.html", + formatted_body: message, + msgtype: "m.text", }); }); - it('Should add reply to message content', () => { + it("Should add reply to message content", () => { // When const content = createMessageContent(message, true, { permalinkCreator, replyToEvent: mockEvent }); @@ -59,13 +58,14 @@ describe('createMessageContent', () => { expect(content).toEqual({ "body": "> Replying to this\n\nhello world", "format": "org.matrix.custom.html", - "formatted_body": "
In reply to" + - " myfakeuser"+ - "
Replying to this
hello world", + "formatted_body": + '
In reply to' + + ' myfakeuser' + + "
Replying to this
hello world", "msgtype": "m.text", "m.relates_to": { "m.in_reply_to": { - "event_id": mockEvent.getId(), + event_id: mockEvent.getId(), }, }, }); @@ -86,31 +86,31 @@ describe('createMessageContent', () => { "formatted_body": message, "msgtype": "m.text", "m.relates_to": { - "event_id": "myFakeThreadId", - "rel_type": "m.thread", + event_id: "myFakeThreadId", + rel_type: "m.thread", }, }); }); - it('Should add fields related to edition', () => { + it("Should add fields related to edition", () => { // When const editedEvent = mkEvent({ type: "m.room.message", - room: 'myfakeroom', - user: 'myfakeuser2', + room: "myfakeroom", + user: "myfakeuser2", content: { "msgtype": "m.text", "body": "First message", "formatted_body": "First Message", "m.relates_to": { "m.in_reply_to": { - "event_id": 'eventId', + event_id: "eventId", }, - } }, + }, + }, event: true, }); - const content = - createMessageContent(message, true, { permalinkCreator, editedEvent }); + const content = createMessageContent(message, true, { permalinkCreator, editedEvent }); // Then expect(content).toEqual({ @@ -119,14 +119,14 @@ describe('createMessageContent', () => { "formatted_body": ` * ${message}`, "msgtype": "m.text", "m.new_content": { - "body": "hello world", - "format": "org.matrix.custom.html", - "formatted_body": message, - "msgtype": "m.text", + body: "hello world", + format: "org.matrix.custom.html", + formatted_body: message, + msgtype: "m.text", }, "m.relates_to": { - "event_id": editedEvent.getId(), - "rel_type": "m.replace", + event_id: editedEvent.getId(), + rel_type: "m.replace", }, }); }); diff --git a/test/components/views/rooms/wysiwyg_composer/utils/message-test.ts b/test/components/views/rooms/wysiwyg_composer/utils/message-test.ts index 0829b19adb..ceb00ade79 100644 --- a/test/components/views/rooms/wysiwyg_composer/utils/message-test.ts +++ b/test/components/views/rooms/wysiwyg_composer/utils/message-test.ts @@ -17,40 +17,38 @@ limitations under the License. import { EventStatus } from "matrix-js-sdk/src/matrix"; import { IRoomState } from "../../../../../../src/components/structures/RoomView"; -import { editMessage, sendMessage } - from "../../../../../../src/components/views/rooms/wysiwyg_composer/utils/message"; +import { editMessage, sendMessage } from "../../../../../../src/components/views/rooms/wysiwyg_composer/utils/message"; import { createTestClient, getRoomContext, mkEvent, mkStubRoom } from "../../../../../test-utils"; import defaultDispatcher from "../../../../../../src/dispatcher/dispatcher"; import SettingsStore from "../../../../../../src/settings/SettingsStore"; import { SettingLevel } from "../../../../../../src/settings/SettingLevel"; import { RoomPermalinkCreator } from "../../../../../../src/utils/permalinks/Permalinks"; import EditorStateTransfer from "../../../../../../src/utils/EditorStateTransfer"; -import * as ConfirmRedactDialog - from "../../../../../../src/components/views/dialogs/ConfirmRedactDialog"; +import * as ConfirmRedactDialog from "../../../../../../src/components/views/dialogs/ConfirmRedactDialog"; -describe('message', () => { +describe("message", () => { const permalinkCreator = { forEvent(eventId: string): string { return "$$permalink$$"; }, } as RoomPermalinkCreator; - const message = 'hello world'; + const message = "hello world"; const mockEvent = mkEvent({ type: "m.room.message", - room: 'myfakeroom', - user: 'myfakeuser', + room: "myfakeroom", + user: "myfakeuser", content: { - "msgtype": "m.text", - "body": "Replying to this", - "format": 'org.matrix.custom.html', - "formatted_body": 'Replying to this', + msgtype: "m.text", + body: "Replying to this", + format: "org.matrix.custom.html", + formatted_body: "Replying to this", }, event: true, }); const mockClient = createTestClient(); - const mockRoom = mkStubRoom('myfakeroom', 'myfakeroom', mockClient) as any; - mockRoom.findEventById = jest.fn(eventId => { + const mockRoom = mkStubRoom("myfakeroom", "myfakeroom", mockClient) as any; + mockRoom.findEventById = jest.fn((eventId) => { return eventId === mockEvent.getId() ? mockEvent : null; }); @@ -62,41 +60,41 @@ describe('message', () => { jest.resetAllMocks(); }); - describe('sendMessage', () => { - it('Should not send empty html message', async () => { + describe("sendMessage", () => { + it("Should not send empty html message", async () => { // When - await sendMessage('', true, { roomContext: defaultRoomContext, mxClient: mockClient, permalinkCreator }); + await sendMessage("", true, { roomContext: defaultRoomContext, mxClient: mockClient, permalinkCreator }); // Then expect(mockClient.sendMessage).toBeCalledTimes(0); expect(spyDispatcher).toBeCalledTimes(0); }); - it('Should send html message', async () => { + it("Should send html message", async () => { // When - await sendMessage( - message, - true, - { roomContext: defaultRoomContext, mxClient: mockClient, permalinkCreator }, - ); + await sendMessage(message, true, { + roomContext: defaultRoomContext, + mxClient: mockClient, + permalinkCreator, + }); // Then const expectedContent = { - "body": "hello world", - "format": "org.matrix.custom.html", - "formatted_body": "hello world", - "msgtype": "m.text", + body: "hello world", + format: "org.matrix.custom.html", + formatted_body: "hello world", + msgtype: "m.text", }; - expect(mockClient.sendMessage).toBeCalledWith('myfakeroom', null, expectedContent); - expect(spyDispatcher).toBeCalledWith({ action: 'message_sent' }); + expect(mockClient.sendMessage).toBeCalledWith("myfakeroom", null, expectedContent); + expect(spyDispatcher).toBeCalledWith({ action: "message_sent" }); }); - it('Should send reply to html message', async () => { + it("Should send reply to html message", async () => { const mockReplyEvent = mkEvent({ type: "m.room.message", - room: 'myfakeroom', - user: 'myfakeuser2', - content: { "msgtype": "m.text", "body": "My reply" }, + room: "myfakeroom", + user: "myfakeuser2", + content: { msgtype: "m.text", body: "My reply" }, event: true, }); @@ -110,7 +108,7 @@ describe('message', () => { // Then expect(spyDispatcher).toBeCalledWith({ - action: 'reply_to_event', + action: "reply_to_event", event: null, context: defaultRoomContext.timelineRenderingType, }); @@ -118,75 +116,71 @@ describe('message', () => { const expectedContent = { "body": "> My reply\n\nhello world", "format": "org.matrix.custom.html", - "formatted_body": "
In reply to" + - " myfakeuser2" + - "
My reply
hello world", + "formatted_body": + '
In reply to' + + ' myfakeuser2' + + "
My reply
hello world", "msgtype": "m.text", "m.relates_to": { "m.in_reply_to": { - "event_id": mockReplyEvent.getId(), + event_id: mockReplyEvent.getId(), }, }, }; - expect(mockClient.sendMessage).toBeCalledWith('myfakeroom', null, expectedContent); + expect(mockClient.sendMessage).toBeCalledWith("myfakeroom", null, expectedContent); }); - it('Should scroll to bottom after sending a html message', async () => { + it("Should scroll to bottom after sending a html message", async () => { // When SettingsStore.setValue("scrollToBottomOnMessageSent", null, SettingLevel.DEVICE, true); - await sendMessage( - message, - true, - { roomContext: defaultRoomContext, mxClient: mockClient, permalinkCreator }, - ); + await sendMessage(message, true, { + roomContext: defaultRoomContext, + mxClient: mockClient, + permalinkCreator, + }); // Then - expect(spyDispatcher).toBeCalledWith( - { action: 'scroll_to_bottom', timelineRenderingType: defaultRoomContext.timelineRenderingType }, - ); + expect(spyDispatcher).toBeCalledWith({ + action: "scroll_to_bottom", + timelineRenderingType: defaultRoomContext.timelineRenderingType, + }); }); - it('Should handle emojis', async () => { + it("Should handle emojis", async () => { // When - await sendMessage( - '🎉', - false, - { roomContext: defaultRoomContext, mxClient: mockClient, permalinkCreator }, - ); + await sendMessage("🎉", false, { roomContext: defaultRoomContext, mxClient: mockClient, permalinkCreator }); // Then - expect(spyDispatcher).toBeCalledWith( - { action: 'effects.confetti' }, - ); + expect(spyDispatcher).toBeCalledWith({ action: "effects.confetti" }); }); }); - describe('editMessage', () => { + describe("editMessage", () => { const editorStateTransfer = new EditorStateTransfer(mockEvent); - it('Should cancel editing and ask for event removal when message is empty', async () => { + it("Should cancel editing and ask for event removal when message is empty", async () => { // When - const mockCreateRedactEventDialog = jest.spyOn(ConfirmRedactDialog, 'createRedactEventDialog'); + const mockCreateRedactEventDialog = jest.spyOn(ConfirmRedactDialog, "createRedactEventDialog"); const mockEvent = mkEvent({ type: "m.room.message", - room: 'myfakeroom', - user: 'myfakeuser', - content: { "msgtype": "m.text", "body": "Replying to this" }, + room: "myfakeroom", + user: "myfakeuser", + content: { msgtype: "m.text", body: "Replying to this" }, event: true, }); const replacingEvent = mkEvent({ type: "m.room.message", - room: 'myfakeroom', - user: 'myfakeuser', - content: { "msgtype": "m.text", "body": "ReplacingEvent" }, + room: "myfakeroom", + user: "myfakeuser", + content: { msgtype: "m.text", body: "ReplacingEvent" }, event: true, }); replacingEvent.setStatus(EventStatus.QUEUED); mockEvent.makeReplaced(replacingEvent); const editorStateTransfer = new EditorStateTransfer(mockEvent); - await editMessage('', { roomContext: defaultRoomContext, mxClient: mockClient, editorStateTransfer }); + await editMessage("", { roomContext: defaultRoomContext, mxClient: mockClient, editorStateTransfer }); // Then expect(mockClient.sendMessage).toBeCalledTimes(0); @@ -195,22 +189,26 @@ describe('message', () => { expect(spyDispatcher).toBeCalledTimes(0); }); - it('Should do nothing if the content is unmodified', async () => { + it("Should do nothing if the content is unmodified", async () => { // When - await editMessage( - mockEvent.getContent().body, - { roomContext: defaultRoomContext, mxClient: mockClient, editorStateTransfer }); + await editMessage(mockEvent.getContent().body, { + roomContext: defaultRoomContext, + mxClient: mockClient, + editorStateTransfer, + }); // Then expect(mockClient.sendMessage).toBeCalledTimes(0); }); - it('Should send a message when the content is modified', async () => { + it("Should send a message when the content is modified", async () => { // When const newMessage = `${mockEvent.getContent().body} new content`; - await editMessage( - newMessage, - { roomContext: defaultRoomContext, mxClient: mockClient, editorStateTransfer }); + await editMessage(newMessage, { + roomContext: defaultRoomContext, + mxClient: mockClient, + editorStateTransfer, + }); // Then const { msgtype, format } = mockEvent.getContent(); @@ -218,20 +216,20 @@ describe('message', () => { "body": ` * ${newMessage}`, "formatted_body": ` * ${newMessage}`, "m.new_content": { - "body": "Replying to this new content", - "format": "org.matrix.custom.html", - "formatted_body": "Replying to this new content", - "msgtype": "m.text", + body: "Replying to this new content", + format: "org.matrix.custom.html", + formatted_body: "Replying to this new content", + msgtype: "m.text", }, "m.relates_to": { - "event_id": mockEvent.getId(), - "rel_type": "m.replace", + event_id: mockEvent.getId(), + rel_type: "m.replace", }, msgtype, format, }; expect(mockClient.sendMessage).toBeCalledWith(mockEvent.getRoomId(), null, expectedContent); - expect(spyDispatcher).toBeCalledWith({ action: 'message_sent' }); + expect(spyDispatcher).toBeCalledWith({ action: "message_sent" }); }); }); }); diff --git a/test/components/views/settings/AddPrivilegedUsers-test.tsx b/test/components/views/settings/AddPrivilegedUsers-test.tsx index 67258e47df..68acb7395d 100644 --- a/test/components/views/settings/AddPrivilegedUsers-test.tsx +++ b/test/components/views/settings/AddPrivilegedUsers-test.tsx @@ -13,34 +13,31 @@ 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 { act, fireEvent, render, waitFor } from '@testing-library/react'; +import React from "react"; +import { act, fireEvent, render, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { mocked } from "jest-mock"; import { RoomMember, EventType } from "matrix-js-sdk/src/matrix"; -import { - getMockClientWithEventEmitter, - makeRoomWithStateEvents, - mkEvent, -} from "../../../test-utils"; -import MatrixClientContext from '../../../../src/contexts/MatrixClientContext'; +import { getMockClientWithEventEmitter, makeRoomWithStateEvents, mkEvent } from "../../../test-utils"; +import MatrixClientContext from "../../../../src/contexts/MatrixClientContext"; import { AddPrivilegedUsers, - getUserIdsFromCompletions, hasLowerOrEqualLevelThanDefaultLevel, + getUserIdsFromCompletions, + hasLowerOrEqualLevelThanDefaultLevel, } from "../../../../src/components/views/settings/AddPrivilegedUsers"; import UserProvider from "../../../../src/autocomplete/UserProvider"; import { ICompletion } from "../../../../src/autocomplete/Autocompleter"; -jest.mock('../../../../src/autocomplete/UserProvider'); +jest.mock("../../../../src/autocomplete/UserProvider"); const completions: ICompletion[] = [ - { type: 'user', completion: 'user_1', completionId: '@user_1:host.local', range: { start: 1, end: 1 } }, - { type: 'user', completion: 'user_2', completionId: '@user_2:host.local', range: { start: 1, end: 1 } }, - { type: 'user', completion: 'user_without_completion_id', range: { start: 1, end: 1 } }, + { type: "user", completion: "user_1", completionId: "@user_1:host.local", range: { start: 1, end: 1 } }, + { type: "user", completion: "user_2", completionId: "@user_2:host.local", range: { start: 1, end: 1 } }, + { type: "user", completion: "user_without_completion_id", range: { start: 1, end: 1 } }, ]; -describe('', () => { +describe("", () => { const provider = mocked(UserProvider, { shallow: true }); provider.prototype.getCompletions.mockResolvedValue(completions); @@ -50,9 +47,9 @@ describe('', () => { setPowerLevel: jest.fn(), }); - const room = makeRoomWithStateEvents([], { roomId: 'room_id', mockClient: mockClient }); + const room = makeRoomWithStateEvents([], { roomId: "room_id", mockClient: mockClient }); room.getMember = (userId: string) => { - const member = new RoomMember('room_id', userId); + const member = new RoomMember("room_id", userId); member.powerLevel = 0; return member; @@ -61,36 +58,34 @@ describe('', () => { return mkEvent({ type: EventType.RoomPowerLevels, content: {}, - user: 'user_id', + user: "user_id", }); }; - const getComponent = () => + const getComponent = () => ( - - ; + + + ); - it('checks whether form submit works as intended', async () => { + it("checks whether form submit works as intended", async () => { const { getByTestId, queryAllByTestId } = render(getComponent()); // Verify that the submit button is disabled initially. - const submitButton = getByTestId('add-privileged-users-submit-button'); + const submitButton = getByTestId("add-privileged-users-submit-button"); expect(submitButton).toBeDisabled(); // Find some suggestions and select them. - const autocompleteInput = getByTestId('autocomplete-input'); + const autocompleteInput = getByTestId("autocomplete-input"); act(() => { fireEvent.focus(autocompleteInput); - fireEvent.change(autocompleteInput, { target: { value: 'u' } }); + fireEvent.change(autocompleteInput, { target: { value: "u" } }); }); await waitFor(() => expect(provider.mock.instances[0].getCompletions).toHaveBeenCalledTimes(1)); - const matchOne = getByTestId('autocomplete-suggestion-item-@user_1:host.local'); - const matchTwo = getByTestId('autocomplete-suggestion-item-@user_2:host.local'); + const matchOne = getByTestId("autocomplete-suggestion-item-@user_1:host.local"); + const matchTwo = getByTestId("autocomplete-suggestion-item-@user_2:host.local"); act(() => { fireEvent.mouseDown(matchOne); @@ -101,16 +96,16 @@ describe('', () => { }); // Check that `defaultUserLevel` is initially set and select a higher power level. - expect((getByTestId('power-level-option-0') as HTMLOptionElement).selected).toBeTruthy(); - expect((getByTestId('power-level-option-50') as HTMLOptionElement).selected).toBeFalsy(); - expect((getByTestId('power-level-option-100') as HTMLOptionElement).selected).toBeFalsy(); + expect((getByTestId("power-level-option-0") as HTMLOptionElement).selected).toBeTruthy(); + expect((getByTestId("power-level-option-50") as HTMLOptionElement).selected).toBeFalsy(); + expect((getByTestId("power-level-option-100") as HTMLOptionElement).selected).toBeFalsy(); - const powerLevelSelect = getByTestId('power-level-select-element'); + const powerLevelSelect = getByTestId("power-level-select-element"); await userEvent.selectOptions(powerLevelSelect, "100"); - expect((getByTestId('power-level-option-0') as HTMLOptionElement).selected).toBeFalsy(); - expect((getByTestId('power-level-option-50') as HTMLOptionElement).selected).toBeFalsy(); - expect((getByTestId('power-level-option-100') as HTMLOptionElement).selected).toBeTruthy(); + expect((getByTestId("power-level-option-0") as HTMLOptionElement).selected).toBeFalsy(); + expect((getByTestId("power-level-option-50") as HTMLOptionElement).selected).toBeFalsy(); + expect((getByTestId("power-level-option-100") as HTMLOptionElement).selected).toBeTruthy(); // The submit button should be enabled now. expect(submitButton).toBeEnabled(); @@ -126,24 +121,25 @@ describe('', () => { expect(submitButton).toBeDisabled(); // Verify that previously selected items are reset. - const selectionItems = queryAllByTestId('autocomplete-selection-item', { exact: false }); + const selectionItems = queryAllByTestId("autocomplete-selection-item", { exact: false }); expect(selectionItems).toHaveLength(0); // Verify that power level select is reset to `defaultUserLevel`. - expect((getByTestId('power-level-option-0') as HTMLOptionElement).selected).toBeTruthy(); - expect((getByTestId('power-level-option-50') as HTMLOptionElement).selected).toBeFalsy(); - expect((getByTestId('power-level-option-100') as HTMLOptionElement).selected).toBeFalsy(); + expect((getByTestId("power-level-option-0") as HTMLOptionElement).selected).toBeTruthy(); + expect((getByTestId("power-level-option-50") as HTMLOptionElement).selected).toBeFalsy(); + expect((getByTestId("power-level-option-100") as HTMLOptionElement).selected).toBeFalsy(); }); - it('getUserIdsFromCompletions() should map completions to user id\'s', () => { - expect(getUserIdsFromCompletions(completions)).toStrictEqual(['@user_1:host.local', '@user_2:host.local']); + it("getUserIdsFromCompletions() should map completions to user id's", () => { + expect(getUserIdsFromCompletions(completions)).toStrictEqual(["@user_1:host.local", "@user_2:host.local"]); }); it.each([ { defaultUserLevel: -50, expectation: false }, { defaultUserLevel: 0, expectation: true }, { defaultUserLevel: 50, expectation: true }, - ])('hasLowerOrEqualLevelThanDefaultLevel() should return $expectation for default level $defaultUserLevel', + ])( + "hasLowerOrEqualLevelThanDefaultLevel() should return $expectation for default level $defaultUserLevel", ({ defaultUserLevel, expectation }) => { expect(hasLowerOrEqualLevelThanDefaultLevel(room, completions[0], defaultUserLevel)).toBe(expectation); }, diff --git a/test/components/views/settings/CryptographyPanel-test.tsx b/test/components/views/settings/CryptographyPanel-test.tsx index c46aa09f5a..7455b2adc5 100644 --- a/test/components/views/settings/CryptographyPanel-test.tsx +++ b/test/components/views/settings/CryptographyPanel-test.tsx @@ -14,16 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { ReactElement } from 'react'; -import ReactDOM from 'react-dom'; -import { MatrixClient } from 'matrix-js-sdk/src/matrix'; +import React, { ReactElement } from "react"; +import ReactDOM from "react-dom"; +import { MatrixClient } from "matrix-js-sdk/src/matrix"; -import { MatrixClientPeg } from '../../../../src/MatrixClientPeg'; -import * as TestUtils from '../../../test-utils'; -import CryptographyPanel from '../../../../src/components/views/settings/CryptographyPanel'; +import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; +import * as TestUtils from "../../../test-utils"; +import CryptographyPanel from "../../../../src/components/views/settings/CryptographyPanel"; -describe('CryptographyPanel', () => { - it('shows the session ID and key', () => { +describe("CryptographyPanel", () => { + it("shows the session ID and key", () => { const sessionId = "ABCDEFGHIJ"; const sessionKey = "AbCDeFghIJK7L/m4nOPqRSTUVW4xyzaBCDef6gHIJkl"; const sessionKeyFormatted = "AbCD eFgh IJK7 L/m4 nOPq RSTU VW4x yzaB CDef 6gHI Jkl"; @@ -45,7 +45,7 @@ describe('CryptographyPanel', () => { }); function render(component: ReactElement): HTMLDivElement { - const parentDiv = document.createElement('div'); + const parentDiv = document.createElement("div"); document.body.appendChild(parentDiv); ReactDOM.render(component, parentDiv); return parentDiv; diff --git a/test/components/views/settings/DevicesPanel-test.tsx b/test/components/views/settings/DevicesPanel-test.tsx index 81f6fb328a..3565e59c43 100644 --- a/test/components/views/settings/DevicesPanel-test.tsx +++ b/test/components/views/settings/DevicesPanel-test.tsx @@ -13,81 +13,76 @@ 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 { fireEvent, render } from '@testing-library/react'; -import { act } from 'react-dom/test-utils'; -import { CrossSigningInfo } from 'matrix-js-sdk/src/crypto/CrossSigning'; -import { DeviceInfo } from 'matrix-js-sdk/src/crypto/deviceinfo'; -import { sleep } from 'matrix-js-sdk/src/utils'; -import { PUSHER_DEVICE_ID, PUSHER_ENABLED } from 'matrix-js-sdk/src/@types/event'; +import React from "react"; +import { fireEvent, render } from "@testing-library/react"; +import { act } from "react-dom/test-utils"; +import { CrossSigningInfo } from "matrix-js-sdk/src/crypto/CrossSigning"; +import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo"; +import { sleep } from "matrix-js-sdk/src/utils"; +import { PUSHER_DEVICE_ID, PUSHER_ENABLED } from "matrix-js-sdk/src/@types/event"; import DevicesPanel from "../../../../src/components/views/settings/DevicesPanel"; -import { - flushPromises, - getMockClientWithEventEmitter, - mkPusher, - mockClientMethodsUser, -} from "../../../test-utils"; -import MatrixClientContext from '../../../../src/contexts/MatrixClientContext'; +import { flushPromises, getMockClientWithEventEmitter, mkPusher, mockClientMethodsUser } from "../../../test-utils"; +import MatrixClientContext from "../../../../src/contexts/MatrixClientContext"; -describe('', () => { - const userId = '@alice:server.org'; - const device1 = { device_id: 'device_1' }; - const device2 = { device_id: 'device_2' }; - const device3 = { device_id: 'device_3' }; +describe("", () => { + const userId = "@alice:server.org"; + const device1 = { device_id: "device_1" }; + const device2 = { device_id: "device_2" }; + const device3 = { device_id: "device_3" }; const mockClient = getMockClientWithEventEmitter({ ...mockClientMethodsUser(userId), getDevices: jest.fn(), getDeviceId: jest.fn().mockReturnValue(device1.device_id), deleteMultipleDevices: jest.fn(), getStoredCrossSigningForUser: jest.fn().mockReturnValue(new CrossSigningInfo(userId, {}, {})), - getStoredDevice: jest.fn().mockReturnValue(new DeviceInfo('id')), + getStoredDevice: jest.fn().mockReturnValue(new DeviceInfo("id")), generateClientSecret: jest.fn(), getPushers: jest.fn(), setPusher: jest.fn(), }); - const getComponent = () => + const getComponent = () => ( - ; + + ); beforeEach(() => { jest.clearAllMocks(); - mockClient.getDevices - .mockReset() - .mockResolvedValue({ devices: [device1, device2, device3] }); + mockClient.getDevices.mockReset().mockResolvedValue({ devices: [device1, device2, device3] }); - mockClient.getPushers - .mockReset() - .mockResolvedValue({ - pushers: [mkPusher({ + mockClient.getPushers.mockReset().mockResolvedValue({ + pushers: [ + mkPusher({ [PUSHER_DEVICE_ID.name]: device1.device_id, [PUSHER_ENABLED.name]: true, - })], - }); + }), + ], + }); }); - it('renders device panel with devices', async () => { + it("renders device panel with devices", async () => { const { container } = render(getComponent()); await flushPromises(); expect(container).toMatchSnapshot(); }); - describe('device deletion', () => { + describe("device deletion", () => { const interactiveAuthError = { httpStatus: 401, data: { flows: [{ stages: ["m.login.password"] }] } }; - const toggleDeviceSelection = (container: HTMLElement, deviceId: string) => act(() => { - const checkbox = container.querySelector(`#device-tile-checkbox-${deviceId}`); - fireEvent.click(checkbox); - }); + const toggleDeviceSelection = (container: HTMLElement, deviceId: string) => + act(() => { + const checkbox = container.querySelector(`#device-tile-checkbox-${deviceId}`); + fireEvent.click(checkbox); + }); beforeEach(() => { mockClient.deleteMultipleDevices.mockReset(); }); - it('deletes selected devices when interactive auth is not required', async () => { + it("deletes selected devices when interactive auth is not required", async () => { mockClient.deleteMultipleDevices.mockResolvedValue({}); mockClient.getDevices .mockResolvedValueOnce({ devices: [device1, device2, device3] }) @@ -97,17 +92,17 @@ describe('', () => { const { container, getByTestId } = render(getComponent()); await flushPromises(); - expect(container.getElementsByClassName('mx_DevicesPanel_device').length).toEqual(3); + expect(container.getElementsByClassName("mx_DevicesPanel_device").length).toEqual(3); toggleDeviceSelection(container, device2.device_id); mockClient.getDevices.mockClear(); act(() => { - fireEvent.click(getByTestId('sign-out-devices-btn')); + fireEvent.click(getByTestId("sign-out-devices-btn")); }); - expect(container.getElementsByClassName('mx_Spinner').length).toBeTruthy(); + expect(container.getElementsByClassName("mx_Spinner").length).toBeTruthy(); expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith([device2.device_id], undefined); await flushPromises(); @@ -115,10 +110,10 @@ describe('', () => { // devices refreshed expect(mockClient.getDevices).toHaveBeenCalled(); // and rerendered - expect(container.getElementsByClassName('mx_DevicesPanel_device').length).toEqual(2); + expect(container.getElementsByClassName("mx_DevicesPanel_device").length).toEqual(2); }); - it('deletes selected devices when interactive auth is required', async () => { + it("deletes selected devices when interactive auth is required", async () => { mockClient.deleteMultipleDevices // require auth .mockRejectedValueOnce(interactiveAuthError) @@ -140,7 +135,7 @@ describe('', () => { toggleDeviceSelection(container, device2.device_id); act(() => { - fireEvent.click(getByTestId('sign-out-devices-btn')); + fireEvent.click(getByTestId("sign-out-devices-btn")); }); await flushPromises(); @@ -149,30 +144,34 @@ describe('', () => { expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith([device2.device_id], undefined); - const modal = document.getElementsByClassName('mx_Dialog'); + const modal = document.getElementsByClassName("mx_Dialog"); expect(modal).toMatchSnapshot(); // fill password and submit for interactive auth act(() => { - fireEvent.change(getByLabelText('Password'), { target: { value: 'topsecret' } }); - fireEvent.submit(getByLabelText('Password')); + fireEvent.change(getByLabelText("Password"), { target: { value: "topsecret" } }); + fireEvent.submit(getByLabelText("Password")); }); await flushPromises(); // called again with auth - expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith([device2.device_id], - { identifier: { - type: "m.id.user", user: userId, - }, password: "", type: "m.login.password", user: userId, - }); + expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith([device2.device_id], { + identifier: { + type: "m.id.user", + user: userId, + }, + password: "", + type: "m.login.password", + user: userId, + }); // devices refreshed expect(mockClient.getDevices).toHaveBeenCalled(); // and rerendered - expect(container.getElementsByClassName('mx_DevicesPanel_device').length).toEqual(2); + expect(container.getElementsByClassName("mx_DevicesPanel_device").length).toEqual(2); }); - it('clears loading state when interactive auth fail is cancelled', async () => { + it("clears loading state when interactive auth fail is cancelled", async () => { mockClient.deleteMultipleDevices // require auth .mockRejectedValueOnce(interactiveAuthError) @@ -194,10 +193,10 @@ describe('', () => { toggleDeviceSelection(container, device2.device_id); act(() => { - fireEvent.click(getByTestId('sign-out-devices-btn')); + fireEvent.click(getByTestId("sign-out-devices-btn")); }); - expect(container.getElementsByClassName('mx_Spinner').length).toBeTruthy(); + expect(container.getElementsByClassName("mx_Spinner").length).toBeTruthy(); await flushPromises(); // modal rendering has some weird sleeps @@ -214,7 +213,7 @@ describe('', () => { // not refreshed expect(mockClient.getDevices).not.toHaveBeenCalled(); // spinner removed - expect(container.getElementsByClassName('mx_Spinner').length).toBeFalsy(); + expect(container.getElementsByClassName("mx_Spinner").length).toBeFalsy(); }); }); }); diff --git a/test/components/views/settings/FontScalingPanel-test.tsx b/test/components/views/settings/FontScalingPanel-test.tsx index aa8421f083..d52dacee54 100644 --- a/test/components/views/settings/FontScalingPanel-test.tsx +++ b/test/components/views/settings/FontScalingPanel-test.tsx @@ -14,28 +14,23 @@ 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 React from "react"; +import { render } from "@testing-library/react"; import * as TestUtils from "../../../test-utils"; -import FontScalingPanel from '../../../../src/components/views/settings/FontScalingPanel'; +import FontScalingPanel from "../../../../src/components/views/settings/FontScalingPanel"; // Fake random strings to give a predictable snapshot -jest.mock( - 'matrix-js-sdk/src/randomstring', - () => { - return { - randomString: () => "abdefghi", - }; - }, -); +jest.mock("matrix-js-sdk/src/randomstring", () => { + return { + randomString: () => "abdefghi", + }; +}); -describe('FontScalingPanel', () => { - it('renders the font scaling UI', () => { +describe("FontScalingPanel", () => { + it("renders the font scaling UI", () => { TestUtils.stubClient(); - const { asFragment } = render( - , - ); + const { asFragment } = render(); expect(asFragment()).toMatchSnapshot(); }); }); diff --git a/test/components/views/settings/KeyboardShortcut-test.tsx b/test/components/views/settings/KeyboardShortcut-test.tsx index d26c0dd1e9..b12fc514c4 100644 --- a/test/components/views/settings/KeyboardShortcut-test.tsx +++ b/test/components/views/settings/KeyboardShortcut-test.tsx @@ -1,4 +1,3 @@ - /* Copyright 2022 Šimon Brandner diff --git a/test/components/views/settings/Notifications-test.tsx b/test/components/views/settings/Notifications-test.tsx index df2a5f4b61..6ee844d4e0 100644 --- a/test/components/views/settings/Notifications-test.tsx +++ b/test/components/views/settings/Notifications-test.tsx @@ -12,7 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; import { IPushRule, IPushRules, @@ -22,15 +22,15 @@ import { MatrixEvent, Room, NotificationCountType, -} from 'matrix-js-sdk/src/matrix'; -import { IThreepid, ThreepidMedium } from 'matrix-js-sdk/src/@types/threepids'; -import { act } from 'react-dom/test-utils'; -import { fireEvent, getByTestId, render, screen, waitFor } from '@testing-library/react'; +} from "matrix-js-sdk/src/matrix"; +import { IThreepid, ThreepidMedium } from "matrix-js-sdk/src/@types/threepids"; +import { act } from "react-dom/test-utils"; +import { fireEvent, getByTestId, render, screen, waitFor } from "@testing-library/react"; -import Notifications from '../../../../src/components/views/settings/Notifications'; +import Notifications from "../../../../src/components/views/settings/Notifications"; import SettingsStore from "../../../../src/settings/SettingsStore"; -import { StandardActions } from '../../../../src/notifications/StandardActions'; -import { getMockClientWithEventEmitter, mkMessage } from '../../../test-utils'; +import { StandardActions } from "../../../../src/notifications/StandardActions"; +import { getMockClientWithEventEmitter, mkMessage } from "../../../test-utils"; // don't pollute test output with error logs from mock rejections jest.mock("matrix-js-sdk/src/logger"); @@ -46,17 +46,155 @@ const masterRule = { rule_id: RuleId.Master, }; // eslint-disable-next-line max-len -const oneToOneRule = { "conditions": [{ "kind": "room_member_count", "is": "2" }, { "kind": "event_match", "key": "type", "pattern": "m.room.message" }], "actions": ["notify", { "set_tweak": "highlight", "value": false }], "rule_id": ".m.rule.room_one_to_one", "default": true, "enabled": true } as IPushRule; +const oneToOneRule = { + conditions: [ + { kind: "room_member_count", is: "2" }, + { kind: "event_match", key: "type", pattern: "m.room.message" }, + ], + actions: ["notify", { set_tweak: "highlight", value: false }], + rule_id: ".m.rule.room_one_to_one", + default: true, + enabled: true, +} as IPushRule; // eslint-disable-next-line max-len -const encryptedOneToOneRule = { "conditions": [{ "kind": "room_member_count", "is": "2" }, { "kind": "event_match", "key": "type", "pattern": "m.room.encrypted" }], "actions": ["notify", { "set_tweak": "sound", "value": "default" }, { "set_tweak": "highlight", "value": false }], "rule_id": ".m.rule.encrypted_room_one_to_one", "default": true, "enabled": true } as IPushRule; +const encryptedOneToOneRule = { + conditions: [ + { kind: "room_member_count", is: "2" }, + { kind: "event_match", key: "type", pattern: "m.room.encrypted" }, + ], + actions: ["notify", { set_tweak: "sound", value: "default" }, { set_tweak: "highlight", value: false }], + rule_id: ".m.rule.encrypted_room_one_to_one", + default: true, + enabled: true, +} as IPushRule; // eslint-disable-next-line max-len -const encryptedGroupRule = { "conditions": [{ "kind": "event_match", "key": "type", "pattern": "m.room.encrypted" }], "actions": ["dont_notify"], "rule_id": ".m.rule.encrypted", "default": true, "enabled": true } as IPushRule; +const encryptedGroupRule = { + conditions: [{ kind: "event_match", key: "type", pattern: "m.room.encrypted" }], + actions: ["dont_notify"], + rule_id: ".m.rule.encrypted", + default: true, + enabled: true, +} as IPushRule; // eslint-disable-next-line max-len -const pushRules: IPushRules = { "global": { "underride": [{ "conditions": [{ "kind": "event_match", "key": "type", "pattern": "m.call.invite" }], "actions": ["notify", { "set_tweak": "sound", "value": "ring" }, { "set_tweak": "highlight", "value": false }], "rule_id": ".m.rule.call", "default": true, "enabled": true }, oneToOneRule, encryptedOneToOneRule, { "conditions": [{ "kind": "event_match", "key": "type", "pattern": "m.room.message" }], "actions": ["notify", { "set_tweak": "sound", "value": "default" }, { "set_tweak": "highlight", "value": false }], "rule_id": ".m.rule.message", "default": true, "enabled": true }, encryptedGroupRule, { "conditions": [{ "kind": "event_match", "key": "type", "pattern": "im.vector.modular.widgets" }, { "kind": "event_match", "key": "content.type", "pattern": "jitsi" }, { "kind": "event_match", "key": "state_key", "pattern": "*" }], "actions": ["notify", { "set_tweak": "highlight", "value": false }], "rule_id": ".im.vector.jitsi", "default": true, "enabled": true }], "sender": [], "room": [{ "actions": ["dont_notify"], "rule_id": "!zJPyWqpMorfCcWObge:matrix.org", "default": false, "enabled": true }], "content": [{ "actions": ["notify", { "set_tweak": "highlight", "value": false }], "pattern": "banana", "rule_id": "banana", "default": false, "enabled": true }, { "actions": ["notify", { "set_tweak": "sound", "value": "default" }, { "set_tweak": "highlight" }], "pattern": "kadev1", "rule_id": ".m.rule.contains_user_name", "default": true, "enabled": true }], "override": [{ "conditions": [], "actions": ["dont_notify"], "rule_id": ".m.rule.master", "default": true, "enabled": false }, { "conditions": [{ "kind": "event_match", "key": "content.msgtype", "pattern": "m.notice" }], "actions": ["dont_notify"], "rule_id": ".m.rule.suppress_notices", "default": true, "enabled": true }, { "conditions": [{ "kind": "event_match", "key": "type", "pattern": "m.room.member" }, { "kind": "event_match", "key": "content.membership", "pattern": "invite" }, { "kind": "event_match", "key": "state_key", "pattern": "@kadev1:matrix.org" }], "actions": ["notify", { "set_tweak": "sound", "value": "default" }, { "set_tweak": "highlight", "value": false }], "rule_id": ".m.rule.invite_for_me", "default": true, "enabled": true }, { "conditions": [{ "kind": "event_match", "key": "type", "pattern": "m.room.member" }], "actions": ["dont_notify"], "rule_id": ".m.rule.member_event", "default": true, "enabled": true }, { "conditions": [{ "kind": "contains_display_name" }], "actions": ["notify", { "set_tweak": "sound", "value": "default" }, { "set_tweak": "highlight" }], "rule_id": ".m.rule.contains_display_name", "default": true, "enabled": true }, { "conditions": [{ "kind": "event_match", "key": "content.body", "pattern": "@room" }, { "kind": "sender_notification_permission", "key": "room" }], "actions": ["notify", { "set_tweak": "highlight", "value": true }], "rule_id": ".m.rule.roomnotif", "default": true, "enabled": true }, { "conditions": [{ "kind": "event_match", "key": "type", "pattern": "m.room.tombstone" }, { "kind": "event_match", "key": "state_key", "pattern": "" }], "actions": ["notify", { "set_tweak": "highlight", "value": true }], "rule_id": ".m.rule.tombstone", "default": true, "enabled": true }, { "conditions": [{ "kind": "event_match", "key": "type", "pattern": "m.reaction" }], "actions": ["dont_notify"], "rule_id": ".m.rule.reaction", "default": true, "enabled": true }] }, "device": {} } as IPushRules; +const pushRules: IPushRules = { + global: { + underride: [ + { + conditions: [{ kind: "event_match", key: "type", pattern: "m.call.invite" }], + actions: ["notify", { set_tweak: "sound", value: "ring" }, { set_tweak: "highlight", value: false }], + rule_id: ".m.rule.call", + default: true, + enabled: true, + }, + oneToOneRule, + encryptedOneToOneRule, + { + conditions: [{ kind: "event_match", key: "type", pattern: "m.room.message" }], + actions: ["notify", { set_tweak: "sound", value: "default" }, { set_tweak: "highlight", value: false }], + rule_id: ".m.rule.message", + default: true, + enabled: true, + }, + encryptedGroupRule, + { + conditions: [ + { kind: "event_match", key: "type", pattern: "im.vector.modular.widgets" }, + { kind: "event_match", key: "content.type", pattern: "jitsi" }, + { kind: "event_match", key: "state_key", pattern: "*" }, + ], + actions: ["notify", { set_tweak: "highlight", value: false }], + rule_id: ".im.vector.jitsi", + default: true, + enabled: true, + }, + ], + sender: [], + room: [{ actions: ["dont_notify"], rule_id: "!zJPyWqpMorfCcWObge:matrix.org", default: false, enabled: true }], + content: [ + { + actions: ["notify", { set_tweak: "highlight", value: false }], + pattern: "banana", + rule_id: "banana", + default: false, + enabled: true, + }, + { + actions: ["notify", { set_tweak: "sound", value: "default" }, { set_tweak: "highlight" }], + pattern: "kadev1", + rule_id: ".m.rule.contains_user_name", + default: true, + enabled: true, + }, + ], + override: [ + { conditions: [], actions: ["dont_notify"], rule_id: ".m.rule.master", default: true, enabled: false }, + { + conditions: [{ kind: "event_match", key: "content.msgtype", pattern: "m.notice" }], + actions: ["dont_notify"], + rule_id: ".m.rule.suppress_notices", + default: true, + enabled: true, + }, + { + conditions: [ + { kind: "event_match", key: "type", pattern: "m.room.member" }, + { kind: "event_match", key: "content.membership", pattern: "invite" }, + { kind: "event_match", key: "state_key", pattern: "@kadev1:matrix.org" }, + ], + actions: ["notify", { set_tweak: "sound", value: "default" }, { set_tweak: "highlight", value: false }], + rule_id: ".m.rule.invite_for_me", + default: true, + enabled: true, + }, + { + conditions: [{ kind: "event_match", key: "type", pattern: "m.room.member" }], + actions: ["dont_notify"], + rule_id: ".m.rule.member_event", + default: true, + enabled: true, + }, + { + conditions: [{ kind: "contains_display_name" }], + actions: ["notify", { set_tweak: "sound", value: "default" }, { set_tweak: "highlight" }], + rule_id: ".m.rule.contains_display_name", + default: true, + enabled: true, + }, + { + conditions: [ + { kind: "event_match", key: "content.body", pattern: "@room" }, + { kind: "sender_notification_permission", key: "room" }, + ], + actions: ["notify", { set_tweak: "highlight", value: true }], + rule_id: ".m.rule.roomnotif", + default: true, + enabled: true, + }, + { + conditions: [ + { kind: "event_match", key: "type", pattern: "m.room.tombstone" }, + { kind: "event_match", key: "state_key", pattern: "" }, + ], + actions: ["notify", { set_tweak: "highlight", value: true }], + rule_id: ".m.rule.tombstone", + default: true, + enabled: true, + }, + { + conditions: [{ kind: "event_match", key: "type", pattern: "m.reaction" }], + actions: ["dont_notify"], + rule_id: ".m.rule.reaction", + default: true, + enabled: true, + }, + ], + }, + device: {}, +} as IPushRules; -const flushPromises = async () => await new Promise(resolve => window.setTimeout(resolve)); +const flushPromises = async () => await new Promise((resolve) => window.setTimeout(resolve)); -describe('', () => { +describe("", () => { const getComponent = () => render(); // get component, wait for async data and force a render @@ -74,7 +212,7 @@ describe('', () => { setPushRuleEnabled: jest.fn(), setPushRuleActions: jest.fn(), getRooms: jest.fn().mockReturnValue([]), - getAccountData: jest.fn().mockImplementation(eventType => { + getAccountData: jest.fn().mockImplementation((eventType) => { if (eventType.startsWith(LOCAL_NOTIFICATION_SETTINGS_PREFIX.name)) { return new MatrixEvent({ type: eventType, @@ -97,29 +235,29 @@ describe('', () => { mockClient.setPusher.mockClear().mockResolvedValue({}); }); - it('renders spinner while loading', async () => { + it("renders spinner while loading", async () => { getComponent(); - expect(screen.getByTestId('spinner')).toBeInTheDocument(); + expect(screen.getByTestId("spinner")).toBeInTheDocument(); }); - it('renders error message when fetching push rules fails', async () => { + it("renders error message when fetching push rules fails", async () => { mockClient.getPushRules.mockRejectedValue({}); await getComponentAndWait(); - expect(screen.getByTestId('error-message')).toBeInTheDocument(); + expect(screen.getByTestId("error-message")).toBeInTheDocument(); }); - it('renders error message when fetching pushers fails', async () => { + it("renders error message when fetching pushers fails", async () => { mockClient.getPushers.mockRejectedValue({}); await getComponentAndWait(); - expect(screen.getByTestId('error-message')).toBeInTheDocument(); + expect(screen.getByTestId("error-message")).toBeInTheDocument(); }); - it('renders error message when fetching threepids fails', async () => { + it("renders error message when fetching threepids fails", async () => { mockClient.getThreePids.mockRejectedValue({}); await getComponentAndWait(); - expect(screen.getByTestId('error-message')).toBeInTheDocument(); + expect(screen.getByTestId("error-message")).toBeInTheDocument(); }); - describe('main notification switches', () => { - it('renders only enable notifications switch when notifications are disabled', async () => { + describe("main notification switches", () => { + it("renders only enable notifications switch when notifications are disabled", async () => { const disableNotificationsPushRules = { global: { ...pushRules.global, @@ -131,18 +269,18 @@ describe('', () => { expect(container).toMatchSnapshot(); }); - it('renders switches correctly', async () => { + it("renders switches correctly", async () => { await getComponentAndWait(); - expect(screen.getByTestId('notif-master-switch')).toBeInTheDocument(); - expect(screen.getByTestId('notif-device-switch')).toBeInTheDocument(); - expect(screen.getByTestId('notif-setting-notificationsEnabled')).toBeInTheDocument(); - expect(screen.getByTestId('notif-setting-notificationBodyEnabled')).toBeInTheDocument(); - expect(screen.getByTestId('notif-setting-audioNotificationsEnabled')).toBeInTheDocument(); + expect(screen.getByTestId("notif-master-switch")).toBeInTheDocument(); + expect(screen.getByTestId("notif-device-switch")).toBeInTheDocument(); + expect(screen.getByTestId("notif-setting-notificationsEnabled")).toBeInTheDocument(); + expect(screen.getByTestId("notif-setting-notificationBodyEnabled")).toBeInTheDocument(); + expect(screen.getByTestId("notif-setting-audioNotificationsEnabled")).toBeInTheDocument(); }); - describe('email switches', () => { - const testEmail = 'tester@test.com'; + describe("email switches", () => { + const testEmail = "tester@test.com"; beforeEach(() => { mockClient.getThreePids.mockResolvedValue({ threepids: [ @@ -155,49 +293,47 @@ describe('', () => { }); }); - it('renders email switches correctly when email 3pids exist', async () => { + it("renders email switches correctly when email 3pids exist", async () => { await getComponentAndWait(); - expect(screen.getByTestId('notif-email-switch')).toBeInTheDocument(); + expect(screen.getByTestId("notif-email-switch")).toBeInTheDocument(); }); - it('renders email switches correctly when notifications are on for email', async () => { + it("renders email switches correctly when notifications are on for email", async () => { mockClient.getPushers.mockResolvedValue({ - pushers: [ - { kind: 'email', pushkey: testEmail } as unknown as IPusher, - ], + pushers: [{ kind: "email", pushkey: testEmail } as unknown as IPusher], }); await getComponentAndWait(); - const emailSwitch = screen.getByTestId('notif-email-switch'); + const emailSwitch = screen.getByTestId("notif-email-switch"); expect(emailSwitch.querySelector('[aria-checked="true"]')).toBeInTheDocument(); }); - it('enables email notification when toggling on', async () => { + it("enables email notification when toggling on", async () => { await getComponentAndWait(); - const emailToggle = screen.getByTestId('notif-email-switch') - .querySelector('div[role="switch"]'); + const emailToggle = screen.getByTestId("notif-email-switch").querySelector('div[role="switch"]'); await act(async () => { fireEvent.click(emailToggle); }); - expect(mockClient.setPusher).toHaveBeenCalledWith(expect.objectContaining({ - kind: "email", - app_id: "m.email", - pushkey: testEmail, - app_display_name: "Email Notifications", - device_display_name: testEmail, - append: true, - })); + expect(mockClient.setPusher).toHaveBeenCalledWith( + expect.objectContaining({ + kind: "email", + app_id: "m.email", + pushkey: testEmail, + app_display_name: "Email Notifications", + device_display_name: testEmail, + append: true, + }), + ); }); - it('displays error when pusher update fails', async () => { + it("displays error when pusher update fails", async () => { mockClient.setPusher.mockRejectedValue({}); await getComponentAndWait(); - const emailToggle = screen.getByTestId('notif-email-switch') - .querySelector('div[role="switch"]'); + const emailToggle = screen.getByTestId("notif-email-switch").querySelector('div[role="switch"]'); await act(async () => { fireEvent.click(emailToggle); @@ -206,33 +342,34 @@ describe('', () => { // force render await flushPromises(); - expect(screen.getByTestId('error-message')).toBeInTheDocument(); + expect(screen.getByTestId("error-message")).toBeInTheDocument(); }); - it('enables email notification when toggling off', async () => { - const testPusher = { kind: 'email', pushkey: 'tester@test.com' } as unknown as IPusher; + it("enables email notification when toggling off", async () => { + const testPusher = { kind: "email", pushkey: "tester@test.com" } as unknown as IPusher; mockClient.getPushers.mockResolvedValue({ pushers: [testPusher] }); await getComponentAndWait(); - const emailToggle = screen.getByTestId('notif-email-switch') - .querySelector('div[role="switch"]'); + const emailToggle = screen.getByTestId("notif-email-switch").querySelector('div[role="switch"]'); await act(async () => { fireEvent.click(emailToggle); }); expect(mockClient.setPusher).toHaveBeenCalledWith({ - ...testPusher, kind: null, + ...testPusher, + kind: null, }); }); }); - it('toggles and sets settings correctly', async () => { + it("toggles and sets settings correctly", async () => { await getComponentAndWait(); let audioNotifsToggle; const update = () => { - audioNotifsToggle = screen.getByTestId('notif-setting-audioNotificationsEnabled') + audioNotifsToggle = screen + .getByTestId("notif-setting-audioNotificationsEnabled") .querySelector('div[role="switch"]'); }; update(); @@ -240,7 +377,9 @@ describe('', () => { expect(audioNotifsToggle.getAttribute("aria-checked")).toEqual("true"); expect(SettingsStore.getValue("audioNotificationsEnabled")).toEqual(true); - act(() => { fireEvent.click(audioNotifsToggle); }); + act(() => { + fireEvent.click(audioNotifsToggle); + }); update(); expect(audioNotifsToggle.getAttribute("aria-checked")).toEqual("false"); @@ -248,22 +387,22 @@ describe('', () => { }); }); - describe('individual notification level settings', () => { - it('renders categories correctly', async () => { + describe("individual notification level settings", () => { + it("renders categories correctly", async () => { await getComponentAndWait(); - expect(screen.getByTestId('notif-section-vector_global')).toBeInTheDocument(); - expect(screen.getByTestId('notif-section-vector_mentions')).toBeInTheDocument(); - expect(screen.getByTestId('notif-section-vector_other')).toBeInTheDocument(); + expect(screen.getByTestId("notif-section-vector_global")).toBeInTheDocument(); + expect(screen.getByTestId("notif-section-vector_mentions")).toBeInTheDocument(); + expect(screen.getByTestId("notif-section-vector_other")).toBeInTheDocument(); }); - it('renders radios correctly', async () => { + it("renders radios correctly", async () => { await getComponentAndWait(); - const section = 'vector_global'; + const section = "vector_global"; const globalSection = screen.getByTestId(`notif-section-${section}`); // 4 notification rules with class 'global' - expect(globalSection.querySelectorAll('fieldset').length).toEqual(4); + expect(globalSection.querySelectorAll("fieldset").length).toEqual(4); // oneToOneRule is set to 'on' const oneToOneRuleElement = screen.getByTestId(section + oneToOneRule.rule_id); expect(oneToOneRuleElement.querySelector("[aria-label='On']")).toBeInTheDocument(); @@ -275,9 +414,9 @@ describe('', () => { expect(encryptedGroupElement.querySelector("[aria-label='Off']")).toBeInTheDocument(); }); - it('updates notification level when changed', async () => { + it("updates notification level when changed", async () => { await getComponentAndWait(); - const section = 'vector_global'; + const section = "vector_global"; // oneToOneRule is set to 'on' // and is kind: 'underride' @@ -289,11 +428,19 @@ describe('', () => { }); expect(mockClient.setPushRuleEnabled).toHaveBeenCalledWith( - 'global', 'underride', oneToOneRule.rule_id, true); + "global", + "underride", + oneToOneRule.rule_id, + true, + ); // actions for '.m.rule.room_one_to_one' state is ACTION_DONT_NOTIFY expect(mockClient.setPushRuleActions).toHaveBeenCalledWith( - 'global', 'underride', oneToOneRule.rule_id, StandardActions.ACTION_DONT_NOTIFY); + "global", + "underride", + oneToOneRule.rule_id, + StandardActions.ACTION_DONT_NOTIFY, + ); }); }); diff --git a/test/components/views/settings/SettingsFieldset-test.tsx b/test/components/views/settings/SettingsFieldset-test.tsx index 28a784f25b..3aafce504d 100644 --- a/test/components/views/settings/SettingsFieldset-test.tsx +++ b/test/components/views/settings/SettingsFieldset-test.tsx @@ -12,35 +12,42 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import { renderIntoDocument } from 'react-dom/test-utils'; +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"; -describe('', () => { +describe("", () => { const defaultProps = { - "legend": 'Who can read history?', + "legend": "Who can read history?", "children":
test
, - 'data-test-id': 'test', + "data-test-id": "test", }; const getComponent = (props = {}) => { const wrapper = renderIntoDocument( -
, +
+ +
, ) as HTMLDivElement; return wrapper.children[0]; }; - it('renders fieldset without description', () => { + it("renders fieldset without description", () => { expect(getComponent()).toMatchSnapshot(); }); - it('renders fieldset with plain text description', () => { - const description = 'Changes to who can read history.'; + it("renders fieldset with plain text description", () => { + const description = "Changes to who can read history."; expect(getComponent({ description })).toMatchSnapshot(); }); - it('renders fieldset with react description', () => { - const description = <>

Test

a link; + it("renders fieldset with react description", () => { + const description = ( + <> +

Test

+ a link + + ); expect(getComponent({ description })).toMatchSnapshot(); }); }); diff --git a/test/components/views/settings/ThemeChoicePanel-test.tsx b/test/components/views/settings/ThemeChoicePanel-test.tsx index 2194dad1ec..ce36778231 100644 --- a/test/components/views/settings/ThemeChoicePanel-test.tsx +++ b/test/components/views/settings/ThemeChoicePanel-test.tsx @@ -14,28 +14,23 @@ 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 React from "react"; +import { render } from "@testing-library/react"; import * as TestUtils from "../../../test-utils"; -import ThemeChoicePanel from '../../../../src/components/views/settings/ThemeChoicePanel'; +import ThemeChoicePanel from "../../../../src/components/views/settings/ThemeChoicePanel"; // Fake random strings to give a predictable snapshot -jest.mock( - 'matrix-js-sdk/src/randomstring', - () => { - return { - randomString: () => "abdefghi", - }; - }, -); +jest.mock("matrix-js-sdk/src/randomstring", () => { + return { + randomString: () => "abdefghi", + }; +}); -describe('ThemeChoicePanel', () => { - it('renders the theme choice UI', () => { +describe("ThemeChoicePanel", () => { + it("renders the theme choice UI", () => { TestUtils.stubClient(); - const { asFragment } = render( - , - ); + const { asFragment } = render(); expect(asFragment()).toMatchSnapshot(); }); }); diff --git a/test/components/views/settings/UiFeatureSettingWrapper-test.tsx b/test/components/views/settings/UiFeatureSettingWrapper-test.tsx index 5e917e9025..aeba1273de 100644 --- a/test/components/views/settings/UiFeatureSettingWrapper-test.tsx +++ b/test/components/views/settings/UiFeatureSettingWrapper-test.tsx @@ -14,16 +14,16 @@ 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 React from "react"; +import { render } from "@testing-library/react"; -import SettingsStore from '../../../../src/settings/SettingsStore'; -import UiFeatureSettingWrapper from '../../../../src/components/views/settings/UiFeatureSettingWrapper'; -import { UIFeature } from '../../../../src/settings/UIFeature'; +import SettingsStore from "../../../../src/settings/SettingsStore"; +import UiFeatureSettingWrapper from "../../../../src/components/views/settings/UiFeatureSettingWrapper"; +import { UIFeature } from "../../../../src/settings/UIFeature"; -jest.mock('../../../../src/settings/SettingsStore'); +jest.mock("../../../../src/settings/SettingsStore"); -describe('', () => { +describe("", () => { const defaultProps = { uiFeature: UIFeature.Feedback, children:
test
, @@ -34,20 +34,20 @@ describe('', () => { (SettingsStore.getValue as jest.Mock).mockClear().mockReturnValue(true); }); - it('renders children when setting is truthy', () => { + it("renders children when setting is truthy", () => { const { asFragment } = getComponent(); expect(asFragment()).toMatchSnapshot(); expect(SettingsStore.getValue).toHaveBeenCalledWith(defaultProps.uiFeature); }); - it('returns null when setting is truthy but children are undefined', () => { + it("returns null when setting is truthy but children are undefined", () => { const { asFragment } = getComponent({ children: undefined }); expect(asFragment()).toMatchSnapshot(); }); - it('returns null when setting is falsy', () => { + it("returns null when setting is falsy", () => { (SettingsStore.getValue as jest.Mock).mockReturnValue(false); const { asFragment } = getComponent(); diff --git a/test/components/views/settings/devices/CurrentDeviceSection-test.tsx b/test/components/views/settings/devices/CurrentDeviceSection-test.tsx index 21bf95dbe4..f51fd51386 100644 --- a/test/components/views/settings/devices/CurrentDeviceSection-test.tsx +++ b/test/components/views/settings/devices/CurrentDeviceSection-test.tsx @@ -14,15 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import { fireEvent, render } from '@testing-library/react'; -import { act } from 'react-dom/test-utils'; +import React from "react"; +import { fireEvent, render } from "@testing-library/react"; +import { act } from "react-dom/test-utils"; -import CurrentDeviceSection from '../../../../../src/components/views/settings/devices/CurrentDeviceSection'; -import { DeviceType } from '../../../../../src/utils/device/parseUserAgent'; +import CurrentDeviceSection from "../../../../../src/components/views/settings/devices/CurrentDeviceSection"; +import { DeviceType } from "../../../../../src/utils/device/parseUserAgent"; -describe('', () => { - const deviceId = 'alices_device'; +describe("", () => { + const deviceId = "alices_device"; const alicesVerifiedDevice = { device_id: deviceId, @@ -44,60 +44,59 @@ describe('', () => { isSigningOut: false, }; - const getComponent = (props = {}): React.ReactElement => - (); + const getComponent = (props = {}): React.ReactElement => ; - it('renders spinner while device is loading', () => { + it("renders spinner while device is loading", () => { const { container } = render(getComponent({ device: undefined, isLoading: true })); - expect(container.getElementsByClassName('mx_Spinner').length).toBeTruthy(); + expect(container.getElementsByClassName("mx_Spinner").length).toBeTruthy(); }); - it('handles when device is falsy', async () => { + it("handles when device is falsy", async () => { const { container } = render(getComponent({ device: undefined })); expect(container).toMatchSnapshot(); }); - it('renders device and correct security card when device is verified', () => { + it("renders device and correct security card when device is verified", () => { const { container } = render(getComponent()); expect(container).toMatchSnapshot(); }); - it('renders device and correct security card when device is unverified', () => { + it("renders device and correct security card when device is unverified", () => { const { container } = render(getComponent({ device: alicesUnverifiedDevice })); expect(container).toMatchSnapshot(); }); - it('displays device details on main tile click', () => { + it("displays device details on main tile click", () => { const { getByTestId, container } = render(getComponent({ device: alicesUnverifiedDevice })); act(() => { fireEvent.click(getByTestId(`device-tile-${alicesUnverifiedDevice.device_id}`)); }); - expect(container.getElementsByClassName('mx_DeviceDetails').length).toBeTruthy(); + expect(container.getElementsByClassName("mx_DeviceDetails").length).toBeTruthy(); act(() => { fireEvent.click(getByTestId(`device-tile-${alicesUnverifiedDevice.device_id}`)); }); // device details are hidden - expect(container.getElementsByClassName('mx_DeviceDetails').length).toBeFalsy(); + expect(container.getElementsByClassName("mx_DeviceDetails").length).toBeFalsy(); }); - it('displays device details on toggle click', () => { + it("displays device details on toggle click", () => { const { container, getByTestId } = render(getComponent({ device: alicesUnverifiedDevice })); act(() => { - fireEvent.click(getByTestId('current-session-toggle-details')); + fireEvent.click(getByTestId("current-session-toggle-details")); }); - expect(container.getElementsByClassName('mx_DeviceDetails')).toMatchSnapshot(); + expect(container.getElementsByClassName("mx_DeviceDetails")).toMatchSnapshot(); act(() => { - fireEvent.click(getByTestId('current-session-toggle-details')); + fireEvent.click(getByTestId("current-session-toggle-details")); }); // device details are hidden - expect(container.getElementsByClassName('mx_DeviceDetails').length).toBeFalsy(); + expect(container.getElementsByClassName("mx_DeviceDetails").length).toBeFalsy(); }); }); diff --git a/test/components/views/settings/devices/DeviceDetailHeading-test.tsx b/test/components/views/settings/devices/DeviceDetailHeading-test.tsx index 8db3814645..2224e6054b 100644 --- a/test/components/views/settings/devices/DeviceDetailHeading-test.tsx +++ b/test/components/views/settings/devices/DeviceDetailHeading-test.tsx @@ -14,19 +14,19 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import { fireEvent, render, RenderResult } from '@testing-library/react'; +import React from "react"; +import { fireEvent, render, RenderResult } from "@testing-library/react"; -import { DeviceDetailHeading } from '../../../../../src/components/views/settings/devices/DeviceDetailHeading'; -import { flushPromisesWithFakeTimers } from '../../../../test-utils'; -import { DeviceType } from '../../../../../src/utils/device/parseUserAgent'; +import { DeviceDetailHeading } from "../../../../../src/components/views/settings/devices/DeviceDetailHeading"; +import { flushPromisesWithFakeTimers } from "../../../../test-utils"; +import { DeviceType } from "../../../../../src/utils/device/parseUserAgent"; jest.useFakeTimers(); -describe('', () => { +describe("", () => { const device = { - device_id: '123', - display_name: 'My device', + device_id: "123", + display_name: "My device", isVerified: true, deviceType: DeviceType.Unknown, }; @@ -34,104 +34,101 @@ describe('', () => { device, saveDeviceName: jest.fn(), }; - const getComponent = (props = {}) => - ; + const getComponent = (props = {}) => ; - const setInputValue = (getByTestId: RenderResult['getByTestId'], value: string) => { - const input = getByTestId('device-rename-input'); + const setInputValue = (getByTestId: RenderResult["getByTestId"], value: string) => { + const input = getByTestId("device-rename-input"); fireEvent.change(input, { target: { value } }); }; - it('renders device name', () => { + it("renders device name", () => { const { container } = render(getComponent()); expect({ container }).toMatchSnapshot(); }); - it('renders device id as fallback when device has no display name ', () => { - const { getByText } = render(getComponent({ - device: { ...device, display_name: undefined }, - })); + it("renders device id as fallback when device has no display name ", () => { + const { getByText } = render( + getComponent({ + device: { ...device, display_name: undefined }, + }), + ); expect(getByText(device.device_id)).toBeTruthy(); }); - it('displays name edit form on rename button click', () => { + it("displays name edit form on rename button click", () => { const { getByTestId, container } = render(getComponent()); - fireEvent.click(getByTestId('device-heading-rename-cta')); + fireEvent.click(getByTestId("device-heading-rename-cta")); expect({ container }).toMatchSnapshot(); }); - it('cancelling edit switches back to original display', () => { + it("cancelling edit switches back to original display", () => { const { getByTestId, container } = render(getComponent()); // start editing - fireEvent.click(getByTestId('device-heading-rename-cta')); + fireEvent.click(getByTestId("device-heading-rename-cta")); // stop editing - fireEvent.click(getByTestId('device-rename-cancel-cta')); + fireEvent.click(getByTestId("device-rename-cancel-cta")); - expect(container.getElementsByClassName('mx_DeviceDetailHeading').length).toBe(1); + expect(container.getElementsByClassName("mx_DeviceDetailHeading").length).toBe(1); }); - it('clicking submit updates device name with edited value', () => { + it("clicking submit updates device name with edited value", () => { const saveDeviceName = jest.fn(); const { getByTestId } = render(getComponent({ saveDeviceName })); // start editing - fireEvent.click(getByTestId('device-heading-rename-cta')); + fireEvent.click(getByTestId("device-heading-rename-cta")); - setInputValue(getByTestId, 'new device name'); + setInputValue(getByTestId, "new device name"); - fireEvent.click(getByTestId('device-rename-submit-cta')); + fireEvent.click(getByTestId("device-rename-submit-cta")); - expect(saveDeviceName).toHaveBeenCalledWith('new device name'); + expect(saveDeviceName).toHaveBeenCalledWith("new device name"); }); - it('disables form while device name is saving', () => { + it("disables form while device name is saving", () => { const { getByTestId, container } = render(getComponent()); // start editing - fireEvent.click(getByTestId('device-heading-rename-cta')); + fireEvent.click(getByTestId("device-heading-rename-cta")); - setInputValue(getByTestId, 'new device name'); + setInputValue(getByTestId, "new device name"); - fireEvent.click(getByTestId('device-rename-submit-cta')); + fireEvent.click(getByTestId("device-rename-submit-cta")); // buttons disabled - expect( - getByTestId('device-rename-cancel-cta').getAttribute('aria-disabled'), - ).toEqual("true"); - expect( - getByTestId('device-rename-submit-cta').getAttribute('aria-disabled'), - ).toEqual("true"); + expect(getByTestId("device-rename-cancel-cta").getAttribute("aria-disabled")).toEqual("true"); + expect(getByTestId("device-rename-submit-cta").getAttribute("aria-disabled")).toEqual("true"); - expect(container.getElementsByClassName('mx_Spinner').length).toBeTruthy(); + expect(container.getElementsByClassName("mx_Spinner").length).toBeTruthy(); }); - it('toggles out of editing mode when device name is saved successfully', async () => { + it("toggles out of editing mode when device name is saved successfully", async () => { const { getByTestId } = render(getComponent()); // start editing - fireEvent.click(getByTestId('device-heading-rename-cta')); - setInputValue(getByTestId, 'new device name'); - fireEvent.click(getByTestId('device-rename-submit-cta')); + fireEvent.click(getByTestId("device-heading-rename-cta")); + setInputValue(getByTestId, "new device name"); + fireEvent.click(getByTestId("device-rename-submit-cta")); await flushPromisesWithFakeTimers(); // read mode displayed - expect(getByTestId('device-detail-heading')).toBeTruthy(); + expect(getByTestId("device-detail-heading")).toBeTruthy(); }); - it('displays error when device name fails to save', async () => { - const saveDeviceName = jest.fn().mockRejectedValueOnce('oups').mockResolvedValue({}); + it("displays error when device name fails to save", async () => { + const saveDeviceName = jest.fn().mockRejectedValueOnce("oups").mockResolvedValue({}); const { getByTestId, queryByText, container } = render(getComponent({ saveDeviceName })); // start editing - fireEvent.click(getByTestId('device-heading-rename-cta')); - setInputValue(getByTestId, 'new device name'); - fireEvent.click(getByTestId('device-rename-submit-cta')); + fireEvent.click(getByTestId("device-heading-rename-cta")); + setInputValue(getByTestId, "new device name"); + fireEvent.click(getByTestId("device-rename-submit-cta")); // flush promise await flushPromisesWithFakeTimers(); @@ -139,14 +136,14 @@ describe('', () => { await flushPromisesWithFakeTimers(); // error message displayed - expect(queryByText('Failed to set display name')).toBeTruthy(); + expect(queryByText("Failed to set display name")).toBeTruthy(); // spinner removed - expect(container.getElementsByClassName('mx_Spinner').length).toBeFalsy(); + expect(container.getElementsByClassName("mx_Spinner").length).toBeFalsy(); // try again - fireEvent.click(getByTestId('device-rename-submit-cta')); + fireEvent.click(getByTestId("device-rename-submit-cta")); // error message cleared - expect(queryByText('Failed to set display name')).toBeFalsy(); + expect(queryByText("Failed to set display name")).toBeFalsy(); }); }); diff --git a/test/components/views/settings/devices/DeviceDetails-test.tsx b/test/components/views/settings/devices/DeviceDetails-test.tsx index f19f1f58ce..36a7e4e93b 100644 --- a/test/components/views/settings/devices/DeviceDetails-test.tsx +++ b/test/components/views/settings/devices/DeviceDetails-test.tsx @@ -14,17 +14,17 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import { fireEvent, render } from '@testing-library/react'; -import { PUSHER_ENABLED } from 'matrix-js-sdk/src/@types/event'; +import React from "react"; +import { fireEvent, render } from "@testing-library/react"; +import { PUSHER_ENABLED } from "matrix-js-sdk/src/@types/event"; -import DeviceDetails from '../../../../../src/components/views/settings/devices/DeviceDetails'; -import { mkPusher } from '../../../../test-utils/test-utils'; -import { DeviceType } from '../../../../../src/utils/device/parseUserAgent'; +import DeviceDetails from "../../../../../src/components/views/settings/devices/DeviceDetails"; +import { mkPusher } from "../../../../test-utils/test-utils"; +import { DeviceType } from "../../../../../src/utils/device/parseUserAgent"; -describe('', () => { +describe("", () => { const baseDevice = { - device_id: 'my-device', + device_id: "my-device", isVerified: false, deviceType: DeviceType.Unknown, }; @@ -49,27 +49,27 @@ describe('', () => { jest.setSystemTime(now); }); - it('renders device without metadata', () => { + it("renders device without metadata", () => { const { container } = render(getComponent()); expect(container).toMatchSnapshot(); }); - it('renders device with metadata', () => { + it("renders device with metadata", () => { const device = { ...baseDevice, - display_name: 'My Device', - last_seen_ip: '123.456.789', + display_name: "My Device", + last_seen_ip: "123.456.789", last_seen_ts: now - 60000000, - appName: 'Element Web', - client: 'Firefox 100', - deviceModel: 'Iphone X', - deviceOperatingSystem: 'Windows 95', + appName: "Element Web", + client: "Firefox 100", + deviceModel: "Iphone X", + deviceOperatingSystem: "Windows 95", }; const { container } = render(getComponent({ device })); expect(container).toMatchSnapshot(); }); - it('renders a verified device', () => { + it("renders a verified device", () => { const device = { ...baseDevice, isVerified: true, @@ -78,17 +78,15 @@ describe('', () => { expect(container).toMatchSnapshot(); }); - it('disables sign out button while sign out is pending', () => { + it("disables sign out button while sign out is pending", () => { const device = { ...baseDevice, }; const { getByTestId } = render(getComponent({ device, isSigningOut: true })); - expect( - getByTestId('device-detail-sign-out-cta').getAttribute('aria-disabled'), - ).toEqual("true"); + expect(getByTestId("device-detail-sign-out-cta").getAttribute("aria-disabled")).toEqual("true"); }); - it('renders the push notification section when a pusher exists', () => { + it("renders the push notification section when a pusher exists", () => { const device = { ...baseDevice, }; @@ -96,30 +94,34 @@ describe('', () => { device_id: device.device_id, }); - const { getByTestId } = render(getComponent({ - device, - pusher, - isSigningOut: true, - })); + const { getByTestId } = render( + getComponent({ + device, + pusher, + isSigningOut: true, + }), + ); - expect(getByTestId('device-detail-push-notification')).toBeTruthy(); + expect(getByTestId("device-detail-push-notification")).toBeTruthy(); }); - it('hides the push notification section when no pusher', () => { + it("hides the push notification section when no pusher", () => { const device = { ...baseDevice, }; - const { getByTestId } = render(getComponent({ - device, - pusher: null, - isSigningOut: true, - })); + const { getByTestId } = render( + getComponent({ + device, + pusher: null, + isSigningOut: true, + }), + ); - expect(() => getByTestId('device-detail-push-notification')).toThrow(); + expect(() => getByTestId("device-detail-push-notification")).toThrow(); }); - it('disables the checkbox when there is no server support', () => { + it("disables the checkbox when there is no server support", () => { const device = { ...baseDevice, }; @@ -128,20 +130,22 @@ describe('', () => { [PUSHER_ENABLED.name]: false, }); - const { getByTestId } = render(getComponent({ - device, - pusher, - isSigningOut: true, - supportsMSC3881: false, - })); + const { getByTestId } = render( + getComponent({ + device, + pusher, + isSigningOut: true, + supportsMSC3881: false, + }), + ); - const checkbox = getByTestId('device-detail-push-notification-checkbox'); + const checkbox = getByTestId("device-detail-push-notification-checkbox"); - expect(checkbox.getAttribute('aria-disabled')).toEqual("true"); - expect(checkbox.getAttribute('aria-checked')).toEqual("false"); + expect(checkbox.getAttribute("aria-disabled")).toEqual("true"); + expect(checkbox.getAttribute("aria-checked")).toEqual("false"); }); - it('changes the pusher status when clicked', () => { + it("changes the pusher status when clicked", () => { const device = { ...baseDevice, }; @@ -153,35 +157,39 @@ describe('', () => { [PUSHER_ENABLED.name]: enabled, }); - const { getByTestId } = render(getComponent({ - device, - pusher, - isSigningOut: true, - })); + const { getByTestId } = render( + getComponent({ + device, + pusher, + isSigningOut: true, + }), + ); - const checkbox = getByTestId('device-detail-push-notification-checkbox'); + const checkbox = getByTestId("device-detail-push-notification-checkbox"); fireEvent.click(checkbox); expect(defaultProps.setPushNotifications).toHaveBeenCalledWith(device.device_id, !enabled); }); - it('changes the local notifications settings status when clicked', () => { + it("changes the local notifications settings status when clicked", () => { const device = { ...baseDevice, }; const enabled = false; - const { getByTestId } = render(getComponent({ - device, - localNotificationSettings: { - is_silenced: !enabled, - }, - isSigningOut: true, - })); + const { getByTestId } = render( + getComponent({ + device, + localNotificationSettings: { + is_silenced: !enabled, + }, + isSigningOut: true, + }), + ); - const checkbox = getByTestId('device-detail-push-notification-checkbox'); + const checkbox = getByTestId("device-detail-push-notification-checkbox"); fireEvent.click(checkbox); expect(defaultProps.setPushNotifications).toHaveBeenCalledWith(device.device_id, !enabled); diff --git a/test/components/views/settings/devices/DeviceExpandDetailsButton-test.tsx b/test/components/views/settings/devices/DeviceExpandDetailsButton-test.tsx index 310397469b..45253d6c15 100644 --- a/test/components/views/settings/devices/DeviceExpandDetailsButton-test.tsx +++ b/test/components/views/settings/devices/DeviceExpandDetailsButton-test.tsx @@ -14,35 +14,32 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import { fireEvent, render } from '@testing-library/react'; +import React from "react"; +import { fireEvent, render } from "@testing-library/react"; -import { - DeviceExpandDetailsButton, -} from '../../../../../src/components/views/settings/devices/DeviceExpandDetailsButton'; +import { DeviceExpandDetailsButton } from "../../../../../src/components/views/settings/devices/DeviceExpandDetailsButton"; -describe('', () => { +describe("", () => { const defaultProps = { isExpanded: false, onClick: jest.fn(), }; - const getComponent = (props = {}) => - ; + const getComponent = (props = {}) => ; - it('renders when not expanded', () => { + it("renders when not expanded", () => { const { container } = render(getComponent()); expect({ container }).toMatchSnapshot(); }); - it('renders when expanded', () => { + it("renders when expanded", () => { const { container } = render(getComponent({ isExpanded: true })); expect({ container }).toMatchSnapshot(); }); - it('calls onClick', () => { + it("calls onClick", () => { const onClick = jest.fn(); - const { getByTestId } = render(getComponent({ 'data-testid': 'test', onClick })); - fireEvent.click(getByTestId('test')); + const { getByTestId } = render(getComponent({ "data-testid": "test", onClick })); + fireEvent.click(getByTestId("test")); expect(onClick).toHaveBeenCalled(); }); diff --git a/test/components/views/settings/devices/DeviceSecurityCard-test.tsx b/test/components/views/settings/devices/DeviceSecurityCard-test.tsx index 43bf025eba..d8e313d08c 100644 --- a/test/components/views/settings/devices/DeviceSecurityCard-test.tsx +++ b/test/components/views/settings/devices/DeviceSecurityCard-test.tsx @@ -14,31 +14,32 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { render } from '@testing-library/react'; -import React from 'react'; +import { render } from "@testing-library/react"; +import React from "react"; -import DeviceSecurityCard from '../../../../../src/components/views/settings/devices/DeviceSecurityCard'; -import { DeviceSecurityVariation } from '../../../../../src/components/views/settings/devices/types'; +import DeviceSecurityCard from "../../../../../src/components/views/settings/devices/DeviceSecurityCard"; +import { DeviceSecurityVariation } from "../../../../../src/components/views/settings/devices/types"; -describe('', () => { +describe("", () => { const defaultProps = { variation: DeviceSecurityVariation.Verified, - heading: 'Verified session', - description: 'nice', + heading: "Verified session", + description: "nice", }; - const getComponent = (props = {}): React.ReactElement => - ; + const getComponent = (props = {}): React.ReactElement => ; - it('renders basic card', () => { + it("renders basic card", () => { const { container } = render(getComponent()); expect(container).toMatchSnapshot(); }); - it('renders with children', () => { - const { container } = render(getComponent({ - children:
hey
, - variation: DeviceSecurityVariation.Unverified, - })); + it("renders with children", () => { + const { container } = render( + getComponent({ + children:
hey
, + variation: DeviceSecurityVariation.Unverified, + }), + ); expect(container).toMatchSnapshot(); }); }); diff --git a/test/components/views/settings/devices/DeviceTile-test.tsx b/test/components/views/settings/devices/DeviceTile-test.tsx index 6651630732..d905248f8f 100644 --- a/test/components/views/settings/devices/DeviceTile-test.tsx +++ b/test/components/views/settings/devices/DeviceTile-test.tsx @@ -14,24 +14,22 @@ 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 { IMyDevice } from 'matrix-js-sdk/src/matrix'; +import React from "react"; +import { render } from "@testing-library/react"; +import { IMyDevice } from "matrix-js-sdk/src/matrix"; -import DeviceTile from '../../../../../src/components/views/settings/devices/DeviceTile'; -import { DeviceType } from '../../../../../src/utils/device/parseUserAgent'; +import DeviceTile from "../../../../../src/components/views/settings/devices/DeviceTile"; +import { DeviceType } from "../../../../../src/utils/device/parseUserAgent"; -describe('', () => { +describe("", () => { const defaultProps = { device: { - device_id: '123', + device_id: "123", isVerified: false, deviceType: DeviceType.Unknown, }, }; - const getComponent = (props = {}) => ( - - ); + const getComponent = (props = {}) => ; // 14.03.2022 16:15 const now = 1647270879403; @@ -41,96 +39,94 @@ describe('', () => { jest.setSystemTime(now); }); - it('renders a device with no metadata', () => { + it("renders a device with no metadata", () => { const { container } = render(getComponent()); expect(container).toMatchSnapshot(); }); - it('applies interactive class when tile has click handler', () => { + it("applies interactive class when tile has click handler", () => { const onClick = jest.fn(); const { getByTestId } = render(getComponent({ onClick })); - expect( - getByTestId('device-tile-123').className.includes('mx_DeviceTile_interactive'), - ).toBeTruthy(); + expect(getByTestId("device-tile-123").className.includes("mx_DeviceTile_interactive")).toBeTruthy(); }); - it('renders a verified device with no metadata', () => { + it("renders a verified device with no metadata", () => { const { container } = render(getComponent()); expect(container).toMatchSnapshot(); }); - it('renders display name with a tooltip', () => { + it("renders display name with a tooltip", () => { const device: IMyDevice = { - device_id: '123', - display_name: 'My device', + device_id: "123", + display_name: "My device", }; const { container } = render(getComponent({ device })); expect(container).toMatchSnapshot(); }); - it('renders last seen ip metadata', () => { + it("renders last seen ip metadata", () => { const device: IMyDevice = { - device_id: '123', - display_name: 'My device', - last_seen_ip: '1.2.3.4', + device_id: "123", + display_name: "My device", + last_seen_ip: "1.2.3.4", }; const { getByTestId } = render(getComponent({ device })); - expect(getByTestId('device-metadata-lastSeenIp').textContent).toEqual(device.last_seen_ip); + expect(getByTestId("device-metadata-lastSeenIp").textContent).toEqual(device.last_seen_ip); }); - it('separates metadata with a dot', () => { + it("separates metadata with a dot", () => { const device: IMyDevice = { - device_id: '123', - last_seen_ip: '1.2.3.4', + device_id: "123", + last_seen_ip: "1.2.3.4", last_seen_ts: now - 60000, }; const { container } = render(getComponent({ device })); expect(container).toMatchSnapshot(); }); - describe('Last activity', () => { + describe("Last activity", () => { const MS_DAY = 24 * 60 * 60 * 1000; - it('renders with day of week and time when last activity is less than 6 days ago', () => { + it("renders with day of week and time when last activity is less than 6 days ago", () => { const device: IMyDevice = { - device_id: '123', - last_seen_ip: '1.2.3.4', - last_seen_ts: now - (MS_DAY * 3), + device_id: "123", + last_seen_ip: "1.2.3.4", + last_seen_ts: now - MS_DAY * 3, }; const { getByTestId } = render(getComponent({ device })); - expect(getByTestId('device-metadata-lastActivity').textContent).toEqual('Last activity Fri 15:14'); + expect(getByTestId("device-metadata-lastActivity").textContent).toEqual("Last activity Fri 15:14"); }); - it('renders with month and date when last activity is more than 6 days ago', () => { + it("renders with month and date when last activity is more than 6 days ago", () => { const device: IMyDevice = { - device_id: '123', - last_seen_ip: '1.2.3.4', - last_seen_ts: now - (MS_DAY * 8), + device_id: "123", + last_seen_ip: "1.2.3.4", + last_seen_ts: now - MS_DAY * 8, }; const { getByTestId } = render(getComponent({ device })); - expect(getByTestId('device-metadata-lastActivity').textContent).toEqual('Last activity Mar 6'); + expect(getByTestId("device-metadata-lastActivity").textContent).toEqual("Last activity Mar 6"); }); - it('renders with month, date, year when activity is in a different calendar year', () => { + it("renders with month, date, year when activity is in a different calendar year", () => { const device: IMyDevice = { - device_id: '123', - last_seen_ip: '1.2.3.4', - last_seen_ts: new Date('2021-12-29').getTime(), + device_id: "123", + last_seen_ip: "1.2.3.4", + last_seen_ts: new Date("2021-12-29").getTime(), }; const { getByTestId } = render(getComponent({ device })); - expect(getByTestId('device-metadata-lastActivity').textContent).toEqual('Last activity Dec 29, 2021'); + expect(getByTestId("device-metadata-lastActivity").textContent).toEqual("Last activity Dec 29, 2021"); }); - it('renders with inactive notice when last activity was more than 90 days ago', () => { + it("renders with inactive notice when last activity was more than 90 days ago", () => { const device: IMyDevice = { - device_id: '123', - last_seen_ip: '1.2.3.4', - last_seen_ts: now - (MS_DAY * 100), + device_id: "123", + last_seen_ip: "1.2.3.4", + last_seen_ts: now - MS_DAY * 100, }; const { getByTestId, queryByTestId } = render(getComponent({ device })); - expect(getByTestId('device-metadata-inactive').textContent).toEqual('Inactive for 90+ days (Dec 4, 2021)'); + expect(getByTestId("device-metadata-inactive").textContent).toEqual("Inactive for 90+ days (Dec 4, 2021)"); // last activity and verification not shown when inactive - expect(queryByTestId('device-metadata-lastActivity')).toBeFalsy(); - expect(queryByTestId('device-metadata-verificationStatus')).toBeFalsy(); + expect(queryByTestId("device-metadata-lastActivity")).toBeFalsy(); + expect(queryByTestId("device-metadata-verificationStatus")).toBeFalsy(); }); }); }); diff --git a/test/components/views/settings/devices/DeviceTypeIcon-test.tsx b/test/components/views/settings/devices/DeviceTypeIcon-test.tsx index 9d11a7e659..417f1cd27b 100644 --- a/test/components/views/settings/devices/DeviceTypeIcon-test.tsx +++ b/test/components/views/settings/devices/DeviceTypeIcon-test.tsx @@ -14,61 +14,60 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { render } from '@testing-library/react'; -import React from 'react'; +import { render } from "@testing-library/react"; +import React from "react"; -import { DeviceTypeIcon } from '../../../../../src/components/views/settings/devices/DeviceTypeIcon'; -import { DeviceType } from '../../../../../src/utils/device/parseUserAgent'; +import { DeviceTypeIcon } from "../../../../../src/components/views/settings/devices/DeviceTypeIcon"; +import { DeviceType } from "../../../../../src/utils/device/parseUserAgent"; -describe('', () => { +describe("", () => { const defaultProps = { isVerified: false, isSelected: false, }; - const getComponent = (props = {}) => - ; + const getComponent = (props = {}) => ; - it('renders an unverified device', () => { + it("renders an unverified device", () => { const { container } = render(getComponent()); expect(container).toMatchSnapshot(); }); - it('renders a verified device', () => { + it("renders a verified device", () => { const { container } = render(getComponent({ isVerified: true })); expect(container).toMatchSnapshot(); }); - it('renders correctly when selected', () => { + it("renders correctly when selected", () => { const { container } = render(getComponent({ isSelected: true })); expect(container).toMatchSnapshot(); }); - it('renders an unknown device icon when no device type given', () => { + it("renders an unknown device icon when no device type given", () => { const { getByLabelText } = render(getComponent()); - expect(getByLabelText('Unknown session type')).toBeTruthy(); + expect(getByLabelText("Unknown session type")).toBeTruthy(); }); - it('renders a desktop device type', () => { + it("renders a desktop device type", () => { const deviceType = DeviceType.Desktop; const { getByLabelText } = render(getComponent({ deviceType })); - expect(getByLabelText('Desktop session')).toBeTruthy(); + expect(getByLabelText("Desktop session")).toBeTruthy(); }); - it('renders a web device type', () => { + it("renders a web device type", () => { const deviceType = DeviceType.Web; const { getByLabelText } = render(getComponent({ deviceType })); - expect(getByLabelText('Web session')).toBeTruthy(); + expect(getByLabelText("Web session")).toBeTruthy(); }); - it('renders a mobile device type', () => { + it("renders a mobile device type", () => { const deviceType = DeviceType.Mobile; const { getByLabelText } = render(getComponent({ deviceType })); - expect(getByLabelText('Mobile session')).toBeTruthy(); + expect(getByLabelText("Mobile session")).toBeTruthy(); }); - it('renders an unknown device type', () => { + it("renders an unknown device type", () => { const deviceType = DeviceType.Unknown; const { getByLabelText } = render(getComponent({ deviceType })); - expect(getByLabelText('Unknown session type')).toBeTruthy(); + expect(getByLabelText("Unknown session type")).toBeTruthy(); }); }); diff --git a/test/components/views/settings/devices/FilteredDeviceList-test.tsx b/test/components/views/settings/devices/FilteredDeviceList-test.tsx index e2f9c1bd5f..b8dc13a930 100644 --- a/test/components/views/settings/devices/FilteredDeviceList-test.tsx +++ b/test/components/views/settings/devices/FilteredDeviceList-test.tsx @@ -14,43 +14,46 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import { act, fireEvent, render } from '@testing-library/react'; +import React from "react"; +import { act, fireEvent, render } from "@testing-library/react"; -import { FilteredDeviceList } from '../../../../../src/components/views/settings/devices/FilteredDeviceList'; -import { DeviceSecurityVariation } from '../../../../../src/components/views/settings/devices/types'; -import { flushPromises, mockPlatformPeg } from '../../../../test-utils'; -import { DeviceType } from '../../../../../src/utils/device/parseUserAgent'; +import { FilteredDeviceList } from "../../../../../src/components/views/settings/devices/FilteredDeviceList"; +import { DeviceSecurityVariation } from "../../../../../src/components/views/settings/devices/types"; +import { flushPromises, mockPlatformPeg } from "../../../../test-utils"; +import { DeviceType } from "../../../../../src/utils/device/parseUserAgent"; mockPlatformPeg(); const MS_DAY = 86400000; -describe('', () => { +describe("", () => { const newDevice = { - device_id: 'new', + device_id: "new", last_seen_ts: Date.now() - 500, - last_seen_ip: '123.456.789', - display_name: 'My Device', + last_seen_ip: "123.456.789", + display_name: "My Device", isVerified: true, deviceType: DeviceType.Unknown, }; const unverifiedNoMetadata = { - device_id: 'unverified-no-metadata', + device_id: "unverified-no-metadata", isVerified: false, - deviceType: DeviceType.Unknown }; + deviceType: DeviceType.Unknown, + }; const verifiedNoMetadata = { - device_id: 'verified-no-metadata', + device_id: "verified-no-metadata", isVerified: true, - deviceType: DeviceType.Unknown }; + deviceType: DeviceType.Unknown, + }; const hundredDaysOld = { - device_id: '100-days-old', + device_id: "100-days-old", isVerified: true, - last_seen_ts: Date.now() - (MS_DAY * 100), - deviceType: DeviceType.Unknown }; + last_seen_ts: Date.now() - MS_DAY * 100, + deviceType: DeviceType.Unknown, + }; const hundredDaysOldUnverified = { - device_id: 'unverified-100-days-old', + device_id: "unverified-100-days-old", isVerified: false, - last_seen_ts: Date.now() - (MS_DAY * 100), + last_seen_ts: Date.now() - MS_DAY * 100, deviceType: DeviceType.Unknown, }; const defaultProps = { @@ -76,20 +79,19 @@ describe('', () => { supportsMSC3881: true, }; - const getComponent = (props = {}) => - (); + const getComponent = (props = {}) => ; - it('renders devices in correct order', () => { + it("renders devices in correct order", () => { const { container } = render(getComponent()); - const tiles = container.querySelectorAll('.mx_DeviceTile'); - expect(tiles[0].getAttribute('data-testid')).toEqual(`device-tile-${newDevice.device_id}`); - expect(tiles[1].getAttribute('data-testid')).toEqual(`device-tile-${hundredDaysOld.device_id}`); - expect(tiles[2].getAttribute('data-testid')).toEqual(`device-tile-${hundredDaysOldUnverified.device_id}`); - expect(tiles[3].getAttribute('data-testid')).toEqual(`device-tile-${unverifiedNoMetadata.device_id}`); - expect(tiles[4].getAttribute('data-testid')).toEqual(`device-tile-${verifiedNoMetadata.device_id}`); + const tiles = container.querySelectorAll(".mx_DeviceTile"); + expect(tiles[0].getAttribute("data-testid")).toEqual(`device-tile-${newDevice.device_id}`); + expect(tiles[1].getAttribute("data-testid")).toEqual(`device-tile-${hundredDaysOld.device_id}`); + expect(tiles[2].getAttribute("data-testid")).toEqual(`device-tile-${hundredDaysOldUnverified.device_id}`); + expect(tiles[3].getAttribute("data-testid")).toEqual(`device-tile-${unverifiedNoMetadata.device_id}`); + expect(tiles[4].getAttribute("data-testid")).toEqual(`device-tile-${verifiedNoMetadata.device_id}`); }); - it('updates list order when devices change', () => { + it("updates list order when devices change", () => { const updatedOldDevice = { ...hundredDaysOld, last_seen_ts: new Date().getTime() }; const updatedDevices = { [hundredDaysOld.device_id]: updatedOldDevice, @@ -99,58 +101,56 @@ describe('', () => { rerender(getComponent({ devices: updatedDevices })); - const tiles = container.querySelectorAll('.mx_DeviceTile'); + const tiles = container.querySelectorAll(".mx_DeviceTile"); expect(tiles.length).toBe(2); - expect(tiles[0].getAttribute('data-testid')).toEqual(`device-tile-${hundredDaysOld.device_id}`); - expect(tiles[1].getAttribute('data-testid')).toEqual(`device-tile-${newDevice.device_id}`); + expect(tiles[0].getAttribute("data-testid")).toEqual(`device-tile-${hundredDaysOld.device_id}`); + expect(tiles[1].getAttribute("data-testid")).toEqual(`device-tile-${newDevice.device_id}`); }); - it('displays no results message when there are no devices', () => { + it("displays no results message when there are no devices", () => { const { container } = render(getComponent({ devices: {} })); - expect(container.getElementsByClassName('mx_FilteredDeviceList_noResults')).toMatchSnapshot(); + expect(container.getElementsByClassName("mx_FilteredDeviceList_noResults")).toMatchSnapshot(); }); - describe('filtering', () => { - const setFilter = async ( - container: HTMLElement, - option: DeviceSecurityVariation | string, - ) => await act(async () => { - const dropdown = container.querySelector('[aria-label="Filter devices"]'); + describe("filtering", () => { + const setFilter = async (container: HTMLElement, option: DeviceSecurityVariation | string) => + await act(async () => { + const dropdown = container.querySelector('[aria-label="Filter devices"]'); - fireEvent.click(dropdown as Element); - // tick to let dropdown render - await flushPromises(); + fireEvent.click(dropdown as Element); + // tick to let dropdown render + await flushPromises(); - fireEvent.click(container.querySelector(`#device-list-filter__${option}`) as Element); - }); + fireEvent.click(container.querySelector(`#device-list-filter__${option}`) as Element); + }); - it('does not display filter description when filter is falsy', () => { + it("does not display filter description when filter is falsy", () => { const { container } = render(getComponent({ filter: undefined })); - const tiles = container.querySelectorAll('.mx_DeviceTile'); - expect(container.getElementsByClassName('mx_FilteredDeviceList_securityCard').length).toBeFalsy(); + const tiles = container.querySelectorAll(".mx_DeviceTile"); + expect(container.getElementsByClassName("mx_FilteredDeviceList_securityCard").length).toBeFalsy(); expect(tiles.length).toEqual(5); }); - it('updates filter when prop changes', () => { + it("updates filter when prop changes", () => { const { container, rerender } = render(getComponent({ filter: DeviceSecurityVariation.Verified })); - const tiles = container.querySelectorAll('.mx_DeviceTile'); + const tiles = container.querySelectorAll(".mx_DeviceTile"); expect(tiles.length).toEqual(3); - expect(tiles[0].getAttribute('data-testid')).toEqual(`device-tile-${newDevice.device_id}`); - expect(tiles[1].getAttribute('data-testid')).toEqual(`device-tile-${hundredDaysOld.device_id}`); - expect(tiles[2].getAttribute('data-testid')).toEqual(`device-tile-${verifiedNoMetadata.device_id}`); + expect(tiles[0].getAttribute("data-testid")).toEqual(`device-tile-${newDevice.device_id}`); + expect(tiles[1].getAttribute("data-testid")).toEqual(`device-tile-${hundredDaysOld.device_id}`); + expect(tiles[2].getAttribute("data-testid")).toEqual(`device-tile-${verifiedNoMetadata.device_id}`); rerender(getComponent({ filter: DeviceSecurityVariation.Inactive })); - const rerenderedTiles = container.querySelectorAll('.mx_DeviceTile'); + const rerenderedTiles = container.querySelectorAll(".mx_DeviceTile"); expect(rerenderedTiles.length).toEqual(2); - expect(rerenderedTiles[0].getAttribute('data-testid')).toEqual(`device-tile-${hundredDaysOld.device_id}`); - expect(rerenderedTiles[1].getAttribute('data-testid')).toEqual( + expect(rerenderedTiles[0].getAttribute("data-testid")).toEqual(`device-tile-${hundredDaysOld.device_id}`); + expect(rerenderedTiles[1].getAttribute("data-testid")).toEqual( `device-tile-${hundredDaysOldUnverified.device_id}`, ); }); - it('calls onFilterChange handler', async () => { + it("calls onFilterChange handler", async () => { const onFilterChange = jest.fn(); const { container } = render(getComponent({ onFilterChange })); await setFilter(container, DeviceSecurityVariation.Verified); @@ -158,10 +158,10 @@ describe('', () => { expect(onFilterChange).toHaveBeenCalledWith(DeviceSecurityVariation.Verified); }); - it('calls onFilterChange handler correctly when setting filter to All', async () => { + it("calls onFilterChange handler correctly when setting filter to All", async () => { const onFilterChange = jest.fn(); const { container } = render(getComponent({ onFilterChange, filter: DeviceSecurityVariation.Verified })); - await setFilter(container, 'ALL'); + await setFilter(container, "ALL"); // filter is cleared expect(onFilterChange).toHaveBeenCalledWith(undefined); @@ -171,51 +171,54 @@ describe('', () => { [DeviceSecurityVariation.Verified, [newDevice, hundredDaysOld, verifiedNoMetadata]], [DeviceSecurityVariation.Unverified, [hundredDaysOldUnverified, unverifiedNoMetadata]], [DeviceSecurityVariation.Inactive, [hundredDaysOld, hundredDaysOldUnverified]], - ])('filters correctly for %s', (filter, expectedDevices) => { + ])("filters correctly for %s", (filter, expectedDevices) => { const { container } = render(getComponent({ filter })); - expect(container.getElementsByClassName('mx_FilteredDeviceList_securityCard')).toMatchSnapshot(); - const tileDeviceIds = [...container.querySelectorAll('.mx_DeviceTile')] - .map(tile => tile.getAttribute('data-testid')); - expect(tileDeviceIds).toEqual(expectedDevices.map(device => `device-tile-${device.device_id}`)); + expect(container.getElementsByClassName("mx_FilteredDeviceList_securityCard")).toMatchSnapshot(); + const tileDeviceIds = [...container.querySelectorAll(".mx_DeviceTile")].map((tile) => + tile.getAttribute("data-testid"), + ); + expect(tileDeviceIds).toEqual(expectedDevices.map((device) => `device-tile-${device.device_id}`)); }); it.each([ [DeviceSecurityVariation.Verified], [DeviceSecurityVariation.Unverified], [DeviceSecurityVariation.Inactive], - ])('renders no results correctly for %s', (filter) => { + ])("renders no results correctly for %s", (filter) => { const { container } = render(getComponent({ filter, devices: {} })); - expect(container.getElementsByClassName('mx_FilteredDeviceList_securityCard').length).toBeFalsy(); - expect(container.getElementsByClassName('mx_FilteredDeviceList_noResults')).toMatchSnapshot(); + expect(container.getElementsByClassName("mx_FilteredDeviceList_securityCard").length).toBeFalsy(); + expect(container.getElementsByClassName("mx_FilteredDeviceList_noResults")).toMatchSnapshot(); }); - it('clears filter from no results message', () => { + it("clears filter from no results message", () => { const onFilterChange = jest.fn(); - const { getByTestId } = render(getComponent({ - onFilterChange, - filter: DeviceSecurityVariation.Verified, - devices: { - [unverifiedNoMetadata.device_id]: unverifiedNoMetadata, - }, - })); + const { getByTestId } = render( + getComponent({ + onFilterChange, + filter: DeviceSecurityVariation.Verified, + devices: { + [unverifiedNoMetadata.device_id]: unverifiedNoMetadata, + }, + }), + ); act(() => { - fireEvent.click(getByTestId('devices-clear-filter-btn')); + fireEvent.click(getByTestId("devices-clear-filter-btn")); }); expect(onFilterChange).toHaveBeenCalledWith(undefined); }); }); - describe('device details', () => { - it('renders expanded devices with device details', () => { + describe("device details", () => { + it("renders expanded devices with device details", () => { const expandedDeviceIds = [newDevice.device_id, hundredDaysOld.device_id]; const { container, getByTestId } = render(getComponent({ expandedDeviceIds })); - expect(container.getElementsByClassName('mx_DeviceDetails').length).toBeTruthy(); + expect(container.getElementsByClassName("mx_DeviceDetails").length).toBeTruthy(); expect(getByTestId(`device-detail-${newDevice.device_id}`)).toBeTruthy(); expect(getByTestId(`device-detail-${hundredDaysOld.device_id}`)).toBeTruthy(); }); - it('clicking toggle calls onDeviceExpandToggle', () => { + it("clicking toggle calls onDeviceExpandToggle", () => { const onDeviceExpandToggle = jest.fn(); const { getByTestId } = render(getComponent({ onDeviceExpandToggle })); diff --git a/test/components/views/settings/devices/FilteredDeviceListHeader-test.tsx b/test/components/views/settings/devices/FilteredDeviceListHeader-test.tsx index 49e8d9a861..05380493d3 100644 --- a/test/components/views/settings/devices/FilteredDeviceListHeader-test.tsx +++ b/test/components/views/settings/devices/FilteredDeviceListHeader-test.tsx @@ -14,40 +14,40 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { fireEvent, render } from '@testing-library/react'; -import React from 'react'; +import { fireEvent, render } from "@testing-library/react"; +import React from "react"; -import FilteredDeviceListHeader from '../../../../../src/components/views/settings/devices/FilteredDeviceListHeader'; +import FilteredDeviceListHeader from "../../../../../src/components/views/settings/devices/FilteredDeviceListHeader"; -describe('', () => { +describe("", () => { const defaultProps = { selectedDeviceCount: 0, isAllSelected: false, toggleSelectAll: jest.fn(), children:
test
, - ['data-testid']: 'test123', + ["data-testid"]: "test123", }; - const getComponent = (props = {}) => (); + const getComponent = (props = {}) => ; - it('renders correctly when no devices are selected', () => { + it("renders correctly when no devices are selected", () => { const { container } = render(getComponent()); expect(container).toMatchSnapshot(); }); - it('renders correctly when all devices are selected', () => { + it("renders correctly when all devices are selected", () => { const { container } = render(getComponent({ isAllSelected: true })); expect(container).toMatchSnapshot(); }); - it('renders correctly when some devices are selected', () => { + it("renders correctly when some devices are selected", () => { const { getByText } = render(getComponent({ selectedDeviceCount: 2 })); - expect(getByText('2 sessions selected')).toBeTruthy(); + expect(getByText("2 sessions selected")).toBeTruthy(); }); - it('clicking checkbox toggles selection', () => { + it("clicking checkbox toggles selection", () => { const toggleSelectAll = jest.fn(); const { getByTestId } = render(getComponent({ toggleSelectAll })); - fireEvent.click(getByTestId('device-select-all-checkbox')); + fireEvent.click(getByTestId("device-select-all-checkbox")); expect(toggleSelectAll).toHaveBeenCalled(); }); diff --git a/test/components/views/settings/devices/LoginWithQR-test.tsx b/test/components/views/settings/devices/LoginWithQR-test.tsx index a3871e34c4..6d8a153545 100644 --- a/test/components/views/settings/devices/LoginWithQR-test.tsx +++ b/test/components/views/settings/devices/LoginWithQR-test.tsx @@ -14,21 +14,21 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { cleanup, render, waitFor } from '@testing-library/react'; -import { mocked } from 'jest-mock'; -import React from 'react'; -import { MSC3906Rendezvous, RendezvousFailureReason } from 'matrix-js-sdk/src/rendezvous'; +import { cleanup, render, waitFor } from "@testing-library/react"; +import { mocked } from "jest-mock"; +import React from "react"; +import { MSC3906Rendezvous, RendezvousFailureReason } from "matrix-js-sdk/src/rendezvous"; -import LoginWithQR, { Click, Mode, Phase } from '../../../../../src/components/views/auth/LoginWithQR'; -import type { MatrixClient } from 'matrix-js-sdk/src/matrix'; +import LoginWithQR, { Click, Mode, Phase } from "../../../../../src/components/views/auth/LoginWithQR"; +import type { MatrixClient } from "matrix-js-sdk/src/matrix"; -jest.mock('matrix-js-sdk/src/rendezvous'); -jest.mock('matrix-js-sdk/src/rendezvous/transports'); -jest.mock('matrix-js-sdk/src/rendezvous/channels'); +jest.mock("matrix-js-sdk/src/rendezvous"); +jest.mock("matrix-js-sdk/src/rendezvous/transports"); +jest.mock("matrix-js-sdk/src/rendezvous/channels"); const mockedFlow = jest.fn(); -jest.mock('../../../../../src/components/views/auth/LoginWithQRFlow', () => (props) => { +jest.mock("../../../../../src/components/views/auth/LoginWithQRFlow", () => (props) => { mockedFlow(props); return
; }); @@ -43,7 +43,7 @@ function makeClient() { on: jest.fn(), isSynapseAdministrator: jest.fn().mockResolvedValue(false), isRoomEncrypted: jest.fn().mockReturnValue(false), - mxcUrlToHttp: jest.fn().mockReturnValue('mock-mxcUrlToHttp'), + mxcUrlToHttp: jest.fn().mockReturnValue("mock-mxcUrlToHttp"), doesServerSupportUnstableFeature: jest.fn().mockReturnValue(true), removeListener: jest.fn(), requestLoginToken: jest.fn(), @@ -57,33 +57,36 @@ function unresolvedPromise(): Promise { return new Promise(() => {}); } -describe('', () => { +describe("", () => { let client = makeClient(); const defaultProps = { mode: Mode.Show, onFinished: jest.fn(), }; - const mockConfirmationDigits = 'mock-confirmation-digits'; - const mockRendezvousCode = 'mock-rendezvous-code'; - const newDeviceId = 'new-device-id'; + const mockConfirmationDigits = "mock-confirmation-digits"; + const mockRendezvousCode = "mock-rendezvous-code"; + const newDeviceId = "new-device-id"; - const getComponent = (props: { client: MatrixClient, onFinished?: () => void }) => - (); + const getComponent = (props: { client: MatrixClient; onFinished?: () => void }) => ( + + + + ); beforeEach(() => { mockedFlow.mockReset(); jest.resetAllMocks(); - jest.spyOn(MSC3906Rendezvous.prototype, 'generateCode').mockResolvedValue(); + jest.spyOn(MSC3906Rendezvous.prototype, "generateCode").mockResolvedValue(); // @ts-ignore // workaround for https://github.com/facebook/jest/issues/9675 MSC3906Rendezvous.prototype.code = mockRendezvousCode; - jest.spyOn(MSC3906Rendezvous.prototype, 'cancel').mockResolvedValue(); - jest.spyOn(MSC3906Rendezvous.prototype, 'startAfterShowingCode').mockResolvedValue(mockConfirmationDigits); - jest.spyOn(MSC3906Rendezvous.prototype, 'declineLoginOnExistingDevice').mockResolvedValue(); - jest.spyOn(MSC3906Rendezvous.prototype, 'approveLoginOnExistingDevice').mockResolvedValue(newDeviceId); - jest.spyOn(MSC3906Rendezvous.prototype, 'verifyNewDeviceOnExistingDevice').mockResolvedValue(undefined); + jest.spyOn(MSC3906Rendezvous.prototype, "cancel").mockResolvedValue(); + jest.spyOn(MSC3906Rendezvous.prototype, "startAfterShowingCode").mockResolvedValue(mockConfirmationDigits); + jest.spyOn(MSC3906Rendezvous.prototype, "declineLoginOnExistingDevice").mockResolvedValue(); + jest.spyOn(MSC3906Rendezvous.prototype, "approveLoginOnExistingDevice").mockResolvedValue(newDeviceId); + jest.spyOn(MSC3906Rendezvous.prototype, "verifyNewDeviceOnExistingDevice").mockResolvedValue(undefined); client.requestLoginToken.mockResolvedValue({ - login_token: 'token', + login_token: "token", expires_in: 1000, }); }); @@ -95,9 +98,9 @@ describe('', () => { cleanup(); }); - test('no homeserver support', async () => { + test("no homeserver support", async () => { // simulate no support - jest.spyOn(MSC3906Rendezvous.prototype, 'generateCode').mockRejectedValue(''); + jest.spyOn(MSC3906Rendezvous.prototype, "generateCode").mockRejectedValue(""); render(getComponent({ client })); await waitFor(() => expect(mockedFlow).toHaveBeenLastCalledWith({ @@ -110,8 +113,8 @@ describe('', () => { expect(rendezvous.generateCode).toHaveBeenCalled(); }); - test('failed to connect', async () => { - jest.spyOn(MSC3906Rendezvous.prototype, 'startAfterShowingCode').mockRejectedValue(''); + test("failed to connect", async () => { + jest.spyOn(MSC3906Rendezvous.prototype, "startAfterShowingCode").mockRejectedValue(""); render(getComponent({ client })); await waitFor(() => expect(mockedFlow).toHaveBeenLastCalledWith({ @@ -125,15 +128,19 @@ describe('', () => { expect(rendezvous.startAfterShowingCode).toHaveBeenCalled(); }); - test('render QR then cancel and try again', async () => { + test("render QR then cancel and try again", async () => { const onFinished = jest.fn(); - jest.spyOn(MSC3906Rendezvous.prototype, 'startAfterShowingCode').mockImplementation(() => unresolvedPromise()); + jest.spyOn(MSC3906Rendezvous.prototype, "startAfterShowingCode").mockImplementation(() => unresolvedPromise()); render(getComponent({ client, onFinished })); const rendezvous = mocked(MSC3906Rendezvous).mock.instances[0]; - await waitFor(() => expect(mockedFlow).toHaveBeenLastCalledWith(expect.objectContaining({ - phase: Phase.ShowingQR, - }))); + await waitFor(() => + expect(mockedFlow).toHaveBeenLastCalledWith( + expect.objectContaining({ + phase: Phase.ShowingQR, + }), + ), + ); // display QR code expect(mockedFlow).toHaveBeenLastCalledWith({ phase: Phase.ShowingQR, @@ -151,9 +158,13 @@ describe('', () => { // try again onClick(Click.TryAgain); - await waitFor(() => expect(mockedFlow).toHaveBeenLastCalledWith(expect.objectContaining({ - phase: Phase.ShowingQR, - }))); + await waitFor(() => + expect(mockedFlow).toHaveBeenLastCalledWith( + expect.objectContaining({ + phase: Phase.ShowingQR, + }), + ), + ); // display QR code expect(mockedFlow).toHaveBeenLastCalledWith({ phase: Phase.ShowingQR, @@ -162,15 +173,19 @@ describe('', () => { }); }); - test('render QR then back', async () => { + test("render QR then back", async () => { const onFinished = jest.fn(); - jest.spyOn(MSC3906Rendezvous.prototype, 'startAfterShowingCode').mockReturnValue(unresolvedPromise()); + jest.spyOn(MSC3906Rendezvous.prototype, "startAfterShowingCode").mockReturnValue(unresolvedPromise()); render(getComponent({ client, onFinished })); const rendezvous = mocked(MSC3906Rendezvous).mock.instances[0]; - await waitFor(() => expect(mockedFlow).toHaveBeenLastCalledWith(expect.objectContaining({ - phase: Phase.ShowingQR, - }))); + await waitFor(() => + expect(mockedFlow).toHaveBeenLastCalledWith( + expect.objectContaining({ + phase: Phase.ShowingQR, + }), + ), + ); // display QR code expect(mockedFlow).toHaveBeenLastCalledWith({ phase: Phase.ShowingQR, @@ -187,14 +202,18 @@ describe('', () => { expect(rendezvous.cancel).toHaveBeenCalledWith(RendezvousFailureReason.UserCancelled); }); - test('render QR then decline', async () => { + test("render QR then decline", async () => { const onFinished = jest.fn(); render(getComponent({ client, onFinished })); const rendezvous = mocked(MSC3906Rendezvous).mock.instances[0]; - await waitFor(() => expect(mockedFlow).toHaveBeenLastCalledWith(expect.objectContaining({ - phase: Phase.Connected, - }))); + await waitFor(() => + expect(mockedFlow).toHaveBeenLastCalledWith( + expect.objectContaining({ + phase: Phase.Connected, + }), + ), + ); expect(mockedFlow).toHaveBeenLastCalledWith({ phase: Phase.Connected, confirmationDigits: mockConfirmationDigits, @@ -211,7 +230,7 @@ describe('', () => { expect(rendezvous.declineLoginOnExistingDevice).toHaveBeenCalled(); }); - test('approve - no crypto', async () => { + test("approve - no crypto", async () => { // @ts-ignore client.crypto = undefined; const onFinished = jest.fn(); @@ -219,9 +238,13 @@ describe('', () => { render(getComponent({ client, onFinished })); const rendezvous = mocked(MSC3906Rendezvous).mock.instances[0]; - await waitFor(() => expect(mockedFlow).toHaveBeenLastCalledWith(expect.objectContaining({ - phase: Phase.Connected, - }))); + await waitFor(() => + expect(mockedFlow).toHaveBeenLastCalledWith( + expect.objectContaining({ + phase: Phase.Connected, + }), + ), + ); expect(mockedFlow).toHaveBeenLastCalledWith({ phase: Phase.Connected, confirmationDigits: mockConfirmationDigits, @@ -234,27 +257,36 @@ describe('', () => { const onClick = mockedFlow.mock.calls[0][0].onClick; await onClick(Click.Approve); - await waitFor(() => expect(mockedFlow).toHaveBeenLastCalledWith(expect.objectContaining({ - phase: Phase.WaitingForDevice, - }))); + await waitFor(() => + expect(mockedFlow).toHaveBeenLastCalledWith( + expect.objectContaining({ + phase: Phase.WaitingForDevice, + }), + ), + ); - expect(rendezvous.approveLoginOnExistingDevice).toHaveBeenCalledWith('token'); + expect(rendezvous.approveLoginOnExistingDevice).toHaveBeenCalledWith("token"); expect(onFinished).toHaveBeenCalledWith(true); }); - test('approve + verifying', async () => { + test("approve + verifying", async () => { const onFinished = jest.fn(); // @ts-ignore client.crypto = {}; - jest.spyOn(MSC3906Rendezvous.prototype, 'verifyNewDeviceOnExistingDevice') - .mockImplementation(() => unresolvedPromise()); + jest.spyOn(MSC3906Rendezvous.prototype, "verifyNewDeviceOnExistingDevice").mockImplementation(() => + unresolvedPromise(), + ); render(getComponent({ client, onFinished })); const rendezvous = mocked(MSC3906Rendezvous).mock.instances[0]; - await waitFor(() => expect(mockedFlow).toHaveBeenLastCalledWith(expect.objectContaining({ - phase: Phase.Connected, - }))); + await waitFor(() => + expect(mockedFlow).toHaveBeenLastCalledWith( + expect.objectContaining({ + phase: Phase.Connected, + }), + ), + ); expect(mockedFlow).toHaveBeenLastCalledWith({ phase: Phase.Connected, confirmationDigits: mockConfirmationDigits, @@ -267,25 +299,33 @@ describe('', () => { const onClick = mockedFlow.mock.calls[0][0].onClick; onClick(Click.Approve); - await waitFor(() => expect(mockedFlow).toHaveBeenLastCalledWith(expect.objectContaining({ - phase: Phase.Verifying, - }))); + await waitFor(() => + expect(mockedFlow).toHaveBeenLastCalledWith( + expect.objectContaining({ + phase: Phase.Verifying, + }), + ), + ); - expect(rendezvous.approveLoginOnExistingDevice).toHaveBeenCalledWith('token'); + expect(rendezvous.approveLoginOnExistingDevice).toHaveBeenCalledWith("token"); expect(rendezvous.verifyNewDeviceOnExistingDevice).toHaveBeenCalled(); // expect(onFinished).toHaveBeenCalledWith(true); }); - test('approve + verify', async () => { + test("approve + verify", async () => { const onFinished = jest.fn(); // @ts-ignore client.crypto = {}; render(getComponent({ client, onFinished })); const rendezvous = mocked(MSC3906Rendezvous).mock.instances[0]; - await waitFor(() => expect(mockedFlow).toHaveBeenLastCalledWith(expect.objectContaining({ - phase: Phase.Connected, - }))); + await waitFor(() => + expect(mockedFlow).toHaveBeenLastCalledWith( + expect.objectContaining({ + phase: Phase.Connected, + }), + ), + ); expect(mockedFlow).toHaveBeenLastCalledWith({ phase: Phase.Connected, confirmationDigits: mockConfirmationDigits, @@ -297,7 +337,7 @@ describe('', () => { // approve const onClick = mockedFlow.mock.calls[0][0].onClick; await onClick(Click.Approve); - expect(rendezvous.approveLoginOnExistingDevice).toHaveBeenCalledWith('token'); + expect(rendezvous.approveLoginOnExistingDevice).toHaveBeenCalledWith("token"); expect(rendezvous.verifyNewDeviceOnExistingDevice).toHaveBeenCalled(); expect(onFinished).toHaveBeenCalledWith(true); }); diff --git a/test/components/views/settings/devices/LoginWithQRFlow-test.tsx b/test/components/views/settings/devices/LoginWithQRFlow-test.tsx index 8b8abfa7bc..df2f835ba8 100644 --- a/test/components/views/settings/devices/LoginWithQRFlow-test.tsx +++ b/test/components/views/settings/devices/LoginWithQRFlow-test.tsx @@ -14,14 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'; -import React from 'react'; -import { RendezvousFailureReason } from 'matrix-js-sdk/src/rendezvous'; +import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react"; +import React from "react"; +import { RendezvousFailureReason } from "matrix-js-sdk/src/rendezvous"; -import LoginWithQRFlow from '../../../../../src/components/views/auth/LoginWithQRFlow'; -import { Click, Phase } from '../../../../../src/components/views/auth/LoginWithQR'; +import LoginWithQRFlow from "../../../../../src/components/views/auth/LoginWithQRFlow"; +import { Click, Phase } from "../../../../../src/components/views/auth/LoginWithQR"; -describe('', () => { +describe("", () => { const onClick = jest.fn(); const defaultProps = { @@ -34,81 +34,81 @@ describe('', () => { failureReason?: RendezvousFailureReason; code?: string; confirmationDigits?: string; - }) => - (); + }) => ; - beforeEach(() => { - }); + beforeEach(() => {}); afterEach(() => { onClick.mockReset(); cleanup(); }); - it('renders spinner while loading', async () => { + it("renders spinner while loading", async () => { const { container } = render(getComponent({ phase: Phase.Loading })); expect(container).toMatchSnapshot(); }); - it('renders spinner whilst QR generating', async () => { + it("renders spinner whilst QR generating", async () => { const { container } = render(getComponent({ phase: Phase.ShowingQR })); - expect(screen.getAllByTestId('cancel-button')).toHaveLength(1); + expect(screen.getAllByTestId("cancel-button")).toHaveLength(1); expect(container).toMatchSnapshot(); - fireEvent.click(screen.getByTestId('cancel-button')); + fireEvent.click(screen.getByTestId("cancel-button")); expect(onClick).toHaveBeenCalledWith(Click.Cancel); }); - it('renders QR code', async () => { - const { container } = render(getComponent({ phase: Phase.ShowingQR, code: 'mock-code' })); + it("renders QR code", async () => { + const { container } = render(getComponent({ phase: Phase.ShowingQR, code: "mock-code" })); // QR code is rendered async so we wait for it: - await waitFor(() => screen.getAllByAltText('QR Code').length === 1); + await waitFor(() => screen.getAllByAltText("QR Code").length === 1); expect(container).toMatchSnapshot(); }); - it('renders spinner while connecting', async () => { + it("renders spinner while connecting", async () => { const { container } = render(getComponent({ phase: Phase.Connecting })); - expect(screen.getAllByTestId('cancel-button')).toHaveLength(1); + expect(screen.getAllByTestId("cancel-button")).toHaveLength(1); expect(container).toMatchSnapshot(); - fireEvent.click(screen.getByTestId('cancel-button')); + fireEvent.click(screen.getByTestId("cancel-button")); expect(onClick).toHaveBeenCalledWith(Click.Cancel); }); - it('renders code when connected', async () => { - const { container } = render(getComponent({ phase: Phase.Connected, confirmationDigits: 'mock-digits' })); - expect(screen.getAllByText('mock-digits')).toHaveLength(1); - expect(screen.getAllByTestId('decline-login-button')).toHaveLength(1); - expect(screen.getAllByTestId('approve-login-button')).toHaveLength(1); + it("renders code when connected", async () => { + const { container } = render(getComponent({ phase: Phase.Connected, confirmationDigits: "mock-digits" })); + expect(screen.getAllByText("mock-digits")).toHaveLength(1); + expect(screen.getAllByTestId("decline-login-button")).toHaveLength(1); + expect(screen.getAllByTestId("approve-login-button")).toHaveLength(1); expect(container).toMatchSnapshot(); - fireEvent.click(screen.getByTestId('decline-login-button')); + fireEvent.click(screen.getByTestId("decline-login-button")); expect(onClick).toHaveBeenCalledWith(Click.Decline); - fireEvent.click(screen.getByTestId('approve-login-button')); + fireEvent.click(screen.getByTestId("approve-login-button")); expect(onClick).toHaveBeenCalledWith(Click.Approve); }); - it('renders spinner while signing in', async () => { + it("renders spinner while signing in", async () => { const { container } = render(getComponent({ phase: Phase.WaitingForDevice })); - expect(screen.getAllByTestId('cancel-button')).toHaveLength(1); + expect(screen.getAllByTestId("cancel-button")).toHaveLength(1); expect(container).toMatchSnapshot(); - fireEvent.click(screen.getByTestId('cancel-button')); + fireEvent.click(screen.getByTestId("cancel-button")); expect(onClick).toHaveBeenCalledWith(Click.Cancel); }); - it('renders spinner while verifying', async () => { + it("renders spinner while verifying", async () => { const { container } = render(getComponent({ phase: Phase.Verifying })); expect(container).toMatchSnapshot(); }); - describe('errors', () => { + describe("errors", () => { for (const failureReason of Object.values(RendezvousFailureReason)) { it(`renders ${failureReason}`, async () => { - const { container } = render(getComponent({ - phase: Phase.Error, - failureReason, - })); - expect(screen.getAllByTestId('cancellation-message')).toHaveLength(1); - expect(screen.getAllByTestId('try-again-button')).toHaveLength(1); + const { container } = render( + getComponent({ + phase: Phase.Error, + failureReason, + }), + ); + expect(screen.getAllByTestId("cancellation-message")).toHaveLength(1); + expect(screen.getAllByTestId("try-again-button")).toHaveLength(1); expect(container).toMatchSnapshot(); - fireEvent.click(screen.getByTestId('try-again-button')); + fireEvent.click(screen.getByTestId("try-again-button")); expect(onClick).toHaveBeenCalledWith(Click.TryAgain); }); } diff --git a/test/components/views/settings/devices/LoginWithQRSection-test.tsx b/test/components/views/settings/devices/LoginWithQRSection-test.tsx index 0045fa1cfd..df71544b32 100644 --- a/test/components/views/settings/devices/LoginWithQRSection-test.tsx +++ b/test/components/views/settings/devices/LoginWithQRSection-test.tsx @@ -14,15 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { render } from '@testing-library/react'; -import { mocked } from 'jest-mock'; -import { IServerVersions, MatrixClient } from 'matrix-js-sdk/src/matrix'; -import React from 'react'; +import { render } from "@testing-library/react"; +import { mocked } from "jest-mock"; +import { IServerVersions, MatrixClient } from "matrix-js-sdk/src/matrix"; +import React from "react"; -import LoginWithQRSection from '../../../../../src/components/views/settings/devices/LoginWithQRSection'; -import { MatrixClientPeg } from '../../../../../src/MatrixClientPeg'; -import { SettingLevel } from '../../../../../src/settings/SettingLevel'; -import SettingsStore from '../../../../../src/settings/SettingsStore'; +import LoginWithQRSection from "../../../../../src/components/views/settings/devices/LoginWithQRSection"; +import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg"; +import { SettingLevel } from "../../../../../src/settings/SettingLevel"; +import SettingsStore from "../../../../../src/settings/SettingsStore"; function makeClient() { return mocked({ @@ -34,7 +34,7 @@ function makeClient() { on: jest.fn(), isSynapseAdministrator: jest.fn().mockResolvedValue(false), isRoomEncrypted: jest.fn().mockReturnValue(false), - mxcUrlToHttp: jest.fn().mockReturnValue('mock-mxcUrlToHttp'), + mxcUrlToHttp: jest.fn().mockReturnValue("mock-mxcUrlToHttp"), removeListener: jest.fn(), currentState: { on: jest.fn(), @@ -49,9 +49,9 @@ function makeVersions(unstableFeatures: Record): IServerVersion }; } -describe('', () => { +describe("", () => { beforeAll(() => { - jest.spyOn(MatrixClientPeg, 'get').mockReturnValue(makeClient()); + jest.spyOn(MatrixClientPeg, "get").mockReturnValue(makeClient()); }); const defaultProps = { @@ -59,35 +59,38 @@ describe('', () => { versions: makeVersions({}), }; - const getComponent = (props = {}) => - (); + const getComponent = (props = {}) => ; - describe('should not render', () => { - it('no support at all', () => { + describe("should not render", () => { + it("no support at all", () => { const { container } = render(getComponent()); expect(container).toMatchSnapshot(); }); - it('feature enabled', async () => { - await SettingsStore.setValue('feature_qr_signin_reciprocate_show', null, SettingLevel.DEVICE, true); + it("feature enabled", async () => { + await SettingsStore.setValue("feature_qr_signin_reciprocate_show", null, SettingLevel.DEVICE, true); const { container } = render(getComponent()); expect(container).toMatchSnapshot(); }); - it('only feature + MSC3882 enabled', async () => { - await SettingsStore.setValue('feature_qr_signin_reciprocate_show', null, SettingLevel.DEVICE, true); - const { container } = render(getComponent({ versions: makeVersions({ 'org.matrix.msc3882': true }) })); + it("only feature + MSC3882 enabled", async () => { + await SettingsStore.setValue("feature_qr_signin_reciprocate_show", null, SettingLevel.DEVICE, true); + const { container } = render(getComponent({ versions: makeVersions({ "org.matrix.msc3882": true }) })); expect(container).toMatchSnapshot(); }); }); - describe('should render panel', () => { - it('enabled by feature + MSC3882 + MSC3886', async () => { - await SettingsStore.setValue('feature_qr_signin_reciprocate_show', null, SettingLevel.DEVICE, true); - const { container } = render(getComponent({ versions: makeVersions({ - 'org.matrix.msc3882': true, - 'org.matrix.msc3886': true, - }) })); + describe("should render panel", () => { + it("enabled by feature + MSC3882 + MSC3886", async () => { + await SettingsStore.setValue("feature_qr_signin_reciprocate_show", null, SettingLevel.DEVICE, true); + const { container } = render( + getComponent({ + versions: makeVersions({ + "org.matrix.msc3882": true, + "org.matrix.msc3886": true, + }), + }), + ); expect(container).toMatchSnapshot(); }); }); diff --git a/test/components/views/settings/devices/SecurityRecommendations-test.tsx b/test/components/views/settings/devices/SecurityRecommendations-test.tsx index a0a1afd2ef..f74d4a8772 100644 --- a/test/components/views/settings/devices/SecurityRecommendations-test.tsx +++ b/test/components/views/settings/devices/SecurityRecommendations-test.tsx @@ -14,37 +14,36 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import { act, fireEvent, render } from '@testing-library/react'; +import React from "react"; +import { act, fireEvent, render } from "@testing-library/react"; -import SecurityRecommendations from '../../../../../src/components/views/settings/devices/SecurityRecommendations'; -import { DeviceSecurityVariation } from '../../../../../src/components/views/settings/devices/types'; +import SecurityRecommendations from "../../../../../src/components/views/settings/devices/SecurityRecommendations"; +import { DeviceSecurityVariation } from "../../../../../src/components/views/settings/devices/types"; const MS_DAY = 24 * 60 * 60 * 1000; -describe('', () => { - const unverifiedNoMetadata = { device_id: 'unverified-no-metadata', isVerified: false }; - const verifiedNoMetadata = { device_id: 'verified-no-metadata', isVerified: true }; - const hundredDaysOld = { device_id: '100-days-old', isVerified: true, last_seen_ts: Date.now() - (MS_DAY * 100) }; +describe("", () => { + const unverifiedNoMetadata = { device_id: "unverified-no-metadata", isVerified: false }; + const verifiedNoMetadata = { device_id: "verified-no-metadata", isVerified: true }; + const hundredDaysOld = { device_id: "100-days-old", isVerified: true, last_seen_ts: Date.now() - MS_DAY * 100 }; const hundredDaysOldUnverified = { - device_id: 'unverified-100-days-old', + device_id: "unverified-100-days-old", isVerified: false, - last_seen_ts: Date.now() - (MS_DAY * 100), + last_seen_ts: Date.now() - MS_DAY * 100, }; const defaultProps = { devices: {}, goToFilteredList: jest.fn(), - currentDeviceId: 'abc123', + currentDeviceId: "abc123", }; - const getComponent = (props = {}) => - (); + const getComponent = (props = {}) => ; - it('renders null when no devices', () => { + it("renders null when no devices", () => { const { container } = render(getComponent()); expect(container.firstChild).toBeNull(); }); - it('renders unverified devices section when user has unverified devices', () => { + it("renders unverified devices section when user has unverified devices", () => { const devices = { [unverifiedNoMetadata.device_id]: unverifiedNoMetadata, [verifiedNoMetadata.device_id]: verifiedNoMetadata, @@ -54,7 +53,7 @@ describe('', () => { expect(container).toMatchSnapshot(); }); - it('does not render unverified devices section when only the current device is unverified', () => { + it("does not render unverified devices section when only the current device is unverified", () => { const devices = { [unverifiedNoMetadata.device_id]: unverifiedNoMetadata, [verifiedNoMetadata.device_id]: verifiedNoMetadata, @@ -64,7 +63,7 @@ describe('', () => { expect(container.firstChild).toBeFalsy(); }); - it('renders inactive devices section when user has inactive devices', () => { + it("renders inactive devices section when user has inactive devices", () => { const devices = { [verifiedNoMetadata.device_id]: verifiedNoMetadata, [hundredDaysOldUnverified.device_id]: hundredDaysOldUnverified, @@ -73,7 +72,7 @@ describe('', () => { expect(container).toMatchSnapshot(); }); - it('renders both cards when user has both unverified and inactive devices', () => { + it("renders both cards when user has both unverified and inactive devices", () => { const devices = { [verifiedNoMetadata.device_id]: verifiedNoMetadata, [hundredDaysOld.device_id]: hundredDaysOld, @@ -83,7 +82,7 @@ describe('', () => { expect(container).toMatchSnapshot(); }); - it('clicking view all unverified devices button works', () => { + it("clicking view all unverified devices button works", () => { const goToFilteredList = jest.fn(); const devices = { [verifiedNoMetadata.device_id]: verifiedNoMetadata, @@ -93,13 +92,13 @@ describe('', () => { const { getByTestId } = render(getComponent({ devices, goToFilteredList })); act(() => { - fireEvent.click(getByTestId('unverified-devices-cta')); + fireEvent.click(getByTestId("unverified-devices-cta")); }); expect(goToFilteredList).toHaveBeenCalledWith(DeviceSecurityVariation.Unverified); }); - it('clicking view all inactive devices button works', () => { + it("clicking view all inactive devices button works", () => { const goToFilteredList = jest.fn(); const devices = { [verifiedNoMetadata.device_id]: verifiedNoMetadata, @@ -109,7 +108,7 @@ describe('', () => { const { getByTestId } = render(getComponent({ devices, goToFilteredList })); act(() => { - fireEvent.click(getByTestId('inactive-devices-cta')); + fireEvent.click(getByTestId("inactive-devices-cta")); }); expect(goToFilteredList).toHaveBeenCalledWith(DeviceSecurityVariation.Inactive); diff --git a/test/components/views/settings/devices/SelectableDeviceTile-test.tsx b/test/components/views/settings/devices/SelectableDeviceTile-test.tsx index c2cb3f7f31..d9327d8ae4 100644 --- a/test/components/views/settings/devices/SelectableDeviceTile-test.tsx +++ b/test/components/views/settings/devices/SelectableDeviceTile-test.tsx @@ -14,18 +14,18 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { fireEvent, render } from '@testing-library/react'; -import React from 'react'; -import { act } from 'react-dom/test-utils'; +import { fireEvent, render } from "@testing-library/react"; +import React from "react"; +import { act } from "react-dom/test-utils"; -import SelectableDeviceTile from '../../../../../src/components/views/settings/devices/SelectableDeviceTile'; -import { DeviceType } from '../../../../../src/utils/device/parseUserAgent'; +import SelectableDeviceTile from "../../../../../src/components/views/settings/devices/SelectableDeviceTile"; +import { DeviceType } from "../../../../../src/utils/device/parseUserAgent"; -describe('', () => { +describe("", () => { const device = { - display_name: 'My Device', - device_id: 'my-device', - last_seen_ip: '123.456.789', + display_name: "My Device", + device_id: "my-device", + last_seen_ip: "123.456.789", isVerified: false, deviceType: DeviceType.Unknown, }; @@ -36,20 +36,19 @@ describe('', () => { children:
test
, isSelected: false, }; - const getComponent = (props = {}) => - (); + const getComponent = (props = {}) => ; - it('renders unselected device tile with checkbox', () => { + it("renders unselected device tile with checkbox", () => { const { container } = render(getComponent()); expect(container).toMatchSnapshot(); }); - it('renders selected tile', () => { + it("renders selected tile", () => { const { container } = render(getComponent({ isSelected: true })); expect(container.querySelector(`#device-tile-checkbox-${device.device_id}`)).toMatchSnapshot(); }); - it('calls onSelect on checkbox click', () => { + it("calls onSelect on checkbox click", () => { const onSelect = jest.fn(); const { container } = render(getComponent({ onSelect })); @@ -60,7 +59,7 @@ describe('', () => { expect(onSelect).toHaveBeenCalled(); }); - it('calls onClick on device tile info click', () => { + it("calls onClick on device tile info click", () => { const onClick = jest.fn(); const { getByText } = render(getComponent({ onClick })); @@ -71,14 +70,18 @@ describe('', () => { expect(onClick).toHaveBeenCalled(); }); - it('does not call onClick when clicking device tiles actions', () => { + it("does not call onClick when clicking device tiles actions", () => { const onClick = jest.fn(); const onDeviceActionClick = jest.fn(); - const children = ; + const children = ( + + ); const { getByTestId } = render(getComponent({ onClick, children })); act(() => { - fireEvent.click(getByTestId('device-action-button')); + fireEvent.click(getByTestId("device-action-button")); }); // action click handler called diff --git a/test/components/views/settings/devices/deleteDevices-test.tsx b/test/components/views/settings/devices/deleteDevices-test.tsx index 81fa5a2f3f..a6ffebd9ae 100644 --- a/test/components/views/settings/devices/deleteDevices-test.tsx +++ b/test/components/views/settings/devices/deleteDevices-test.tsx @@ -18,15 +18,15 @@ import { deleteDevicesWithInteractiveAuth } from "../../../../../src/components/ import Modal from "../../../../../src/Modal"; import { getMockClientWithEventEmitter, mockClientMethodsUser } from "../../../../test-utils"; -describe('deleteDevices()', () => { - const userId = '@alice:server.org'; - const deviceIds = ['device_1', 'device_2']; +describe("deleteDevices()", () => { + const userId = "@alice:server.org"; + const deviceIds = ["device_1", "device_2"]; const mockClient = getMockClientWithEventEmitter({ ...mockClientMethodsUser(userId), deleteMultipleDevices: jest.fn(), }); - const modalSpy = jest.spyOn(Modal, 'createDialog'); + const modalSpy = jest.spyOn(Modal, "createDialog"); const interactiveAuthError = { httpStatus: 401, data: { flows: [] } }; @@ -34,7 +34,7 @@ describe('deleteDevices()', () => { jest.clearAllMocks(); }); - it('deletes devices and calls onFinished when interactive auth is not required', async () => { + it("deletes devices and calls onFinished when interactive auth is not required", async () => { mockClient.deleteMultipleDevices.mockResolvedValue({}); const onFinished = jest.fn(); @@ -47,16 +47,14 @@ describe('deleteDevices()', () => { expect(modalSpy).not.toHaveBeenCalled(); }); - it('throws without opening auth dialog when delete fails with a non-401 status code', async () => { - const error = new Error(''); + it("throws without opening auth dialog when delete fails with a non-401 status code", async () => { + const error = new Error(""); // @ts-ignore error.httpStatus = 404; mockClient.deleteMultipleDevices.mockRejectedValue(error); const onFinished = jest.fn(); - await expect( - deleteDevicesWithInteractiveAuth(mockClient, deviceIds, onFinished), - ).rejects.toThrow(error); + await expect(deleteDevicesWithInteractiveAuth(mockClient, deviceIds, onFinished)).rejects.toThrow(error); expect(onFinished).not.toHaveBeenCalled(); @@ -64,8 +62,8 @@ describe('deleteDevices()', () => { expect(modalSpy).not.toHaveBeenCalled(); }); - it('throws without opening auth dialog when delete fails without data.flows', async () => { - const error = new Error(''); + it("throws without opening auth dialog when delete fails without data.flows", async () => { + const error = new Error(""); // @ts-ignore error.httpStatus = 401; // @ts-ignore @@ -73,9 +71,7 @@ describe('deleteDevices()', () => { mockClient.deleteMultipleDevices.mockRejectedValue(error); const onFinished = jest.fn(); - await expect( - deleteDevicesWithInteractiveAuth(mockClient, deviceIds, onFinished), - ).rejects.toThrow(error); + await expect(deleteDevicesWithInteractiveAuth(mockClient, deviceIds, onFinished)).rejects.toThrow(error); expect(onFinished).not.toHaveBeenCalled(); @@ -83,7 +79,7 @@ describe('deleteDevices()', () => { expect(modalSpy).not.toHaveBeenCalled(); }); - it('opens interactive auth dialog when delete fails with 401', async () => { + it("opens interactive auth dialog when delete fails with 401", async () => { mockClient.deleteMultipleDevices.mockRejectedValue(interactiveAuthError); const onFinished = jest.fn(); @@ -94,12 +90,10 @@ describe('deleteDevices()', () => { // opened modal expect(modalSpy).toHaveBeenCalled(); - const [, { - title, authData, aestheticsForStagePhases, - }] = modalSpy.mock.calls[0]; + const [, { title, authData, aestheticsForStagePhases }] = modalSpy.mock.calls[0]; // modal opened as expected - expect(title).toEqual('Authentication'); + expect(title).toEqual("Authentication"); expect(authData).toEqual(interactiveAuthError.data); expect(aestheticsForStagePhases).toMatchSnapshot(); }); diff --git a/test/components/views/settings/devices/filter-test.ts b/test/components/views/settings/devices/filter-test.ts index 5da256b763..542d2a953e 100644 --- a/test/components/views/settings/devices/filter-test.ts +++ b/test/components/views/settings/devices/filter-test.ts @@ -14,58 +14,48 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { - filterDevicesBySecurityRecommendation, -} from "../../../../../src/components/views/settings/devices/filter"; -import { - DeviceSecurityVariation, -} from "../../../../../src/components/views/settings/devices/types"; +import { filterDevicesBySecurityRecommendation } from "../../../../../src/components/views/settings/devices/filter"; +import { DeviceSecurityVariation } from "../../../../../src/components/views/settings/devices/types"; import { DeviceType } from "../../../../../src/utils/device/parseUserAgent"; const MS_DAY = 86400000; -describe('filterDevicesBySecurityRecommendation()', () => { +describe("filterDevicesBySecurityRecommendation()", () => { const unverifiedNoMetadata = { - device_id: 'unverified-no-metadata', + device_id: "unverified-no-metadata", isVerified: false, deviceType: DeviceType.Unknown, }; const verifiedNoMetadata = { - device_id: 'verified-no-metadata', + device_id: "verified-no-metadata", isVerified: true, deviceType: DeviceType.Unknown, }; const hundredDaysOld = { - device_id: '100-days-old', + device_id: "100-days-old", isVerified: true, - last_seen_ts: Date.now() - (MS_DAY * 100), + last_seen_ts: Date.now() - MS_DAY * 100, deviceType: DeviceType.Unknown, }; const hundredDaysOldUnverified = { - device_id: 'unverified-100-days-old', + device_id: "unverified-100-days-old", isVerified: false, - last_seen_ts: Date.now() - (MS_DAY * 100), + last_seen_ts: Date.now() - MS_DAY * 100, deviceType: DeviceType.Unknown, }; const fiftyDaysOld = { - device_id: '50-days-old', + device_id: "50-days-old", isVerified: true, - last_seen_ts: Date.now() - (MS_DAY * 50), + last_seen_ts: Date.now() - MS_DAY * 50, deviceType: DeviceType.Unknown, }; - const devices = [ - unverifiedNoMetadata, - verifiedNoMetadata, - hundredDaysOld, - hundredDaysOldUnverified, - fiftyDaysOld, - ]; + const devices = [unverifiedNoMetadata, verifiedNoMetadata, hundredDaysOld, hundredDaysOldUnverified, fiftyDaysOld]; - it('returns all devices when no securityRecommendations are passed', () => { + it("returns all devices when no securityRecommendations are passed", () => { expect(filterDevicesBySecurityRecommendation(devices, [])).toBe(devices); }); - it('returns devices older than 90 days as inactive', () => { + it("returns devices older than 90 days as inactive", () => { expect(filterDevicesBySecurityRecommendation(devices, [DeviceSecurityVariation.Inactive])).toEqual([ // devices without ts metadata are not filtered as inactive hundredDaysOld, @@ -73,7 +63,7 @@ describe('filterDevicesBySecurityRecommendation()', () => { ]); }); - it('returns correct devices for verified filter', () => { + it("returns correct devices for verified filter", () => { expect(filterDevicesBySecurityRecommendation(devices, [DeviceSecurityVariation.Verified])).toEqual([ verifiedNoMetadata, hundredDaysOld, @@ -81,19 +71,19 @@ describe('filterDevicesBySecurityRecommendation()', () => { ]); }); - it('returns correct devices for unverified filter', () => { + it("returns correct devices for unverified filter", () => { expect(filterDevicesBySecurityRecommendation(devices, [DeviceSecurityVariation.Unverified])).toEqual([ unverifiedNoMetadata, hundredDaysOldUnverified, ]); }); - it('returns correct devices for combined verified and inactive filters', () => { - expect(filterDevicesBySecurityRecommendation( - devices, - [DeviceSecurityVariation.Unverified, DeviceSecurityVariation.Inactive], - )).toEqual([ - hundredDaysOldUnverified, - ]); + it("returns correct devices for combined verified and inactive filters", () => { + expect( + filterDevicesBySecurityRecommendation(devices, [ + DeviceSecurityVariation.Unverified, + DeviceSecurityVariation.Inactive, + ]), + ).toEqual([hundredDaysOldUnverified]); }); }); diff --git a/test/components/views/settings/discovery/EmailAddresses-test.tsx b/test/components/views/settings/discovery/EmailAddresses-test.tsx index 8f885582f4..639d160c7d 100644 --- a/test/components/views/settings/discovery/EmailAddresses-test.tsx +++ b/test/components/views/settings/discovery/EmailAddresses-test.tsx @@ -14,11 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; import { render, screen } from "@testing-library/react"; -import { IThreepid, ThreepidMedium } from 'matrix-js-sdk/src/@types/threepids'; +import { IThreepid, ThreepidMedium } from "matrix-js-sdk/src/@types/threepids"; -import { EmailAddress } from '../../../../../src/components/views/settings/discovery/EmailAddresses'; +import { EmailAddress } from "../../../../../src/components/views/settings/discovery/EmailAddresses"; describe("", () => { it("should track props.email.bound changes", async () => { diff --git a/test/components/views/settings/discovery/PhoneNumbers-test.tsx b/test/components/views/settings/discovery/PhoneNumbers-test.tsx index 899f5a2254..cac8ed7c42 100644 --- a/test/components/views/settings/discovery/PhoneNumbers-test.tsx +++ b/test/components/views/settings/discovery/PhoneNumbers-test.tsx @@ -14,9 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; import { render, screen } from "@testing-library/react"; -import { IThreepid, ThreepidMedium } from 'matrix-js-sdk/src/@types/threepids'; +import { IThreepid, ThreepidMedium } from "matrix-js-sdk/src/@types/threepids"; import { PhoneNumber } from "../../../../../src/components/views/settings/discovery/PhoneNumbers"; diff --git a/test/components/views/settings/shared/SettingsSubsection-test.tsx b/test/components/views/settings/shared/SettingsSubsection-test.tsx index cd833f90af..0641d11ad8 100644 --- a/test/components/views/settings/shared/SettingsSubsection-test.tsx +++ b/test/components/views/settings/shared/SettingsSubsection-test.tsx @@ -14,42 +14,45 @@ 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 React from "react"; +import { render } from "@testing-library/react"; -import SettingsSubsection from '../../../../../src/components/views/settings/shared/SettingsSubsection'; +import SettingsSubsection from "../../../../../src/components/views/settings/shared/SettingsSubsection"; -describe('', () => { +describe("", () => { const defaultProps = { - heading: 'Test', + heading: "Test", children:
test settings content
, }; - const getComponent = (props = {}): React.ReactElement => - (); + const getComponent = (props = {}): React.ReactElement => ; - it('renders with plain text heading', () => { + it("renders with plain text heading", () => { const { container } = render(getComponent()); expect(container).toMatchSnapshot(); }); - it('renders with react element heading', () => { + it("renders with react element heading", () => { const heading =

This is the heading

; const { container } = render(getComponent({ heading })); expect(container).toMatchSnapshot(); }); - it('renders without description', () => { + it("renders without description", () => { const { container } = render(getComponent()); expect(container).toMatchSnapshot(); }); - it('renders with plain text description', () => { - const { container } = render(getComponent({ description: 'This describes the subsection' })); + it("renders with plain text description", () => { + const { container } = render(getComponent({ description: "This describes the subsection" })); expect(container).toMatchSnapshot(); }); - it('renders with react element description', () => { - const description =

This describes the section link

; + it("renders with react element description", () => { + const description = ( +

+ This describes the section link +

+ ); const { container } = render(getComponent({ description })); expect(container).toMatchSnapshot(); }); diff --git a/test/components/views/settings/shared/SettingsSubsectionHeading-test.tsx b/test/components/views/settings/shared/SettingsSubsectionHeading-test.tsx index cb6959a671..b3ced10b4c 100644 --- a/test/components/views/settings/shared/SettingsSubsectionHeading-test.tsx +++ b/test/components/views/settings/shared/SettingsSubsectionHeading-test.tsx @@ -14,27 +14,24 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { render } from '@testing-library/react'; -import React from 'react'; +import { render } from "@testing-library/react"; +import React from "react"; -import { - SettingsSubsectionHeading, -} from '../../../../../src/components/views/settings/shared/SettingsSubsectionHeading'; +import { SettingsSubsectionHeading } from "../../../../../src/components/views/settings/shared/SettingsSubsectionHeading"; -describe('', () => { +describe("", () => { const defaultProps = { - heading: 'test', + heading: "test", }; - const getComponent = (props = {}) => - render(); + const getComponent = (props = {}) => render(); - it('renders without children', () => { + it("renders without children", () => { const { container } = getComponent(); expect({ container }).toMatchSnapshot(); }); - it('renders with children', () => { - const children = test; + it("renders with children", () => { + const children = test; const { container } = getComponent({ children }); expect({ container }).toMatchSnapshot(); }); diff --git a/test/components/views/settings/tabs/SettingsTab-test.tsx b/test/components/views/settings/tabs/SettingsTab-test.tsx index 001162635a..2cd678c672 100644 --- a/test/components/views/settings/tabs/SettingsTab-test.tsx +++ b/test/components/views/settings/tabs/SettingsTab-test.tsx @@ -14,17 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { ReactElement } from 'react'; -import { render } from '@testing-library/react'; +import React, { ReactElement } from "react"; +import { render } from "@testing-library/react"; -import SettingsTab, { SettingsTabProps } from '../../../../../src/components/views/settings/tabs/SettingsTab'; +import SettingsTab, { SettingsTabProps } from "../../../../../src/components/views/settings/tabs/SettingsTab"; -describe('', () => { - const getComponent = (props: SettingsTabProps): ReactElement => (); - it('renders tab', () => { - const { container } = render(getComponent({ heading: 'Test Tab', children:
test
})); +describe("", () => { + const getComponent = (props: SettingsTabProps): ReactElement => ; + it("renders tab", () => { + const { container } = render(getComponent({ heading: "Test Tab", children:
test
})); expect(container).toMatchSnapshot(); }); }); - diff --git a/test/components/views/settings/tabs/room/NotificationSettingsTab-test.tsx b/test/components/views/settings/tabs/room/NotificationSettingsTab-test.tsx index d8414e9d92..94e1a42289 100644 --- a/test/components/views/settings/tabs/room/NotificationSettingsTab-test.tsx +++ b/test/components/views/settings/tabs/room/NotificationSettingsTab-test.tsx @@ -33,7 +33,7 @@ describe("NotificatinSettingsTab", () => { let roomProps: RoomEchoChamber; const renderTab = (): RenderResult => { - return render( { }} />); + return render( {}} />); }; beforeEach(() => { @@ -50,7 +50,8 @@ describe("NotificatinSettingsTab", () => { // settings link of mentions_only volume const settingsLink = tab.container.querySelector( - "label.mx_NotificationSettingsTab_mentionsKeywordsEntry div.mx_AccessibleButton"); + "label.mx_NotificationSettingsTab_mentionsKeywordsEntry div.mx_AccessibleButton", + ); if (!settingsLink) throw new Error("settings link does not exist."); await userEvent.click(settingsLink); diff --git a/test/components/views/settings/tabs/room/RolesRoomSettingsTab-test.tsx b/test/components/views/settings/tabs/room/RolesRoomSettingsTab-test.tsx index 8bf89df64d..f630082811 100644 --- a/test/components/views/settings/tabs/room/RolesRoomSettingsTab-test.tsx +++ b/test/components/views/settings/tabs/room/RolesRoomSettingsTab-test.tsx @@ -92,15 +92,11 @@ describe("RolesRoomSettingsTab", () => { }); it("should update the power levels", () => { - expect(cli.sendStateEvent).toHaveBeenCalledWith( - roomId, - EventType.RoomPowerLevels, - { - events: { - [VoiceBroadcastInfoEventType]: 0, - }, + expect(cli.sendStateEvent).toHaveBeenCalledWith(roomId, EventType.RoomPowerLevels, { + events: { + [VoiceBroadcastInfoEventType]: 0, }, - ); + }); }); }); @@ -145,15 +141,11 @@ describe("RolesRoomSettingsTab", () => { }); expect(getJoinCallSelectedOption(tab)?.textContent).toBe("Default"); - expect(cli.sendStateEvent).toHaveBeenCalledWith( - roomId, - EventType.RoomPowerLevels, - { - events: { - [ElementCall.MEMBER_EVENT_TYPE.name]: 0, - }, + expect(cli.sendStateEvent).toHaveBeenCalledWith(roomId, EventType.RoomPowerLevels, { + events: { + [ElementCall.MEMBER_EVENT_TYPE.name]: 0, }, - ); + }); }); }); @@ -170,15 +162,11 @@ describe("RolesRoomSettingsTab", () => { }); expect(getStartCallSelectedOption(tab)?.textContent).toBe("Default"); - expect(cli.sendStateEvent).toHaveBeenCalledWith( - roomId, - EventType.RoomPowerLevels, - { - events: { - [ElementCall.CALL_EVENT_TYPE.name]: 0, - }, + expect(cli.sendStateEvent).toHaveBeenCalledWith(roomId, EventType.RoomPowerLevels, { + events: { + [ElementCall.CALL_EVENT_TYPE.name]: 0, }, - ); + }); }); }); }); diff --git a/test/components/views/settings/tabs/room/VoipRoomSettingsTab-test.tsx b/test/components/views/settings/tabs/room/VoipRoomSettingsTab-test.tsx index 0295170ab3..8c00ef4dba 100644 --- a/test/components/views/settings/tabs/room/VoipRoomSettingsTab-test.tsx +++ b/test/components/views/settings/tabs/room/VoipRoomSettingsTab-test.tsx @@ -88,16 +88,18 @@ describe("RolesRoomSettingsTab", () => { const tab = renderTab(); fireEvent.click(getElementCallSwitch(tab).querySelector(".mx_ToggleSwitch")); - await waitFor(() => expect(cli.sendStateEvent).toHaveBeenCalledWith( - room.roomId, - EventType.RoomPowerLevels, - expect.objectContaining({ - events: { - [ElementCall.CALL_EVENT_TYPE.name]: 50, - [ElementCall.MEMBER_EVENT_TYPE.name]: 0, - }, - }), - )); + await waitFor(() => + expect(cli.sendStateEvent).toHaveBeenCalledWith( + room.roomId, + EventType.RoomPowerLevels, + expect.objectContaining({ + events: { + [ElementCall.CALL_EVENT_TYPE.name]: 50, + [ElementCall.MEMBER_EVENT_TYPE.name]: 0, + }, + }), + ), + ); }); it("enables Element calls in private room", async () => { @@ -106,16 +108,18 @@ describe("RolesRoomSettingsTab", () => { const tab = renderTab(); fireEvent.click(getElementCallSwitch(tab).querySelector(".mx_ToggleSwitch")); - await waitFor(() => expect(cli.sendStateEvent).toHaveBeenCalledWith( - room.roomId, - EventType.RoomPowerLevels, - expect.objectContaining({ - events: { - [ElementCall.CALL_EVENT_TYPE.name]: 0, - [ElementCall.MEMBER_EVENT_TYPE.name]: 0, - }, - }), - )); + await waitFor(() => + expect(cli.sendStateEvent).toHaveBeenCalledWith( + room.roomId, + EventType.RoomPowerLevels, + expect.objectContaining({ + events: { + [ElementCall.CALL_EVENT_TYPE.name]: 0, + [ElementCall.MEMBER_EVENT_TYPE.name]: 0, + }, + }), + ), + ); }); }); @@ -125,16 +129,18 @@ describe("RolesRoomSettingsTab", () => { const tab = renderTab(); fireEvent.click(getElementCallSwitch(tab).querySelector(".mx_ToggleSwitch")); - await waitFor(() => expect(cli.sendStateEvent).toHaveBeenCalledWith( - room.roomId, - EventType.RoomPowerLevels, - expect.objectContaining({ - events: { - [ElementCall.CALL_EVENT_TYPE.name]: 100, - [ElementCall.MEMBER_EVENT_TYPE.name]: 100, - }, - }), - )); + await waitFor(() => + expect(cli.sendStateEvent).toHaveBeenCalledWith( + room.roomId, + EventType.RoomPowerLevels, + expect.objectContaining({ + events: { + [ElementCall.CALL_EVENT_TYPE.name]: 100, + [ElementCall.MEMBER_EVENT_TYPE.name]: 100, + }, + }), + ), + ); }); }); }); diff --git a/test/components/views/settings/tabs/user/KeyboardUserSettingsTab-test.tsx b/test/components/views/settings/tabs/user/KeyboardUserSettingsTab-test.tsx index a96b3a6533..18c66da775 100644 --- a/test/components/views/settings/tabs/user/KeyboardUserSettingsTab-test.tsx +++ b/test/components/views/settings/tabs/user/KeyboardUserSettingsTab-test.tsx @@ -1,4 +1,3 @@ - /* Copyright 2022 Šimon Brandner @@ -18,8 +17,7 @@ limitations under the License. import { render } from "@testing-library/react"; import React from "react"; -import KeyboardUserSettingsTab from - "../../../../../../src/components/views/settings/tabs/user/KeyboardUserSettingsTab"; +import KeyboardUserSettingsTab from "../../../../../../src/components/views/settings/tabs/user/KeyboardUserSettingsTab"; import { Key } from "../../../../../../src/Keyboard"; import { mockPlatformPeg } from "../../../../../test-utils/platform"; @@ -58,19 +56,19 @@ describe("KeyboardUserSettingsTab", () => { it("renders list of keyboard shortcuts", () => { mockKeyboardShortcuts({ - "CATEGORIES": { - "Composer": { + CATEGORIES: { + Composer: { settingNames: ["keybind1", "keybind2"], categoryLabel: "Composer", }, - "Navigation": { + Navigation: { settingNames: ["keybind3"], categoryLabel: "Navigation", }, }, }); mockKeyboardShortcutUtils({ - "getKeyboardShortcutValue": (name) => { + getKeyboardShortcutValue: (name) => { switch (name) { case "keybind1": return { @@ -80,7 +78,8 @@ describe("KeyboardUserSettingsTab", () => { case "keybind2": { return { key: Key.B, - ctrlKey: true }; + ctrlKey: true, + }; } case "keybind3": { return { @@ -89,7 +88,7 @@ describe("KeyboardUserSettingsTab", () => { } } }, - "getKeyboardShortcutDisplayName": (name) => { + getKeyboardShortcutDisplayName: (name) => { switch (name) { case "keybind1": return "Cancel replying to a message"; diff --git a/test/components/views/settings/tabs/user/LabsUserSettingsTab-test.tsx b/test/components/views/settings/tabs/user/LabsUserSettingsTab-test.tsx index 614bb4062f..7fa008d4a3 100644 --- a/test/components/views/settings/tabs/user/LabsUserSettingsTab-test.tsx +++ b/test/components/views/settings/tabs/user/LabsUserSettingsTab-test.tsx @@ -14,33 +14,33 @@ 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 React from "react"; +import { render } from "@testing-library/react"; -import LabsUserSettingsTab from '../../../../../../src/components/views/settings/tabs/user/LabsUserSettingsTab'; -import SettingsStore from '../../../../../../src/settings/SettingsStore'; +import LabsUserSettingsTab from "../../../../../../src/components/views/settings/tabs/user/LabsUserSettingsTab"; +import SettingsStore from "../../../../../../src/settings/SettingsStore"; import { getMockClientWithEventEmitter, mockClientMethodsServer, mockClientMethodsUser, -} from '../../../../../test-utils'; -import SdkConfig from '../../../../../../src/SdkConfig'; +} from "../../../../../test-utils"; +import SdkConfig from "../../../../../../src/SdkConfig"; -describe('', () => { - const sdkConfigSpy = jest.spyOn(SdkConfig, 'get'); +describe("", () => { + const sdkConfigSpy = jest.spyOn(SdkConfig, "get"); const defaultProps = { closeSettingsFn: jest.fn(), }; const getComponent = () => ; - const userId = '@alice:server.org'; + const userId = "@alice:server.org"; getMockClientWithEventEmitter({ ...mockClientMethodsUser(userId), ...mockClientMethodsServer(), }); - const settingsValueSpy = jest.spyOn(SettingsStore, 'getValue'); + const settingsValueSpy = jest.spyOn(SettingsStore, "getValue"); beforeEach(() => { jest.clearAllMocks(); @@ -48,26 +48,26 @@ describe('', () => { sdkConfigSpy.mockReturnValue(false); }); - it('renders settings marked as beta as beta cards', () => { + it("renders settings marked as beta as beta cards", () => { const { getByTestId } = render(getComponent()); expect(getByTestId("labs-beta-section")).toMatchSnapshot(); }); - it('does not render non-beta labs settings when disabled in config', () => { + it("does not render non-beta labs settings when disabled in config", () => { const { container } = render(getComponent()); - expect(sdkConfigSpy).toHaveBeenCalledWith('show_labs_settings'); + expect(sdkConfigSpy).toHaveBeenCalledWith("show_labs_settings"); - const labsSections = container.getElementsByClassName('mx_SettingsTab_section'); + const labsSections = container.getElementsByClassName("mx_SettingsTab_section"); // only section is beta section expect(labsSections.length).toEqual(1); }); - it('renders non-beta labs settings when enabled in config', () => { + it("renders non-beta labs settings when enabled in config", () => { // enable labs - sdkConfigSpy.mockImplementation(configName => configName === 'show_labs_settings'); + sdkConfigSpy.mockImplementation((configName) => configName === "show_labs_settings"); const { container } = render(getComponent()); - const labsSections = container.getElementsByClassName('mx_SettingsTab_section'); + const labsSections = container.getElementsByClassName("mx_SettingsTab_section"); expect(labsSections.length).toEqual(11); }); }); diff --git a/test/components/views/settings/tabs/user/PreferencesUserSettingsTab-test.tsx b/test/components/views/settings/tabs/user/PreferencesUserSettingsTab-test.tsx index 36b7e8cec3..0e69f1878c 100644 --- a/test/components/views/settings/tabs/user/PreferencesUserSettingsTab-test.tsx +++ b/test/components/views/settings/tabs/user/PreferencesUserSettingsTab-test.tsx @@ -17,8 +17,7 @@ limitations under the License. import React from "react"; import { fireEvent, render, RenderResult, waitFor } from "@testing-library/react"; -import PreferencesUserSettingsTab from - "../../../../../../src/components/views/settings/tabs/user/PreferencesUserSettingsTab"; +import PreferencesUserSettingsTab from "../../../../../../src/components/views/settings/tabs/user/PreferencesUserSettingsTab"; import { MatrixClientPeg } from "../../../../../../src/MatrixClientPeg"; import { mockPlatformPeg, stubClient } from "../../../../../test-utils"; import SettingsStore from "../../../../../../src/settings/SettingsStore"; @@ -62,12 +61,8 @@ describe("PreferencesUserSettingsTab", () => { }; }; - const expectSetValueToHaveBeenCalled = ( - name: string, - roomId: string, - level: SettingLevel, - value: boolean, - ) => expect(SettingsStore.setValue).toHaveBeenCalledWith(name, roomId, level, value); + const expectSetValueToHaveBeenCalled = (name: string, roomId: string, level: SettingLevel, value: boolean) => + expect(SettingsStore.setValue).toHaveBeenCalledWith(name, roomId, level, value); describe("with server support", () => { beforeEach(() => { diff --git a/test/components/views/settings/tabs/user/SecurityUserSettingsTab-test.tsx b/test/components/views/settings/tabs/user/SecurityUserSettingsTab-test.tsx index 3fc250facc..27b08fcbc9 100644 --- a/test/components/views/settings/tabs/user/SecurityUserSettingsTab-test.tsx +++ b/test/components/views/settings/tabs/user/SecurityUserSettingsTab-test.tsx @@ -13,12 +13,12 @@ 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 { fireEvent, render } from '@testing-library/react'; -import React from 'react'; +import { fireEvent, render } from "@testing-library/react"; +import React from "react"; import SecurityUserSettingsTab from "../../../../../../src/components/views/settings/tabs/user/SecurityUserSettingsTab"; -import MatrixClientContext from '../../../../../../src/contexts/MatrixClientContext'; -import SettingsStore from '../../../../../../src/settings/SettingsStore'; +import MatrixClientContext from "../../../../../../src/contexts/MatrixClientContext"; +import SettingsStore from "../../../../../../src/settings/SettingsStore"; import { getMockClientWithEventEmitter, mockClientMethodsServer, @@ -27,15 +27,15 @@ import { mockClientMethodsDevice, mockPlatformPeg, flushPromises, -} from '../../../../../test-utils'; +} from "../../../../../test-utils"; -describe('', () => { +describe("", () => { const defaultProps = { closeSettingsFn: jest.fn(), }; - const userId = '@alice:server.org'; - const deviceId = 'alices-device'; + const userId = "@alice:server.org"; + const deviceId = "alices-device"; const mockClient = getMockClientWithEventEmitter({ ...mockClientMethodsUser(userId), ...mockClientMethodsServer(), @@ -45,18 +45,19 @@ describe('', () => { getIgnoredUsers: jest.fn(), getVersions: jest.fn().mockResolvedValue({ unstable_features: { - 'org.matrix.msc3882': true, - 'org.matrix.msc3886': true, + "org.matrix.msc3882": true, + "org.matrix.msc3886": true, }, }), }); - const getComponent = () => + const getComponent = () => ( - ; + + ); - const settingsValueSpy = jest.spyOn(SettingsStore, 'getValue'); + const settingsValueSpy = jest.spyOn(SettingsStore, "getValue"); beforeEach(() => { mockPlatformPeg(); @@ -64,46 +65,46 @@ describe('', () => { settingsValueSpy.mockReturnValue(false); }); - it('renders sessions section when new session manager is disabled', () => { + it("renders sessions section when new session manager is disabled", () => { settingsValueSpy.mockReturnValue(false); const { getByTestId } = render(getComponent()); - expect(getByTestId('devices-section')).toBeTruthy(); + expect(getByTestId("devices-section")).toBeTruthy(); }); - it('does not render sessions section when new session manager is enabled', () => { + it("does not render sessions section when new session manager is enabled", () => { settingsValueSpy.mockReturnValue(true); const { queryByTestId } = render(getComponent()); - expect(queryByTestId('devices-section')).toBeFalsy(); + expect(queryByTestId("devices-section")).toBeFalsy(); }); - it('does not render qr code login section when disabled', () => { + it("does not render qr code login section when disabled", () => { settingsValueSpy.mockReturnValue(false); const { queryByText } = render(getComponent()); - expect(settingsValueSpy).toHaveBeenCalledWith('feature_qr_signin_reciprocate_show'); + expect(settingsValueSpy).toHaveBeenCalledWith("feature_qr_signin_reciprocate_show"); - expect(queryByText('Sign in with QR code')).toBeFalsy(); + expect(queryByText("Sign in with QR code")).toBeFalsy(); }); - it('renders qr code login section when enabled', async () => { - settingsValueSpy.mockImplementation(settingName => settingName === 'feature_qr_signin_reciprocate_show'); + it("renders qr code login section when enabled", async () => { + settingsValueSpy.mockImplementation((settingName) => settingName === "feature_qr_signin_reciprocate_show"); const { getByText } = render(getComponent()); // wait for versions call to settle await flushPromises(); - expect(getByText('Sign in with QR code')).toBeTruthy(); + expect(getByText("Sign in with QR code")).toBeTruthy(); }); - it('enters qr code login section when show QR code button clicked', async () => { - settingsValueSpy.mockImplementation(settingName => settingName === 'feature_qr_signin_reciprocate_show'); + it("enters qr code login section when show QR code button clicked", async () => { + settingsValueSpy.mockImplementation((settingName) => settingName === "feature_qr_signin_reciprocate_show"); const { getByText, getByTestId } = render(getComponent()); // wait for versions call to settle await flushPromises(); - fireEvent.click(getByText('Show QR code')); + fireEvent.click(getByText("Show QR code")); expect(getByTestId("login-with-qr")).toBeTruthy(); }); diff --git a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx index 58839d5e96..56e0657b6e 100644 --- a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx +++ b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx @@ -14,14 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import { fireEvent, render, RenderResult } from '@testing-library/react'; -import { act } from 'react-dom/test-utils'; -import { DeviceInfo } from 'matrix-js-sdk/src/crypto/deviceinfo'; -import { logger } from 'matrix-js-sdk/src/logger'; -import { DeviceTrustLevel } from 'matrix-js-sdk/src/crypto/CrossSigning'; -import { VerificationRequest } from 'matrix-js-sdk/src/crypto/verification/request/VerificationRequest'; -import { sleep } from 'matrix-js-sdk/src/utils'; +import React from "react"; +import { fireEvent, render, RenderResult } from "@testing-library/react"; +import { act } from "react-dom/test-utils"; +import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo"; +import { logger } from "matrix-js-sdk/src/logger"; +import { DeviceTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning"; +import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import { sleep } from "matrix-js-sdk/src/utils"; import { ClientEvent, IMyDevice, @@ -30,48 +30,45 @@ import { PUSHER_DEVICE_ID, PUSHER_ENABLED, IAuthData, -} from 'matrix-js-sdk/src/matrix'; +} from "matrix-js-sdk/src/matrix"; -import SessionManagerTab from '../../../../../../src/components/views/settings/tabs/user/SessionManagerTab'; -import MatrixClientContext from '../../../../../../src/contexts/MatrixClientContext'; +import SessionManagerTab from "../../../../../../src/components/views/settings/tabs/user/SessionManagerTab"; +import MatrixClientContext from "../../../../../../src/contexts/MatrixClientContext"; import { flushPromises, getMockClientWithEventEmitter, mkPusher, mockClientMethodsUser, mockPlatformPeg, -} from '../../../../../test-utils'; -import Modal from '../../../../../../src/Modal'; -import LogoutDialog from '../../../../../../src/components/views/dialogs/LogoutDialog'; -import { - DeviceSecurityVariation, - ExtendedDevice, -} from '../../../../../../src/components/views/settings/devices/types'; -import { INACTIVE_DEVICE_AGE_MS } from '../../../../../../src/components/views/settings/devices/filter'; -import SettingsStore from '../../../../../../src/settings/SettingsStore'; +} from "../../../../../test-utils"; +import Modal from "../../../../../../src/Modal"; +import LogoutDialog from "../../../../../../src/components/views/dialogs/LogoutDialog"; +import { DeviceSecurityVariation, ExtendedDevice } from "../../../../../../src/components/views/settings/devices/types"; +import { INACTIVE_DEVICE_AGE_MS } from "../../../../../../src/components/views/settings/devices/filter"; +import SettingsStore from "../../../../../../src/settings/SettingsStore"; mockPlatformPeg(); -describe('', () => { - const aliceId = '@alice:server.org'; - const deviceId = 'alices_device'; +describe("", () => { + const aliceId = "@alice:server.org"; + const deviceId = "alices_device"; const alicesDevice = { device_id: deviceId, - display_name: 'Alices device', + display_name: "Alices device", }; const alicesMobileDevice = { - device_id: 'alices_mobile_device', + device_id: "alices_mobile_device", last_seen_ts: Date.now(), }; const alicesOlderMobileDevice = { - device_id: 'alices_older_mobile_device', + device_id: "alices_older_mobile_device", last_seen_ts: Date.now() - 600000, }; const alicesInactiveDevice = { - device_id: 'alices_older_inactive_mobile_device', + device_id: "alices_older_inactive_mobile_device", last_seen_ts: Date.now() - (INACTIVE_DEVICE_AGE_MS + 1000), }; @@ -98,69 +95,65 @@ describe('', () => { }); const defaultProps = {}; - const getComponent = (props = {}): React.ReactElement => - ( - - - - ); + const getComponent = (props = {}): React.ReactElement => ( + + + + ); const toggleDeviceDetails = ( - getByTestId: ReturnType['getByTestId'], - deviceId: ExtendedDevice['device_id'], + getByTestId: ReturnType["getByTestId"], + deviceId: ExtendedDevice["device_id"], isOpen?: boolean, ): void => { // open device detail const tile = getByTestId(`device-tile-${deviceId}`); - const label = isOpen ? 'Hide details' : 'Show details'; + const label = isOpen ? "Hide details" : "Show details"; const toggle = tile.querySelector(`[aria-label="${label}"]`) as Element; fireEvent.click(toggle); }; const toggleDeviceSelection = ( - getByTestId: ReturnType['getByTestId'], - deviceId: ExtendedDevice['device_id'], + getByTestId: ReturnType["getByTestId"], + deviceId: ExtendedDevice["device_id"], ): void => { const checkbox = getByTestId(`device-tile-checkbox-${deviceId}`); fireEvent.click(checkbox); }; const getDeviceTile = ( - getByTestId: ReturnType['getByTestId'], - deviceId: ExtendedDevice['device_id'], + getByTestId: ReturnType["getByTestId"], + deviceId: ExtendedDevice["device_id"], ): HTMLElement => { return getByTestId(`device-tile-${deviceId}`); }; - const setFilter = async ( - container: HTMLElement, - option: DeviceSecurityVariation | string, - ) => await act(async () => { - const dropdown = container.querySelector('[aria-label="Filter devices"]'); + const setFilter = async (container: HTMLElement, option: DeviceSecurityVariation | string) => + await act(async () => { + const dropdown = container.querySelector('[aria-label="Filter devices"]'); - fireEvent.click(dropdown as Element); - // tick to let dropdown render - await flushPromises(); + fireEvent.click(dropdown as Element); + // tick to let dropdown render + await flushPromises(); - fireEvent.click(container.querySelector(`#device-list-filter__${option}`) as Element); - }); + fireEvent.click(container.querySelector(`#device-list-filter__${option}`) as Element); + }); const isDeviceSelected = ( - getByTestId: ReturnType['getByTestId'], - deviceId: ExtendedDevice['device_id'], + getByTestId: ReturnType["getByTestId"], + deviceId: ExtendedDevice["device_id"], ): boolean => !!(getByTestId(`device-tile-checkbox-${deviceId}`) as HTMLInputElement).checked; - const isSelectAllChecked = ( - getByTestId: ReturnType['getByTestId'], - ): boolean => !!(getByTestId('device-select-all-checkbox') as HTMLInputElement).checked; + const isSelectAllChecked = (getByTestId: ReturnType["getByTestId"]): boolean => + !!(getByTestId("device-select-all-checkbox") as HTMLInputElement).checked; const confirmSignout = async ( - getByTestId: ReturnType['getByTestId'], + getByTestId: ReturnType["getByTestId"], confirm = true, ): Promise => { // modal has sleeps in rendering process :( await sleep(100); - const buttonId = confirm ? 'dialog-primary-button' : 'dialog-cancel-button'; + const buttonId = confirm ? "dialog-primary-button" : "dialog-cancel-button"; fireEvent.click(getByTestId(buttonId)); // flush the confirmation promise @@ -169,52 +162,48 @@ describe('', () => { beforeEach(() => { jest.clearAllMocks(); - jest.spyOn(logger, 'error').mockRestore(); + jest.spyOn(logger, "error").mockRestore(); mockClient.getStoredDevice.mockImplementation((_userId, id) => { - const device = [alicesDevice, alicesMobileDevice].find(device => device.device_id === id); + const device = [alicesDevice, alicesMobileDevice].find((device) => device.device_id === id); return device ? new DeviceInfo(device.device_id) : null; }); mockCrossSigningInfo.checkDeviceTrust .mockReset() .mockReturnValue(new DeviceTrustLevel(false, false, false, false)); - mockClient.getDevices - .mockReset() - .mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] }); + mockClient.getDevices.mockReset().mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] }); - mockClient.getPushers - .mockReset() - .mockResolvedValue({ - pushers: [mkPusher({ + mockClient.getPushers.mockReset().mockResolvedValue({ + pushers: [ + mkPusher({ [PUSHER_DEVICE_ID.name]: alicesMobileDevice.device_id, [PUSHER_ENABLED.name]: true, - })], - }); + }), + ], + }); - mockClient.getAccountData - .mockReset() - .mockImplementation(eventType => { - if (eventType.startsWith(LOCAL_NOTIFICATION_SETTINGS_PREFIX.name)) { - return new MatrixEvent({ - type: eventType, - content: { - is_silenced: false, - }, - }); - } - }); + mockClient.getAccountData.mockReset().mockImplementation((eventType) => { + if (eventType.startsWith(LOCAL_NOTIFICATION_SETTINGS_PREFIX.name)) { + return new MatrixEvent({ + type: eventType, + content: { + is_silenced: false, + }, + }); + } + }); // sometimes a verification modal is in modal state when these tests run // make sure the coast is clear - Modal.closeCurrentModal(''); + Modal.closeCurrentModal(""); }); - it('renders spinner while devices load', () => { + it("renders spinner while devices load", () => { const { container } = render(getComponent()); - expect(container.getElementsByClassName('mx_Spinner').length).toBeTruthy(); + expect(container.getElementsByClassName("mx_Spinner").length).toBeTruthy(); }); - it('removes spinner when device fetch fails', async () => { + it("removes spinner when device fetch fails", async () => { mockClient.getDevices.mockRejectedValue({ httpStatus: 404 }); const { container } = render(getComponent()); expect(mockClient.getDevices).toHaveBeenCalled(); @@ -222,26 +211,28 @@ describe('', () => { await act(async () => { await flushPromises(); }); - expect(container.getElementsByClassName('mx_Spinner').length).toBeFalsy(); + expect(container.getElementsByClassName("mx_Spinner").length).toBeFalsy(); }); - it('removes spinner when device fetch fails', async () => { + it("removes spinner when device fetch fails", async () => { // eat the expected error log - jest.spyOn(logger, 'error').mockImplementation(() => {}); + jest.spyOn(logger, "error").mockImplementation(() => {}); mockClient.getDevices.mockRejectedValue({ httpStatus: 404 }); const { container } = render(getComponent()); await act(async () => { await flushPromises(); }); - expect(container.getElementsByClassName('mx_Spinner').length).toBeFalsy(); + expect(container.getElementsByClassName("mx_Spinner").length).toBeFalsy(); }); - it('does not fail when checking device verification fails', async () => { - const logSpy = jest.spyOn(logger, 'error').mockImplementation(() => {}); + it("does not fail when checking device verification fails", async () => { + const logSpy = jest.spyOn(logger, "error").mockImplementation(() => {}); mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] }); const noCryptoError = new Error("End-to-end encryption disabled"); - mockClient.getStoredDevice.mockImplementation(() => { throw noCryptoError; }); + mockClient.getStoredDevice.mockImplementation(() => { + throw noCryptoError; + }); render(getComponent()); await act(async () => { @@ -251,27 +242,26 @@ describe('', () => { // called for each device despite error expect(mockClient.getStoredDevice).toHaveBeenCalledWith(aliceId, alicesDevice.device_id); expect(mockClient.getStoredDevice).toHaveBeenCalledWith(aliceId, alicesMobileDevice.device_id); - expect(logSpy).toHaveBeenCalledWith('Error getting device cross-signing info', noCryptoError); + expect(logSpy).toHaveBeenCalledWith("Error getting device cross-signing info", noCryptoError); }); - it('sets device verification status correctly', async () => { - mockClient.getDevices.mockResolvedValue({ devices: - [alicesDevice, alicesMobileDevice, alicesOlderMobileDevice], + it("sets device verification status correctly", async () => { + mockClient.getDevices.mockResolvedValue({ + devices: [alicesDevice, alicesMobileDevice, alicesOlderMobileDevice], }); mockClient.getStoredDevice.mockImplementation((_userId, deviceId) => new DeviceInfo(deviceId)); - mockCrossSigningInfo.checkDeviceTrust - .mockImplementation((_userId, { deviceId }) => { - // alices device is trusted - if (deviceId === alicesDevice.device_id) { - return new DeviceTrustLevel(true, true, false, false); - } - // alices mobile device is not - if (deviceId === alicesMobileDevice.device_id) { - return new DeviceTrustLevel(false, false, false, false); - } - // alicesOlderMobileDevice does not support encryption - throw new Error('encryption not supported'); - }); + mockCrossSigningInfo.checkDeviceTrust.mockImplementation((_userId, { deviceId }) => { + // alices device is trusted + if (deviceId === alicesDevice.device_id) { + return new DeviceTrustLevel(true, true, false, false); + } + // alices mobile device is not + if (deviceId === alicesMobileDevice.device_id) { + return new DeviceTrustLevel(false, false, false, false); + } + // alicesOlderMobileDevice does not support encryption + throw new Error("encryption not supported"); + }); const { getByTestId } = render(getComponent()); @@ -281,27 +271,24 @@ describe('', () => { expect(mockCrossSigningInfo.checkDeviceTrust).toHaveBeenCalledTimes(3); expect( - getByTestId(`device-tile-${alicesDevice.device_id}`) - .querySelector('[aria-label="Verified"]'), + getByTestId(`device-tile-${alicesDevice.device_id}`).querySelector('[aria-label="Verified"]'), ).toBeTruthy(); expect( - getByTestId(`device-tile-${alicesMobileDevice.device_id}`) - .querySelector('[aria-label="Unverified"]'), + getByTestId(`device-tile-${alicesMobileDevice.device_id}`).querySelector('[aria-label="Unverified"]'), ).toBeTruthy(); // sessions that dont support encryption use unverified badge expect( - getByTestId(`device-tile-${alicesOlderMobileDevice.device_id}`) - .querySelector('[aria-label="Unverified"]'), + getByTestId(`device-tile-${alicesOlderMobileDevice.device_id}`).querySelector('[aria-label="Unverified"]'), ).toBeTruthy(); }); - it('extends device with client information when available', async () => { + it("extends device with client information when available", async () => { mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] }); mockClient.getAccountData.mockImplementation((eventType: string) => { const content = { - name: 'Element Web', - version: '1.2.3', - url: 'test.com', + name: "Element Web", + version: "1.2.3", + url: "test.com", }; return new MatrixEvent({ type: eventType, @@ -320,10 +307,10 @@ describe('', () => { toggleDeviceDetails(getByTestId, alicesDevice.device_id); // application metadata section rendered - expect(getByTestId('device-detail-metadata-application')).toBeTruthy(); + expect(getByTestId("device-detail-metadata-application")).toBeTruthy(); }); - it('renders devices without available client information without error', async () => { + it("renders devices without available client information without error", async () => { mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] }); const { getByTestId, queryByTestId } = render(getComponent()); @@ -334,10 +321,10 @@ describe('', () => { toggleDeviceDetails(getByTestId, alicesDevice.device_id); // application metadata section not rendered - expect(queryByTestId('device-detail-metadata-application')).toBeFalsy(); + expect(queryByTestId("device-detail-metadata-application")).toBeFalsy(); }); - it('does not render other sessions section when user has only one device', async () => { + it("does not render other sessions section when user has only one device", async () => { mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice] }); const { queryByTestId } = render(getComponent()); @@ -345,10 +332,10 @@ describe('', () => { await flushPromises(); }); - expect(queryByTestId('other-sessions-section')).toBeFalsy(); + expect(queryByTestId("other-sessions-section")).toBeFalsy(); }); - it('renders other sessions section when user has more than one device', async () => { + it("renders other sessions section when user has more than one device", async () => { mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesOlderMobileDevice, alicesMobileDevice], }); @@ -358,10 +345,10 @@ describe('', () => { await flushPromises(); }); - expect(getByTestId('other-sessions-section')).toBeTruthy(); + expect(getByTestId("other-sessions-section")).toBeTruthy(); }); - it('goes to filtered list from security recommendations', async () => { + it("goes to filtered list from security recommendations", async () => { mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] }); const { getByTestId, container } = render(getComponent()); @@ -369,32 +356,32 @@ describe('', () => { await flushPromises(); }); - fireEvent.click(getByTestId('unverified-devices-cta')); + fireEvent.click(getByTestId("unverified-devices-cta")); // our session manager waits a tick for rerender await flushPromises(); // unverified filter is set - expect(container.querySelector('.mx_FilteredDeviceListHeader')).toMatchSnapshot(); + expect(container.querySelector(".mx_FilteredDeviceListHeader")).toMatchSnapshot(); }); - describe('current session section', () => { - it('disables current session context menu while devices are loading', () => { + describe("current session section", () => { + it("disables current session context menu while devices are loading", () => { const { getByTestId } = render(getComponent()); - expect(getByTestId('current-session-menu').getAttribute('aria-disabled')).toBeTruthy(); + expect(getByTestId("current-session-menu").getAttribute("aria-disabled")).toBeTruthy(); }); - it('disables current session context menu when there is no current device', async () => { + it("disables current session context menu when there is no current device", async () => { mockClient.getDevices.mockResolvedValue({ devices: [] }); const { getByTestId } = render(getComponent()); await act(async () => { await flushPromises(); }); - expect(getByTestId('current-session-menu').getAttribute('aria-disabled')).toBeTruthy(); + expect(getByTestId("current-session-menu").getAttribute("aria-disabled")).toBeTruthy(); }); - it('renders current session section with an unverified session', async () => { + it("renders current session section with an unverified session", async () => { mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] }); const { getByTestId } = render(getComponent()); @@ -402,13 +389,13 @@ describe('', () => { await flushPromises(); }); - expect(getByTestId('current-session-section')).toMatchSnapshot(); + expect(getByTestId("current-session-section")).toMatchSnapshot(); }); - it('opens encryption setup dialog when verifiying current session', async () => { + it("opens encryption setup dialog when verifiying current session", async () => { mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] }); const { getByTestId } = render(getComponent()); - const modalSpy = jest.spyOn(Modal, 'createDialog'); + const modalSpy = jest.spyOn(Modal, "createDialog"); await act(async () => { await flushPromises(); @@ -420,11 +407,10 @@ describe('', () => { expect(modalSpy).toHaveBeenCalled(); }); - it('renders current session section with a verified session', async () => { + it("renders current session section with a verified session", async () => { mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] }); mockClient.getStoredDevice.mockImplementation(() => new DeviceInfo(alicesDevice.device_id)); - mockCrossSigningInfo.checkDeviceTrust - .mockReturnValue(new DeviceTrustLevel(true, true, false, false)); + mockCrossSigningInfo.checkDeviceTrust.mockReturnValue(new DeviceTrustLevel(true, true, false, false)); const { getByTestId } = render(getComponent()); @@ -432,12 +418,12 @@ describe('', () => { await flushPromises(); }); - expect(getByTestId('current-session-section')).toMatchSnapshot(); + expect(getByTestId("current-session-section")).toMatchSnapshot(); }); }); - describe('device detail expansion', () => { - it('renders no devices expanded by default', async () => { + describe("device detail expansion", () => { + it("renders no devices expanded by default", async () => { mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesOlderMobileDevice, alicesMobileDevice], }); @@ -447,13 +433,13 @@ describe('', () => { await flushPromises(); }); - const otherSessionsSection = getByTestId('other-sessions-section'); + const otherSessionsSection = getByTestId("other-sessions-section"); // no expanded device details - expect(otherSessionsSection.getElementsByClassName('mx_DeviceDetails').length).toBeFalsy(); + expect(otherSessionsSection.getElementsByClassName("mx_DeviceDetails").length).toBeFalsy(); }); - it('toggles device expansion on click', async () => { + it("toggles device expansion on click", async () => { mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesOlderMobileDevice, alicesMobileDevice], }); @@ -484,8 +470,8 @@ describe('', () => { }); }); - describe('Device verification', () => { - it('does not render device verification cta when current session is not verified', async () => { + describe("Device verification", () => { + it("does not render device verification cta when current session is not verified", async () => { mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesOlderMobileDevice, alicesMobileDevice], }); @@ -501,19 +487,18 @@ describe('', () => { expect(queryByTestId(`verification-status-button-${alicesOlderMobileDevice.device_id}`)).toBeFalsy(); }); - it('renders device verification cta on other sessions when current session is verified', async () => { - const modalSpy = jest.spyOn(Modal, 'createDialog'); + it("renders device verification cta on other sessions when current session is verified", async () => { + const modalSpy = jest.spyOn(Modal, "createDialog"); // make the current device verified mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] }); mockClient.getStoredDevice.mockImplementation((_userId, deviceId) => new DeviceInfo(deviceId)); - mockCrossSigningInfo.checkDeviceTrust - .mockImplementation((_userId, { deviceId }) => { - if (deviceId === alicesDevice.device_id) { - return new DeviceTrustLevel(true, true, false, false); - } - return new DeviceTrustLevel(false, false, false, false); - }); + mockCrossSigningInfo.checkDeviceTrust.mockImplementation((_userId, { deviceId }) => { + if (deviceId === alicesDevice.device_id) { + return new DeviceTrustLevel(true, true, false, false); + } + return new DeviceTrustLevel(false, false, false, false); + }); const { getByTestId } = render(getComponent()); @@ -530,23 +515,19 @@ describe('', () => { expect(modalSpy).toHaveBeenCalled(); }); - it('does not allow device verification on session that do not support encryption', async () => { + it("does not allow device verification on session that do not support encryption", async () => { mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] }); mockClient.getStoredDevice.mockImplementation((_userId, deviceId) => new DeviceInfo(deviceId)); - mockCrossSigningInfo.checkDeviceTrust - .mockImplementation((_userId, { deviceId }) => { - // current session verified = able to verify other sessions - if (deviceId === alicesDevice.device_id) { - return new DeviceTrustLevel(true, true, false, false); - } - // but alicesMobileDevice doesn't support encryption - throw new Error('encryption not supported'); - }); + mockCrossSigningInfo.checkDeviceTrust.mockImplementation((_userId, { deviceId }) => { + // current session verified = able to verify other sessions + if (deviceId === alicesDevice.device_id) { + return new DeviceTrustLevel(true, true, false, false); + } + // but alicesMobileDevice doesn't support encryption + throw new Error("encryption not supported"); + }); - const { - getByTestId, - queryByTestId, - } = render(getComponent()); + const { getByTestId, queryByTestId } = render(getComponent()); await act(async () => { await flushPromises(); @@ -557,24 +538,24 @@ describe('', () => { // no verify button expect(queryByTestId(`verification-status-button-${alicesMobileDevice.device_id}`)).toBeFalsy(); expect( - getByTestId(`device-detail-${alicesMobileDevice.device_id}`) - .getElementsByClassName('mx_DeviceSecurityCard'), + getByTestId(`device-detail-${alicesMobileDevice.device_id}`).getElementsByClassName( + "mx_DeviceSecurityCard", + ), ).toMatchSnapshot(); }); - it('refreshes devices after verifying other device', async () => { - const modalSpy = jest.spyOn(Modal, 'createDialog'); + it("refreshes devices after verifying other device", async () => { + const modalSpy = jest.spyOn(Modal, "createDialog"); // make the current device verified mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] }); mockClient.getStoredDevice.mockImplementation((_userId, deviceId) => new DeviceInfo(deviceId)); - mockCrossSigningInfo.checkDeviceTrust - .mockImplementation((_userId, { deviceId }) => { - if (deviceId === alicesDevice.device_id) { - return new DeviceTrustLevel(true, true, false, false); - } - return new DeviceTrustLevel(false, false, false, false); - }); + mockCrossSigningInfo.checkDeviceTrust.mockImplementation((_userId, { deviceId }) => { + if (deviceId === alicesDevice.device_id) { + return new DeviceTrustLevel(true, true, false, false); + } + return new DeviceTrustLevel(false, false, false, false); + }); const { getByTestId } = render(getComponent()); @@ -601,9 +582,9 @@ describe('', () => { }); }); - describe('Sign out', () => { - it('Signs out of current device', async () => { - const modalSpy = jest.spyOn(Modal, 'createDialog'); + describe("Sign out", () => { + it("Signs out of current device", async () => { + const modalSpy = jest.spyOn(Modal, "createDialog"); mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice] }); const { getByTestId } = render(getComponent()); @@ -614,7 +595,7 @@ describe('', () => { toggleDeviceDetails(getByTestId, alicesDevice.device_id); - const signOutButton = getByTestId('device-detail-sign-out-cta'); + const signOutButton = getByTestId("device-detail-sign-out-cta"); expect(signOutButton).toMatchSnapshot(); fireEvent.click(signOutButton); @@ -622,8 +603,8 @@ describe('', () => { expect(modalSpy).toHaveBeenCalledWith(LogoutDialog, {}, undefined, false, true); }); - it('Signs out of current device from kebab menu', async () => { - const modalSpy = jest.spyOn(Modal, 'createDialog'); + it("Signs out of current device from kebab menu", async () => { + const modalSpy = jest.spyOn(Modal, "createDialog"); mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice] }); const { getByTestId, getByLabelText } = render(getComponent()); @@ -631,14 +612,14 @@ describe('', () => { await flushPromises(); }); - fireEvent.click(getByTestId('current-session-menu')); - fireEvent.click(getByLabelText('Sign out')); + fireEvent.click(getByTestId("current-session-menu")); + fireEvent.click(getByLabelText("Sign out")); // logout dialog opened expect(modalSpy).toHaveBeenCalledWith(LogoutDialog, {}, undefined, false, true); }); - it('does not render sign out other devices option when only one device', async () => { + it("does not render sign out other devices option when only one device", async () => { mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice] }); const { getByTestId, queryByLabelText } = render(getComponent()); @@ -646,38 +627,39 @@ describe('', () => { await flushPromises(); }); - fireEvent.click(getByTestId('current-session-menu')); - expect(queryByLabelText('Sign out all other sessions')).toBeFalsy(); + fireEvent.click(getByTestId("current-session-menu")); + expect(queryByLabelText("Sign out all other sessions")).toBeFalsy(); }); - it('signs out of all other devices from current session context menu', async () => { - mockClient.getDevices.mockResolvedValue({ devices: [ - alicesDevice, alicesMobileDevice, alicesOlderMobileDevice, - ] }); + it("signs out of all other devices from current session context menu", async () => { + mockClient.getDevices.mockResolvedValue({ + devices: [alicesDevice, alicesMobileDevice, alicesOlderMobileDevice], + }); const { getByTestId, getByLabelText } = render(getComponent()); await act(async () => { await flushPromises(); }); - fireEvent.click(getByTestId('current-session-menu')); - fireEvent.click(getByLabelText('Sign out all other sessions')); + fireEvent.click(getByTestId("current-session-menu")); + fireEvent.click(getByLabelText("Sign out all other sessions")); await confirmSignout(getByTestId); // other devices deleted, excluding current device - expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith([ - alicesMobileDevice.device_id, alicesOlderMobileDevice.device_id, - ], undefined); + expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith( + [alicesMobileDevice.device_id, alicesOlderMobileDevice.device_id], + undefined, + ); }); - describe('other devices', () => { + describe("other devices", () => { const interactiveAuthError = { httpStatus: 401, data: { flows: [{ stages: ["m.login.password"] }] } }; beforeEach(() => { mockClient.deleteMultipleDevices.mockReset(); }); - it('deletes a device when interactive auth is not required', async () => { + it("deletes a device when interactive auth is not required", async () => { mockClient.deleteMultipleDevices.mockResolvedValue({}); mockClient.getDevices .mockResolvedValueOnce({ devices: [alicesDevice, alicesMobileDevice, alicesOlderMobileDevice] }) @@ -701,12 +683,15 @@ describe('', () => { await confirmSignout(getByTestId); // sign out button is disabled with spinner - expect((deviceDetails.querySelector( - '[data-testid="device-detail-sign-out-cta"]', - ) as Element).getAttribute('aria-disabled')).toEqual("true"); + expect( + (deviceDetails.querySelector('[data-testid="device-detail-sign-out-cta"]') as Element).getAttribute( + "aria-disabled", + ), + ).toEqual("true"); // delete called expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith( - [alicesMobileDevice.device_id], undefined, + [alicesMobileDevice.device_id], + undefined, ); await flushPromises(); @@ -715,7 +700,7 @@ describe('', () => { expect(mockClient.getDevices).toHaveBeenCalled(); }); - it('deletes a device when interactive auth is not required', async () => { + it("deletes a device when interactive auth is not required", async () => { const { getByTestId } = render(getComponent()); await act(async () => { @@ -733,14 +718,16 @@ describe('', () => { await confirmSignout(getByTestId, false); // doesnt enter loading state - expect((deviceDetails.querySelector( - '[data-testid="device-detail-sign-out-cta"]', - ) as Element).getAttribute('aria-disabled')).toEqual(null); + expect( + (deviceDetails.querySelector('[data-testid="device-detail-sign-out-cta"]') as Element).getAttribute( + "aria-disabled", + ), + ).toEqual(null); // delete not called expect(mockClient.deleteMultipleDevices).not.toHaveBeenCalled(); }); - it('deletes a device when interactive auth is required', async () => { + it("deletes a device when interactive auth is required", async () => { mockClient.deleteMultipleDevices // require auth .mockRejectedValueOnce(interactiveAuthError) @@ -775,39 +762,45 @@ describe('', () => { await sleep(100); expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith( - [alicesMobileDevice.device_id], undefined, + [alicesMobileDevice.device_id], + undefined, ); - const modal = document.getElementsByClassName('mx_Dialog'); + const modal = document.getElementsByClassName("mx_Dialog"); expect(modal.length).toBeTruthy(); // fill password and submit for interactive auth act(() => { - fireEvent.change(getByLabelText('Password'), { target: { value: 'topsecret' } }); - fireEvent.submit(getByLabelText('Password')); + fireEvent.change(getByLabelText("Password"), { target: { value: "topsecret" } }); + fireEvent.submit(getByLabelText("Password")); }); await flushPromises(); // called again with auth - expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith([alicesMobileDevice.device_id], - { identifier: { - type: "m.id.user", user: aliceId, - }, password: "", type: "m.login.password", user: aliceId, - }); + expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith([alicesMobileDevice.device_id], { + identifier: { + type: "m.id.user", + user: aliceId, + }, + password: "", + type: "m.login.password", + user: aliceId, + }); // devices refreshed expect(mockClient.getDevices).toHaveBeenCalled(); }); - it('clears loading state when device deletion is cancelled during interactive auth', async () => { + it("clears loading state when device deletion is cancelled during interactive auth", async () => { mockClient.deleteMultipleDevices // require auth .mockRejectedValueOnce(interactiveAuthError) // then succeed .mockResolvedValueOnce({}); - mockClient.getDevices - .mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice, alicesOlderMobileDevice] }); + mockClient.getDevices.mockResolvedValue({ + devices: [alicesDevice, alicesMobileDevice, alicesOlderMobileDevice], + }); const { getByTestId, getByLabelText } = render(getComponent()); @@ -825,9 +818,11 @@ describe('', () => { await confirmSignout(getByTestId); // button is loading - expect((deviceDetails.querySelector( - '[data-testid="device-detail-sign-out-cta"]', - ) as Element).getAttribute('aria-disabled')).toEqual("true"); + expect( + (deviceDetails.querySelector('[data-testid="device-detail-sign-out-cta"]') as Element).getAttribute( + "aria-disabled", + ), + ).toEqual("true"); await flushPromises(); @@ -837,15 +832,16 @@ describe('', () => { await sleep(0); expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith( - [alicesMobileDevice.device_id], undefined, + [alicesMobileDevice.device_id], + undefined, ); - const modal = document.getElementsByClassName('mx_Dialog'); + const modal = document.getElementsByClassName("mx_Dialog"); expect(modal.length).toBeTruthy(); // cancel iau by closing modal act(() => { - fireEvent.click(getByLabelText('Close dialog')); + fireEvent.click(getByLabelText("Close dialog")); }); await flushPromises(); @@ -856,22 +852,23 @@ describe('', () => { expect(mockClient.getDevices).toHaveBeenCalledTimes(1); // loading state cleared - expect((deviceDetails.querySelector( - '[data-testid="device-detail-sign-out-cta"]', - ) as Element).getAttribute('aria-disabled')).toEqual(null); + expect( + (deviceDetails.querySelector('[data-testid="device-detail-sign-out-cta"]') as Element).getAttribute( + "aria-disabled", + ), + ).toEqual(null); }); - it('deletes multiple devices', async () => { - mockClient.getDevices.mockResolvedValue({ devices: [ - alicesDevice, alicesMobileDevice, alicesOlderMobileDevice, - alicesInactiveDevice, - ] }); + it("deletes multiple devices", async () => { + mockClient.getDevices.mockResolvedValue({ + devices: [alicesDevice, alicesMobileDevice, alicesOlderMobileDevice, alicesInactiveDevice], + }); // get a handle for resolving the delete call // because promise flushing after the confirm modal is resolving this too // and we want to test the loading state here let resolveDeleteRequest; mockClient.deleteMultipleDevices.mockImplementation(() => { - const promise = new Promise(resolve => { + const promise = new Promise((resolve) => { resolveDeleteRequest = resolve; }); return promise; @@ -886,34 +883,31 @@ describe('', () => { toggleDeviceSelection(getByTestId, alicesMobileDevice.device_id); toggleDeviceSelection(getByTestId, alicesOlderMobileDevice.device_id); - fireEvent.click(getByTestId('sign-out-selection-cta')); + fireEvent.click(getByTestId("sign-out-selection-cta")); await confirmSignout(getByTestId); // buttons disabled in list header - expect(getByTestId('sign-out-selection-cta').getAttribute('aria-disabled')).toBeTruthy(); - expect(getByTestId('cancel-selection-cta').getAttribute('aria-disabled')).toBeTruthy(); + expect(getByTestId("sign-out-selection-cta").getAttribute("aria-disabled")).toBeTruthy(); + expect(getByTestId("cancel-selection-cta").getAttribute("aria-disabled")).toBeTruthy(); // spinner rendered in list header - expect(getByTestId('sign-out-selection-cta').querySelector('.mx_Spinner')).toBeTruthy(); + expect(getByTestId("sign-out-selection-cta").querySelector(".mx_Spinner")).toBeTruthy(); // spinners on signing out devices - expect(getDeviceTile( - getByTestId, alicesMobileDevice.device_id, - ).querySelector('.mx_Spinner')).toBeTruthy(); - expect(getDeviceTile( - getByTestId, alicesOlderMobileDevice.device_id, - ).querySelector('.mx_Spinner')).toBeTruthy(); + expect( + getDeviceTile(getByTestId, alicesMobileDevice.device_id).querySelector(".mx_Spinner"), + ).toBeTruthy(); + expect( + getDeviceTile(getByTestId, alicesOlderMobileDevice.device_id).querySelector(".mx_Spinner"), + ).toBeTruthy(); // no spinner for device that is not signing out - expect(getDeviceTile( - getByTestId, alicesInactiveDevice.device_id, - ).querySelector('.mx_Spinner')).toBeFalsy(); + expect( + getDeviceTile(getByTestId, alicesInactiveDevice.device_id).querySelector(".mx_Spinner"), + ).toBeFalsy(); // delete called with both ids expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith( - [ - alicesMobileDevice.device_id, - alicesOlderMobileDevice.device_id, - ], + [alicesMobileDevice.device_id, alicesOlderMobileDevice.device_id], undefined, ); @@ -922,60 +916,62 @@ describe('', () => { }); }); - describe('Rename sessions', () => { + describe("Rename sessions", () => { const updateDeviceName = async ( - getByTestId: RenderResult['getByTestId'], + getByTestId: RenderResult["getByTestId"], device: IMyDevice, newDeviceName: string, ) => { toggleDeviceDetails(getByTestId, device.device_id); // start editing - fireEvent.click(getByTestId('device-heading-rename-cta')); + fireEvent.click(getByTestId("device-heading-rename-cta")); - const input = getByTestId('device-rename-input'); + const input = getByTestId("device-rename-input"); fireEvent.change(input, { target: { value: newDeviceName } }); - fireEvent.click(getByTestId('device-rename-submit-cta')); + fireEvent.click(getByTestId("device-rename-submit-cta")); await flushPromises(); await flushPromises(); }; - it('renames current session', async () => { + it("renames current session", async () => { const { getByTestId } = render(getComponent()); await act(async () => { await flushPromises(); }); - const newDeviceName = 'new device name'; + const newDeviceName = "new device name"; await updateDeviceName(getByTestId, alicesDevice, newDeviceName); - expect(mockClient.setDeviceDetails).toHaveBeenCalledWith( - alicesDevice.device_id, { display_name: newDeviceName }); + expect(mockClient.setDeviceDetails).toHaveBeenCalledWith(alicesDevice.device_id, { + display_name: newDeviceName, + }); // devices refreshed expect(mockClient.getDevices).toHaveBeenCalledTimes(2); }); - it('renames other session', async () => { + it("renames other session", async () => { const { getByTestId } = render(getComponent()); await act(async () => { await flushPromises(); }); - const newDeviceName = 'new device name'; + const newDeviceName = "new device name"; await updateDeviceName(getByTestId, alicesMobileDevice, newDeviceName); - expect(mockClient.setDeviceDetails).toHaveBeenCalledWith( - alicesMobileDevice.device_id, { display_name: newDeviceName }); + expect(mockClient.setDeviceDetails).toHaveBeenCalledWith(alicesMobileDevice.device_id, { + display_name: newDeviceName, + }); // devices refreshed expect(mockClient.getDevices).toHaveBeenCalledTimes(2); }); - it('does not rename session or refresh devices is session name is unchanged', async () => { + it("does not rename session or refresh devices is session name is unchanged", async () => { const { getByTestId } = render(getComponent()); await act(async () => { @@ -989,22 +985,21 @@ describe('', () => { expect(mockClient.getDevices).toHaveBeenCalledTimes(1); }); - it('saves an empty session display name successfully', async () => { + it("saves an empty session display name successfully", async () => { const { getByTestId } = render(getComponent()); await act(async () => { await flushPromises(); }); - await updateDeviceName(getByTestId, alicesDevice, ''); + await updateDeviceName(getByTestId, alicesDevice, ""); - expect(mockClient.setDeviceDetails).toHaveBeenCalledWith( - alicesDevice.device_id, { display_name: '' }); + expect(mockClient.setDeviceDetails).toHaveBeenCalledWith(alicesDevice.device_id, { display_name: "" }); }); - it('displays an error when session display name fails to save', async () => { - const logSpy = jest.spyOn(logger, 'error'); - const error = new Error('oups'); + it("displays an error when session display name fails to save", async () => { + const logSpy = jest.spyOn(logger, "error"); + const error = new Error("oups"); mockClient.setDeviceDetails.mockRejectedValue(error); const { getByTestId } = render(getComponent()); @@ -1012,7 +1007,7 @@ describe('', () => { await flushPromises(); }); - const newDeviceName = 'new device name'; + const newDeviceName = "new device name"; await updateDeviceName(getByTestId, alicesDevice, newDeviceName); await flushPromises(); @@ -1020,18 +1015,18 @@ describe('', () => { expect(logSpy).toHaveBeenCalledWith("Error setting session display name", error); // error displayed - expect(getByTestId('device-rename-error')).toBeTruthy(); + expect(getByTestId("device-rename-error")).toBeTruthy(); }); }); - describe('Multiple selection', () => { + describe("Multiple selection", () => { beforeEach(() => { - mockClient.getDevices.mockResolvedValue({ devices: [ - alicesDevice, alicesMobileDevice, alicesOlderMobileDevice, - ] }); + mockClient.getDevices.mockResolvedValue({ + devices: [alicesDevice, alicesMobileDevice, alicesOlderMobileDevice], + }); }); - it('toggles session selection', async () => { + it("toggles session selection", async () => { const { getByTestId, getByText } = render(getComponent()); await act(async () => { @@ -1042,7 +1037,7 @@ describe('', () => { toggleDeviceSelection(getByTestId, alicesOlderMobileDevice.device_id); // header displayed correctly - expect(getByText('2 sessions selected')).toBeTruthy(); + expect(getByText("2 sessions selected")).toBeTruthy(); expect(isDeviceSelected(getByTestId, alicesMobileDevice.device_id)).toBeTruthy(); expect(isDeviceSelected(getByTestId, alicesOlderMobileDevice.device_id)).toBeTruthy(); @@ -1055,7 +1050,7 @@ describe('', () => { expect(isDeviceSelected(getByTestId, alicesOlderMobileDevice.device_id)).toBeTruthy(); }); - it('cancel button clears selection', async () => { + it("cancel button clears selection", async () => { const { getByTestId, getByText } = render(getComponent()); await act(async () => { @@ -1066,16 +1061,16 @@ describe('', () => { toggleDeviceSelection(getByTestId, alicesOlderMobileDevice.device_id); // header displayed correctly - expect(getByText('2 sessions selected')).toBeTruthy(); + expect(getByText("2 sessions selected")).toBeTruthy(); - fireEvent.click(getByTestId('cancel-selection-cta')); + fireEvent.click(getByTestId("cancel-selection-cta")); // unselected expect(isDeviceSelected(getByTestId, alicesMobileDevice.device_id)).toBeFalsy(); expect(isDeviceSelected(getByTestId, alicesOlderMobileDevice.device_id)).toBeFalsy(); }); - it('changing the filter clears selection', async () => { + it("changing the filter clears selection", async () => { const { getByTestId } = render(getComponent()); await act(async () => { @@ -1085,7 +1080,7 @@ describe('', () => { toggleDeviceSelection(getByTestId, alicesMobileDevice.device_id); expect(isDeviceSelected(getByTestId, alicesMobileDevice.device_id)).toBeTruthy(); - fireEvent.click(getByTestId('unverified-devices-cta')); + fireEvent.click(getByTestId("unverified-devices-cta")); // our session manager waits a tick for rerender await flushPromises(); @@ -1094,18 +1089,18 @@ describe('', () => { expect(isDeviceSelected(getByTestId, alicesOlderMobileDevice.device_id)).toBeFalsy(); }); - describe('toggling select all', () => { - it('selects all sessions when there is not existing selection', async () => { + describe("toggling select all", () => { + it("selects all sessions when there is not existing selection", async () => { const { getByTestId, getByText } = render(getComponent()); await act(async () => { await flushPromises(); }); - fireEvent.click(getByTestId('device-select-all-checkbox')); + fireEvent.click(getByTestId("device-select-all-checkbox")); // header displayed correctly - expect(getByText('2 sessions selected')).toBeTruthy(); + expect(getByText("2 sessions selected")).toBeTruthy(); expect(isSelectAllChecked(getByTestId)).toBeTruthy(); // devices selected @@ -1113,7 +1108,7 @@ describe('', () => { expect(isDeviceSelected(getByTestId, alicesOlderMobileDevice.device_id)).toBeTruthy(); }); - it('selects all sessions when some sessions are already selected', async () => { + it("selects all sessions when some sessions are already selected", async () => { const { getByTestId, getByText } = render(getComponent()); await act(async () => { @@ -1122,10 +1117,10 @@ describe('', () => { toggleDeviceSelection(getByTestId, alicesMobileDevice.device_id); - fireEvent.click(getByTestId('device-select-all-checkbox')); + fireEvent.click(getByTestId("device-select-all-checkbox")); // header displayed correctly - expect(getByText('2 sessions selected')).toBeTruthy(); + expect(getByText("2 sessions selected")).toBeTruthy(); expect(isSelectAllChecked(getByTestId)).toBeTruthy(); // devices selected @@ -1133,17 +1128,17 @@ describe('', () => { expect(isDeviceSelected(getByTestId, alicesOlderMobileDevice.device_id)).toBeTruthy(); }); - it('deselects all sessions when all sessions are selected', async () => { + it("deselects all sessions when all sessions are selected", async () => { const { getByTestId, getByText } = render(getComponent()); await act(async () => { await flushPromises(); }); - fireEvent.click(getByTestId('device-select-all-checkbox')); + fireEvent.click(getByTestId("device-select-all-checkbox")); // header displayed correctly - expect(getByText('2 sessions selected')).toBeTruthy(); + expect(getByText("2 sessions selected")).toBeTruthy(); expect(isSelectAllChecked(getByTestId)).toBeTruthy(); // devices selected @@ -1151,12 +1146,10 @@ describe('', () => { expect(isDeviceSelected(getByTestId, alicesOlderMobileDevice.device_id)).toBeTruthy(); }); - it('selects only sessions that are part of the active filter', async () => { - mockClient.getDevices.mockResolvedValue({ devices: [ - alicesDevice, - alicesMobileDevice, - alicesInactiveDevice, - ] }); + it("selects only sessions that are part of the active filter", async () => { + mockClient.getDevices.mockResolvedValue({ + devices: [alicesDevice, alicesMobileDevice, alicesInactiveDevice], + }); const { getByTestId, container } = render(getComponent()); await act(async () => { @@ -1167,19 +1160,17 @@ describe('', () => { await setFilter(container, DeviceSecurityVariation.Inactive); // select all inactive sessions - fireEvent.click(getByTestId('device-select-all-checkbox')); + fireEvent.click(getByTestId("device-select-all-checkbox")); expect(isSelectAllChecked(getByTestId)).toBeTruthy(); // sign out of all selected sessions - fireEvent.click(getByTestId('sign-out-selection-cta')); + fireEvent.click(getByTestId("sign-out-selection-cta")); await confirmSignout(getByTestId); // only called with session from active filter expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith( - [ - alicesInactiveDevice.device_id, - ], + [alicesInactiveDevice.device_id], undefined, ); }); @@ -1197,9 +1188,9 @@ describe('', () => { // device details are expanded expect(getByTestId(`device-detail-${alicesMobileDevice.device_id}`)).toBeTruthy(); - expect(getByTestId('device-detail-push-notification')).toBeTruthy(); + expect(getByTestId("device-detail-push-notification")).toBeTruthy(); - const checkbox = getByTestId('device-detail-push-notification-checkbox'); + const checkbox = getByTestId("device-detail-push-notification-checkbox"); expect(checkbox).toBeTruthy(); fireEvent.click(checkbox); @@ -1218,17 +1209,16 @@ describe('', () => { // device details are expanded expect(getByTestId(`device-detail-${alicesDevice.device_id}`)).toBeTruthy(); - expect(getByTestId('device-detail-push-notification')).toBeTruthy(); + expect(getByTestId("device-detail-push-notification")).toBeTruthy(); - const checkbox = getByTestId('device-detail-push-notification-checkbox'); + const checkbox = getByTestId("device-detail-push-notification-checkbox"); expect(checkbox).toBeTruthy(); fireEvent.click(checkbox); - expect(mockClient.setLocalNotificationSettings).toHaveBeenCalledWith( - alicesDevice.device_id, - { is_silenced: true }, - ); + expect(mockClient.setLocalNotificationSettings).toHaveBeenCalledWith(alicesDevice.device_id, { + is_silenced: true, + }); }); it("updates the UI when another session changes the local notifications", async () => { @@ -1242,13 +1232,13 @@ describe('', () => { // device details are expanded expect(getByTestId(`device-detail-${alicesDevice.device_id}`)).toBeTruthy(); - expect(getByTestId('device-detail-push-notification')).toBeTruthy(); + expect(getByTestId("device-detail-push-notification")).toBeTruthy(); - const checkbox = getByTestId('device-detail-push-notification-checkbox'); + const checkbox = getByTestId("device-detail-push-notification-checkbox"); expect(checkbox).toBeTruthy(); - expect(checkbox.getAttribute('aria-checked')).toEqual("true"); + expect(checkbox.getAttribute("aria-checked")).toEqual("true"); const evt = new MatrixEvent({ type: LOCAL_NOTIFICATION_SETTINGS_PREFIX.name + "." + alicesDevice.device_id, @@ -1261,11 +1251,11 @@ describe('', () => { mockClient.emit(ClientEvent.AccountData, evt); }); - expect(checkbox.getAttribute('aria-checked')).toEqual("false"); + expect(checkbox.getAttribute("aria-checked")).toEqual("false"); }); - describe('QR code login', () => { - const settingsValueSpy = jest.spyOn(SettingsStore, 'getValue'); + describe("QR code login", () => { + const settingsValueSpy = jest.spyOn(SettingsStore, "getValue"); beforeEach(() => { settingsValueSpy.mockClear().mockReturnValue(false); @@ -1273,38 +1263,38 @@ describe('', () => { mockClient.getVersions.mockResolvedValue({ versions: [], unstable_features: { - 'org.matrix.msc3882': true, - 'org.matrix.msc3886': true, + "org.matrix.msc3882": true, + "org.matrix.msc3886": true, }, }); }); - it('does not render qr code login section when disabled', () => { + it("does not render qr code login section when disabled", () => { settingsValueSpy.mockReturnValue(false); const { queryByText } = render(getComponent()); - expect(settingsValueSpy).toHaveBeenCalledWith('feature_qr_signin_reciprocate_show'); + expect(settingsValueSpy).toHaveBeenCalledWith("feature_qr_signin_reciprocate_show"); - expect(queryByText('Sign in with QR code')).toBeFalsy(); + expect(queryByText("Sign in with QR code")).toBeFalsy(); }); - it('renders qr code login section when enabled', async () => { - settingsValueSpy.mockImplementation(settingName => settingName === 'feature_qr_signin_reciprocate_show'); + it("renders qr code login section when enabled", async () => { + settingsValueSpy.mockImplementation((settingName) => settingName === "feature_qr_signin_reciprocate_show"); const { getByText } = render(getComponent()); // wait for versions call to settle await flushPromises(); - expect(getByText('Sign in with QR code')).toBeTruthy(); + expect(getByText("Sign in with QR code")).toBeTruthy(); }); - it('enters qr code login section when show QR code button clicked', async () => { - settingsValueSpy.mockImplementation(settingName => settingName === 'feature_qr_signin_reciprocate_show'); + it("enters qr code login section when show QR code button clicked", async () => { + settingsValueSpy.mockImplementation((settingName) => settingName === "feature_qr_signin_reciprocate_show"); const { getByText, getByTestId } = render(getComponent()); // wait for versions call to settle await flushPromises(); - fireEvent.click(getByText('Show QR code')); + fireEvent.click(getByText("Show QR code")); expect(getByTestId("login-with-qr")).toBeTruthy(); }); diff --git a/test/components/views/settings/tabs/user/VoiceUserSettingsTab-test.tsx b/test/components/views/settings/tabs/user/VoiceUserSettingsTab-test.tsx index c303efb8a7..9bc8b20517 100644 --- a/test/components/views/settings/tabs/user/VoiceUserSettingsTab-test.tsx +++ b/test/components/views/settings/tabs/user/VoiceUserSettingsTab-test.tsx @@ -14,31 +14,31 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import { mocked } from 'jest-mock'; -import { render } from '@testing-library/react'; +import React from "react"; +import { mocked } from "jest-mock"; +import { render } from "@testing-library/react"; -import VoiceUserSettingsTab from '../../../../../../src/components/views/settings/tabs/user/VoiceUserSettingsTab'; +import VoiceUserSettingsTab from "../../../../../../src/components/views/settings/tabs/user/VoiceUserSettingsTab"; import MediaDeviceHandler from "../../../../../../src/MediaDeviceHandler"; jest.mock("../../../../../../src/MediaDeviceHandler"); const MediaDeviceHandlerMock = mocked(MediaDeviceHandler); -describe('', () => { - const getComponent = (): React.ReactElement => (); +describe("", () => { + const getComponent = (): React.ReactElement => ; beforeEach(() => { jest.clearAllMocks(); }); - it('renders audio processing settings', () => { + it("renders audio processing settings", () => { const { getByTestId } = render(getComponent()); - expect(getByTestId('voice-auto-gain')).toBeTruthy(); - expect(getByTestId('voice-noise-suppression')).toBeTruthy(); - expect(getByTestId('voice-echo-cancellation')).toBeTruthy(); + expect(getByTestId("voice-auto-gain")).toBeTruthy(); + expect(getByTestId("voice-noise-suppression")).toBeTruthy(); + expect(getByTestId("voice-echo-cancellation")).toBeTruthy(); }); - it('sets and displays audio processing settings', () => { + it("sets and displays audio processing settings", () => { MediaDeviceHandlerMock.getAudioAutoGainControl.mockReturnValue(false); MediaDeviceHandlerMock.getAudioEchoCancellation.mockReturnValue(true); MediaDeviceHandlerMock.getAudioNoiseSuppression.mockReturnValue(false); diff --git a/test/components/views/spaces/QuickThemeSwitcher-test.tsx b/test/components/views/spaces/QuickThemeSwitcher-test.tsx index 28a0e3e954..ecd80a32c1 100644 --- a/test/components/views/spaces/QuickThemeSwitcher-test.tsx +++ b/test/components/views/spaces/QuickThemeSwitcher-test.tsx @@ -14,54 +14,56 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; // eslint-disable-next-line deprecate/import -import { mount } from 'enzyme'; -import { mocked } from 'jest-mock'; -import { act } from 'react-dom/test-utils'; +import { mount } from "enzyme"; +import { mocked } from "jest-mock"; +import { act } from "react-dom/test-utils"; -import QuickThemeSwitcher from '../../../../src/components/views/spaces/QuickThemeSwitcher'; -import { getOrderedThemes } from '../../../../src/theme'; -import ThemeChoicePanel from '../../../../src/components/views/settings/ThemeChoicePanel'; -import SettingsStore from '../../../../src/settings/SettingsStore'; -import { findById } from '../../../test-utils'; -import { SettingLevel } from '../../../../src/settings/SettingLevel'; -import dis from '../../../../src/dispatcher/dispatcher'; -import { Action } from '../../../../src/dispatcher/actions'; -import { mockPlatformPeg } from '../../../test-utils/platform'; +import QuickThemeSwitcher from "../../../../src/components/views/spaces/QuickThemeSwitcher"; +import { getOrderedThemes } from "../../../../src/theme"; +import ThemeChoicePanel from "../../../../src/components/views/settings/ThemeChoicePanel"; +import SettingsStore from "../../../../src/settings/SettingsStore"; +import { findById } from "../../../test-utils"; +import { SettingLevel } from "../../../../src/settings/SettingLevel"; +import dis from "../../../../src/dispatcher/dispatcher"; +import { Action } from "../../../../src/dispatcher/actions"; +import { mockPlatformPeg } from "../../../test-utils/platform"; -jest.mock('../../../../src/theme'); -jest.mock('../../../../src/components/views/settings/ThemeChoicePanel', () => ({ +jest.mock("../../../../src/theme"); +jest.mock("../../../../src/components/views/settings/ThemeChoicePanel", () => ({ calculateThemeState: jest.fn(), })); -jest.mock('../../../../src/settings/SettingsStore', () => ({ +jest.mock("../../../../src/settings/SettingsStore", () => ({ setValue: jest.fn(), getValue: jest.fn(), monitorSetting: jest.fn(), watchSetting: jest.fn(), })); -jest.mock('../../../../src/dispatcher/dispatcher', () => ({ +jest.mock("../../../../src/dispatcher/dispatcher", () => ({ dispatch: jest.fn(), register: jest.fn(), })); mockPlatformPeg({ overrideBrowserShortcuts: jest.fn().mockReturnValue(false) }); -describe('', () => { +describe("", () => { const defaultProps = { requestClose: jest.fn(), }; - const getComponent = (props = {}) => - mount(); + const getComponent = (props = {}) => mount(); beforeEach(() => { - mocked(getOrderedThemes).mockClear().mockReturnValue([ - { id: 'light', name: 'Light' }, - { id: 'dark', name: 'Dark' }, - ]); + mocked(getOrderedThemes) + .mockClear() + .mockReturnValue([ + { id: "light", name: "Light" }, + { id: "dark", name: "Dark" }, + ]); mocked(ThemeChoicePanel).calculateThemeState.mockClear().mockReturnValue({ - theme: 'light', useSystemTheme: false, + theme: "light", + useSystemTheme: false, }); mocked(SettingsStore).setValue.mockClear().mockResolvedValue(); mocked(dis).dispatch.mockClear(); @@ -70,66 +72,68 @@ describe('', () => { const getSelectedLabel = (component) => findById(component, "mx_QuickSettingsButton_themePickerDropdown_value").text(); - const openDropdown = component => act(async () => { - component.find('.mx_Dropdown_input').at(0).simulate('click'); - component.setProps({}); - }); + const openDropdown = (component) => + act(async () => { + component.find(".mx_Dropdown_input").at(0).simulate("click"); + component.setProps({}); + }); const getOption = (component, themeId) => findById(component, `mx_QuickSettingsButton_themePickerDropdown__${themeId}`).at(0); const selectOption = async (component, themeId: string) => { await openDropdown(component); await act(async () => { - getOption(component, themeId).simulate('click'); + getOption(component, themeId).simulate("click"); }); }; - it('renders dropdown correctly when light theme is selected', () => { + it("renders dropdown correctly when light theme is selected", () => { const component = getComponent(); - expect(getSelectedLabel(component)).toEqual('Light'); + expect(getSelectedLabel(component)).toEqual("Light"); }); - it('renders dropdown correctly when use system theme is truthy', () => { + it("renders dropdown correctly when use system theme is truthy", () => { mocked(ThemeChoicePanel).calculateThemeState.mockClear().mockReturnValue({ - theme: 'light', useSystemTheme: true, + theme: "light", + useSystemTheme: true, }); const component = getComponent(); - expect(getSelectedLabel(component)).toEqual('Match system'); + expect(getSelectedLabel(component)).toEqual("Match system"); }); - it('updates settings when match system is selected', async () => { + it("updates settings when match system is selected", async () => { const requestClose = jest.fn(); const component = getComponent({ requestClose }); - await selectOption(component, 'MATCH_SYSTEM_THEME_ID'); + await selectOption(component, "MATCH_SYSTEM_THEME_ID"); expect(SettingsStore.setValue).toHaveBeenCalledTimes(1); - expect(SettingsStore.setValue).toHaveBeenCalledWith('use_system_theme', null, SettingLevel.DEVICE, true); + expect(SettingsStore.setValue).toHaveBeenCalledWith("use_system_theme", null, SettingLevel.DEVICE, true); expect(dis.dispatch).not.toHaveBeenCalled(); expect(requestClose).toHaveBeenCalled(); }); - it('updates settings when a theme is selected', async () => { + it("updates settings when a theme is selected", async () => { // ie not match system const requestClose = jest.fn(); const component = getComponent({ requestClose }); - await selectOption(component, 'dark'); + await selectOption(component, "dark"); - expect(SettingsStore.setValue).toHaveBeenCalledWith('use_system_theme', null, SettingLevel.DEVICE, false); - expect(SettingsStore.setValue).toHaveBeenCalledWith('theme', null, SettingLevel.DEVICE, 'dark'); + expect(SettingsStore.setValue).toHaveBeenCalledWith("use_system_theme", null, SettingLevel.DEVICE, false); + expect(SettingsStore.setValue).toHaveBeenCalledWith("theme", null, SettingLevel.DEVICE, "dark"); - expect(dis.dispatch).toHaveBeenCalledWith({ action: Action.RecheckTheme, forceTheme: 'dark' }); + expect(dis.dispatch).toHaveBeenCalledWith({ action: Action.RecheckTheme, forceTheme: "dark" }); expect(requestClose).toHaveBeenCalled(); }); - it('rechecks theme when setting theme fails', async () => { - mocked(SettingsStore.setValue).mockRejectedValue('oops'); + it("rechecks theme when setting theme fails", async () => { + mocked(SettingsStore.setValue).mockRejectedValue("oops"); const requestClose = jest.fn(); const component = getComponent({ requestClose }); - await selectOption(component, 'MATCH_SYSTEM_THEME_ID'); + await selectOption(component, "MATCH_SYSTEM_THEME_ID"); expect(dis.dispatch).toHaveBeenCalledWith({ action: Action.RecheckTheme }); expect(requestClose).toHaveBeenCalled(); diff --git a/test/components/views/spaces/SpacePanel-test.tsx b/test/components/views/spaces/SpacePanel-test.tsx index 7e922b1399..56050ef95e 100644 --- a/test/components/views/spaces/SpacePanel-test.tsx +++ b/test/components/views/spaces/SpacePanel-test.tsx @@ -14,64 +14,64 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; import { render, screen, fireEvent } from "@testing-library/react"; -import { mocked } from 'jest-mock'; -import { MatrixClient } from 'matrix-js-sdk/src/matrix'; +import { mocked } from "jest-mock"; +import { MatrixClient } from "matrix-js-sdk/src/matrix"; -import SpacePanel from '../../../../src/components/views/spaces/SpacePanel'; -import { MatrixClientPeg } from '../../../../src/MatrixClientPeg'; -import { SpaceKey } from '../../../../src/stores/spaces'; -import { shouldShowComponent } from '../../../../src/customisations/helpers/UIComponents'; -import { UIComponent } from '../../../../src/settings/UIFeature'; +import SpacePanel from "../../../../src/components/views/spaces/SpacePanel"; +import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; +import { SpaceKey } from "../../../../src/stores/spaces"; +import { shouldShowComponent } from "../../../../src/customisations/helpers/UIComponents"; +import { UIComponent } from "../../../../src/settings/UIFeature"; -jest.mock('../../../../src/stores/spaces/SpaceStore', () => { +jest.mock("../../../../src/stores/spaces/SpaceStore", () => { // eslint-disable-next-line @typescript-eslint/no-var-requires const EventEmitter = require("events"); class MockSpaceStore extends EventEmitter { invitedSpaces = []; enabledMetaSpaces = []; spacePanelSpaces = []; - activeSpace: SpaceKey = '!space1'; + activeSpace: SpaceKey = "!space1"; } return { instance: new MockSpaceStore(), }; }); -jest.mock('../../../../src/customisations/helpers/UIComponents', () => ({ +jest.mock("../../../../src/customisations/helpers/UIComponents", () => ({ shouldShowComponent: jest.fn(), })); -describe('', () => { +describe("", () => { const mockClient = { - getUserId: jest.fn().mockReturnValue('@test:test'), + getUserId: jest.fn().mockReturnValue("@test:test"), isGuest: jest.fn(), getAccountData: jest.fn(), } as unknown as MatrixClient; beforeAll(() => { - jest.spyOn(MatrixClientPeg, 'get').mockReturnValue(mockClient); + jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient); }); beforeEach(() => { mocked(shouldShowComponent).mockClear().mockReturnValue(true); }); - describe('create new space button', () => { - it('renders create space button when UIComponent.CreateSpaces component should be shown', () => { + describe("create new space button", () => { + it("renders create space button when UIComponent.CreateSpaces component should be shown", () => { render(); screen.getByTestId("create-space-button"); }); - it('does not render create space button when UIComponent.CreateSpaces component should not be shown', () => { + it("does not render create space button when UIComponent.CreateSpaces component should not be shown", () => { mocked(shouldShowComponent).mockReturnValue(false); render(); expect(shouldShowComponent).toHaveBeenCalledWith(UIComponent.CreateSpaces); expect(screen.queryByTestId("create-space-button")).toBeFalsy(); }); - it('opens context menu on create space button click', () => { + it("opens context menu on create space button click", () => { render(); fireEvent.click(screen.getByTestId("create-space-button")); screen.getByTestId("create-space-button"); diff --git a/test/components/views/spaces/SpaceSettingsVisibilityTab-test.tsx b/test/components/views/spaces/SpaceSettingsVisibilityTab-test.tsx index efb6097f36..2cbe804cbb 100644 --- a/test/components/views/spaces/SpaceSettingsVisibilityTab-test.tsx +++ b/test/components/views/spaces/SpaceSettingsVisibilityTab-test.tsx @@ -15,44 +15,50 @@ limitations under the License. */ import React from "react"; -import { mocked } from 'jest-mock'; -import { - renderIntoDocument, - Simulate, -} from 'react-dom/test-utils'; +import { mocked } from "jest-mock"; +import { renderIntoDocument, Simulate } from "react-dom/test-utils"; import { act } from "react-dom/test-utils"; -import { EventType, MatrixClient, Room } from 'matrix-js-sdk/src/matrix'; -import { GuestAccess, HistoryVisibility, JoinRule } from 'matrix-js-sdk/src/@types/partials'; +import { EventType, MatrixClient, Room } from "matrix-js-sdk/src/matrix"; +import { GuestAccess, HistoryVisibility, JoinRule } from "matrix-js-sdk/src/@types/partials"; import _SpaceSettingsVisibilityTab from "../../../../src/components/views/spaces/SpaceSettingsVisibilityTab"; -import { createTestClient, mkEvent, wrapInMatrixClientContext } from '../../../test-utils'; -import { mkSpace, mockStateEventImplementation } from '../../../test-utils'; -import { MatrixClientPeg } from '../../../../src/MatrixClientPeg'; +import { createTestClient, mkEvent, wrapInMatrixClientContext } from "../../../test-utils"; +import { mkSpace, mockStateEventImplementation } from "../../../test-utils"; +import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; const SpaceSettingsVisibilityTab = wrapInMatrixClientContext(_SpaceSettingsVisibilityTab); jest.useFakeTimers(); -describe('', () => { +describe("", () => { const mockMatrixClient = createTestClient() as MatrixClient; - const makeJoinEvent = (rule: JoinRule = JoinRule.Invite) => mkEvent({ - type: EventType.RoomJoinRules, event: true, content: { - join_rule: rule, - }, - } as any); - const makeGuestAccessEvent = (rule: GuestAccess = GuestAccess.CanJoin) => mkEvent({ - type: EventType.RoomGuestAccess, event: true, content: { - guest_access: rule, - }, - } as any); - const makeHistoryEvent = (rule: HistoryVisibility = HistoryVisibility.Shared) => mkEvent({ - type: EventType.RoomHistoryVisibility, event: true, content: { - history_visibility: rule, - }, - } as any); + const makeJoinEvent = (rule: JoinRule = JoinRule.Invite) => + mkEvent({ + type: EventType.RoomJoinRules, + event: true, + content: { + join_rule: rule, + }, + } as any); + const makeGuestAccessEvent = (rule: GuestAccess = GuestAccess.CanJoin) => + mkEvent({ + type: EventType.RoomGuestAccess, + event: true, + content: { + guest_access: rule, + }, + } as any); + const makeHistoryEvent = (rule: HistoryVisibility = HistoryVisibility.Shared) => + mkEvent({ + type: EventType.RoomHistoryVisibility, + event: true, + content: { + history_visibility: rule, + }, + } as any); - const mockSpaceId = 'mock-space'; + const mockSpaceId = "mock-space"; // TODO case for canonical const makeMockSpace = ( @@ -61,11 +67,7 @@ describe('', () => { guestRule: GuestAccess = GuestAccess.CanJoin, historyRule: HistoryVisibility = HistoryVisibility.WorldReadable, ): Room => { - const events = [ - makeJoinEvent(joinRule), - makeGuestAccessEvent(guestRule), - makeHistoryEvent(historyRule), - ]; + const events = [makeJoinEvent(joinRule), makeGuestAccessEvent(guestRule), makeHistoryEvent(historyRule)]; const space = mkSpace(client, mockSpaceId); const getStateEvents = mockStateEventImplementation(events); mocked(space.currentState).getStateEvents.mockImplementation(getStateEvents); @@ -92,14 +94,14 @@ describe('', () => { const getByTestId = (container: Element, id: string) => container.querySelector(`[data-test-id=${id}]`); const toggleGuestAccessSection = async (component) => { - const toggleButton = getByTestId(component, 'toggle-guest-access-btn'); + const toggleButton = getByTestId(component, "toggle-guest-access-btn"); await act(async () => { Simulate.click(toggleButton); }); }; - const getGuestAccessToggle = component => component.querySelector('[aria-label="Enable guest access"'); - const getHistoryVisibilityToggle = component => component.querySelector('[aria-label="Preview Space"'); - const getErrorMessage = component => getByTestId(component, 'space-settings-error')?.textContent; + const getGuestAccessToggle = (component) => component.querySelector('[aria-label="Enable guest access"'); + const getHistoryVisibilityToggle = (component) => component.querySelector('[aria-label="Preview Space"'); + const getErrorMessage = (component) => getByTestId(component, "space-settings-error")?.textContent; beforeEach(() => { (mockMatrixClient.sendStateEvent as jest.Mock).mockClear().mockResolvedValue({}); @@ -110,29 +112,29 @@ describe('', () => { jest.runAllTimers(); }); - it('renders container', () => { + it("renders container", () => { const component = getComponent(); expect(component).toMatchSnapshot(); }); - describe('for a private space', () => { + describe("for a private space", () => { const joinRule = JoinRule.Invite; - it('does not render addresses section', () => { + it("does not render addresses section", () => { const space = makeMockSpace(mockMatrixClient, joinRule); const component = getComponent({ space }); - expect(getByTestId(component, 'published-address-fieldset')).toBeFalsy(); - expect(getByTestId(component, 'local-address-fieldset')).toBeFalsy(); + expect(getByTestId(component, "published-address-fieldset")).toBeFalsy(); + expect(getByTestId(component, "local-address-fieldset")).toBeFalsy(); }); }); - describe('for a public space', () => { + describe("for a public space", () => { const joinRule = JoinRule.Public; const guestRule = GuestAccess.CanJoin; const historyRule = HistoryVisibility.Joined; - describe('Access', () => { - it('renders guest access section toggle', async () => { + describe("Access", () => { + it("renders guest access section toggle", async () => { const space = makeMockSpace(mockMatrixClient, joinRule, guestRule); const component = getComponent({ space }); @@ -141,14 +143,14 @@ describe('', () => { expect(getGuestAccessToggle(component)).toMatchSnapshot(); }); - it('send guest access event on toggle', async () => { + it("send guest access event on toggle", async () => { const space = makeMockSpace(mockMatrixClient, joinRule, guestRule); const component = getComponent({ space }); await toggleGuestAccessSection(component); const guestAccessInput = getGuestAccessToggle(component); - expect(guestAccessInput.getAttribute('aria-checked')).toEqual("true"); + expect(guestAccessInput.getAttribute("aria-checked")).toEqual("true"); await act(async () => { Simulate.click(guestAccessInput); @@ -163,10 +165,10 @@ describe('', () => { ); // toggled off - expect(guestAccessInput.getAttribute('aria-checked')).toEqual("false"); + expect(guestAccessInput.getAttribute("aria-checked")).toEqual("false"); }); - it('renders error message when update fails', async () => { + it("renders error message when update fails", async () => { const space = makeMockSpace(mockMatrixClient, joinRule, guestRule); (mockMatrixClient.sendStateEvent as jest.Mock).mockRejectedValue({}); const component = getComponent({ space }); @@ -178,32 +180,32 @@ describe('', () => { expect(getErrorMessage(component)).toEqual("Failed to update the guest access of this space"); }); - it('disables guest access toggle when setting guest access is not allowed', async () => { + it("disables guest access toggle when setting guest access is not allowed", async () => { const space = makeMockSpace(mockMatrixClient, joinRule, guestRule); (space.currentState.maySendStateEvent as jest.Mock).mockReturnValue(false); const component = getComponent({ space }); await toggleGuestAccessSection(component); - expect(getGuestAccessToggle(component).getAttribute('aria-disabled')).toEqual("true"); + expect(getGuestAccessToggle(component).getAttribute("aria-disabled")).toEqual("true"); }); }); - describe('Preview', () => { - it('renders preview space toggle', () => { + describe("Preview", () => { + it("renders preview space toggle", () => { const space = makeMockSpace(mockMatrixClient, joinRule, guestRule, historyRule); const component = getComponent({ space }); // toggle off because space settings is != WorldReadable - expect(getHistoryVisibilityToggle(component).getAttribute('aria-checked')).toEqual("false"); + expect(getHistoryVisibilityToggle(component).getAttribute("aria-checked")).toEqual("false"); }); - it('updates history visibility on toggle', async () => { + it("updates history visibility on toggle", async () => { const space = makeMockSpace(mockMatrixClient, joinRule, guestRule, historyRule); const component = getComponent({ space }); // toggle off because space settings is != WorldReadable - expect(getHistoryVisibilityToggle(component).getAttribute('aria-checked')).toEqual("false"); + expect(getHistoryVisibilityToggle(component).getAttribute("aria-checked")).toEqual("false"); await act(async () => { Simulate.click(getHistoryVisibilityToggle(component)); @@ -216,10 +218,10 @@ describe('', () => { "", ); - expect(getHistoryVisibilityToggle(component).getAttribute('aria-checked')).toEqual("true"); + expect(getHistoryVisibilityToggle(component).getAttribute("aria-checked")).toEqual("true"); }); - it('renders error message when history update fails', async () => { + it("renders error message when history update fails", async () => { const space = makeMockSpace(mockMatrixClient, joinRule, guestRule, historyRule); (mockMatrixClient.sendStateEvent as jest.Mock).mockRejectedValue({}); const component = getComponent({ space }); @@ -231,20 +233,20 @@ describe('', () => { expect(getErrorMessage(component)).toEqual("Failed to update the history visibility of this space"); }); - it('disables room preview toggle when history visability changes are not allowed', () => { + it("disables room preview toggle when history visability changes are not allowed", () => { const space = makeMockSpace(mockMatrixClient, joinRule, guestRule, historyRule); (space.currentState.maySendStateEvent as jest.Mock).mockReturnValue(false); const component = getComponent({ space }); - expect(getHistoryVisibilityToggle(component).getAttribute('aria-disabled')).toEqual("true"); + expect(getHistoryVisibilityToggle(component).getAttribute("aria-disabled")).toEqual("true"); }); }); - it('renders addresses section', () => { + it("renders addresses section", () => { const space = makeMockSpace(mockMatrixClient, joinRule, guestRule); const component = getComponent({ space }); - expect(getByTestId(component, 'published-address-fieldset')).toBeTruthy(); - expect(getByTestId(component, 'local-address-fieldset')).toBeTruthy(); + expect(getByTestId(component, "published-address-fieldset")).toBeTruthy(); + expect(getByTestId(component, "local-address-fieldset")).toBeTruthy(); }); }); }); diff --git a/test/components/views/spaces/SpaceTreeLevel-test.tsx b/test/components/views/spaces/SpaceTreeLevel-test.tsx index e1de2c1cdd..633cc13c3f 100644 --- a/test/components/views/spaces/SpaceTreeLevel-test.tsx +++ b/test/components/views/spaces/SpaceTreeLevel-test.tsx @@ -48,11 +48,9 @@ describe("SpaceButton", () => { describe("real space", () => { it("activates the space on click", () => { - const { container } = render(); + const { container } = render( + , + ); expect(SpaceStore.instance.setActiveSpace).not.toHaveBeenCalled(); fireEvent.click(getByTestId(container, "create-space-button")); @@ -60,11 +58,9 @@ describe("SpaceButton", () => { }); it("navigates to the space home on click if already active", () => { - const { container } = render(); + const { container } = render( + , + ); expect(dispatchSpy).not.toHaveBeenCalled(); fireEvent.click(getByTestId(container, "create-space-button")); @@ -74,11 +70,14 @@ describe("SpaceButton", () => { describe("metaspace", () => { it("activates the metaspace on click", () => { - const { container } = render(); + const { container } = render( + , + ); expect(SpaceStore.instance.setActiveSpace).not.toHaveBeenCalled(); fireEvent.click(getByTestId(container, "create-space-button")); @@ -86,11 +85,14 @@ describe("SpaceButton", () => { }); it("does nothing on click if already active", () => { - const { container } = render(); + const { container } = render( + , + ); fireEvent.click(getByTestId(container, "create-space-button")); expect(dispatchSpy).not.toHaveBeenCalled(); diff --git a/test/components/views/typography/Caption-test.tsx b/test/components/views/typography/Caption-test.tsx index 3257cd9b7e..ca3258f725 100644 --- a/test/components/views/typography/Caption-test.tsx +++ b/test/components/views/typography/Caption-test.tsx @@ -14,26 +14,29 @@ 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 React from "react"; +import { render } from "@testing-library/react"; -import { Caption } from '../../../../src/components/views/typography/Caption'; +import { Caption } from "../../../../src/components/views/typography/Caption"; -describe('', () => { +describe("", () => { const defaultProps = { - 'children': 'test', - 'data-testid': 'test test id', + "children": "test", + "data-testid": "test test id", }; - const getComponent = (props = {}) => - (); + const getComponent = (props = {}) => ; - it('renders plain text children', () => { + it("renders plain text children", () => { const { container } = render(getComponent()); expect({ container }).toMatchSnapshot(); }); - it('renders react children', () => { - const children = <>Test test but bold; + it("renders react children", () => { + const children = ( + <> + Test test but bold + + ); const { container } = render(getComponent({ children })); expect({ container }).toMatchSnapshot(); }); diff --git a/test/components/views/typography/Heading-test.tsx b/test/components/views/typography/Heading-test.tsx index 3770402933..74dc12c782 100644 --- a/test/components/views/typography/Heading-test.tsx +++ b/test/components/views/typography/Heading-test.tsx @@ -14,35 +14,37 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import { renderIntoDocument } from 'react-dom/test-utils'; +import React from "react"; +import { renderIntoDocument } from "react-dom/test-utils"; import Heading from "../../../../src/components/views/typography/Heading"; -describe('', () => { +describe("", () => { const defaultProps = { - size: 'h1', + size: "h1", children:
test
, - ['data-test-id']: 'test', - className: 'test', + ["data-test-id"]: "test", + className: "test", } as any; const getComponent = (props = {}) => { const wrapper = renderIntoDocument( -
, +
+ +
, ) as HTMLDivElement; return wrapper.children[0]; }; - it('renders h1 with correct attributes', () => { - expect(getComponent({ size: 'h1' })).toMatchSnapshot(); + it("renders h1 with correct attributes", () => { + expect(getComponent({ size: "h1" })).toMatchSnapshot(); }); - it('renders h2 with correct attributes', () => { - expect(getComponent({ size: 'h2' })).toMatchSnapshot(); + it("renders h2 with correct attributes", () => { + expect(getComponent({ size: "h2" })).toMatchSnapshot(); }); - it('renders h3 with correct attributes', () => { - expect(getComponent({ size: 'h3' })).toMatchSnapshot(); + it("renders h3 with correct attributes", () => { + expect(getComponent({ size: "h3" })).toMatchSnapshot(); }); - it('renders h4 with correct attributes', () => { - expect(getComponent({ size: 'h4' })).toMatchSnapshot(); + it("renders h4 with correct attributes", () => { + expect(getComponent({ size: "h4" })).toMatchSnapshot(); }); }); diff --git a/test/components/views/voip/CallView-test.tsx b/test/components/views/voip/CallView-test.tsx index 40d19c2fec..97937954ab 100644 --- a/test/components/views/voip/CallView-test.tsx +++ b/test/components/views/voip/CallView-test.tsx @@ -60,9 +60,9 @@ describe("CallLobby", () => { pendingEventOrdering: PendingEventOrdering.Detached, }); alice = mkRoomMember(room.roomId, "@alice:example.org"); - jest.spyOn(room, "getMember").mockImplementation(userId => userId === alice.userId ? alice : null); + jest.spyOn(room, "getMember").mockImplementation((userId) => (userId === alice.userId ? alice : null)); - client.getRoom.mockImplementation(roomId => roomId === room.roomId ? room : null); + client.getRoom.mockImplementation((roomId) => (roomId === room.roomId ? room : null)); client.getRooms.mockReturnValue([room]); client.reEmitter.reEmit(room, [RoomStateEvent.Events]); @@ -139,7 +139,9 @@ describe("CallLobby", () => { expect(screen.queryByLabelText(/joined/)).toBe(null); expectAvatars([]); - act(() => { call.participants = new Map([[alice, new Set(["a"])]]); }); + act(() => { + call.participants = new Map([[alice, new Set(["a"])]]); + }); screen.getByText("1 person joined"); expectAvatars([alice.userId]); @@ -153,7 +155,9 @@ describe("CallLobby", () => { screen.getByText("4 people joined"); expectAvatars([alice.userId, bob.userId, bob.userId, carol.userId]); - act(() => { call.participants = new Map(); }); + act(() => { + call.participants = new Map(); + }); expect(screen.queryByLabelText(/joined/)).toBe(null); expectAvatars([]); }); @@ -170,9 +174,12 @@ describe("CallLobby", () => { const carol = mkRoomMember(room.roomId, "@carol:example.org"); SdkConfig.put({ - "element_call": { participant_limit: 2, url: "", use_exclusively: false, brand: "Element Call" }, + element_call: { participant_limit: 2, url: "", use_exclusively: false, brand: "Element Call" }, }); - call.participants = new Map([[bob, new Set("b")], [carol, new Set("c")]]); + call.participants = new Map([ + [bob, new Set("b")], + [carol, new Set("c")], + ]); await renderView(); const connectSpy = jest.spyOn(call, "connect"); @@ -249,9 +256,7 @@ describe("CallLobby", () => { }); it("show with dropdown when multiple devices are available", async () => { - mocked(navigator.mediaDevices.enumerateDevices).mockResolvedValue([ - fakeAudioInput1, fakeAudioInput2, - ]); + mocked(navigator.mediaDevices.enumerateDevices).mockResolvedValue([fakeAudioInput1, fakeAudioInput2]); await renderView(); screen.getByRole("button", { name: /microphone/ }); @@ -261,9 +266,7 @@ describe("CallLobby", () => { }); it("sets video device when selected", async () => { - mocked(navigator.mediaDevices.enumerateDevices).mockResolvedValue([ - fakeVideoInput1, fakeVideoInput2, - ]); + mocked(navigator.mediaDevices.enumerateDevices).mockResolvedValue([fakeVideoInput1, fakeVideoInput2]); await renderView(); screen.getByRole("button", { name: /camera/ }); @@ -274,9 +277,7 @@ describe("CallLobby", () => { }); it("sets audio device when selected", async () => { - mocked(navigator.mediaDevices.enumerateDevices).mockResolvedValue([ - fakeAudioInput1, fakeAudioInput2, - ]); + mocked(navigator.mediaDevices.enumerateDevices).mockResolvedValue([fakeAudioInput1, fakeAudioInput2]); await renderView(); screen.getByRole("button", { name: /microphone/ }); diff --git a/test/components/views/voip/PipView-test.tsx b/test/components/views/voip/PipView-test.tsx index 6a9105a413..531cad314d 100644 --- a/test/components/views/voip/PipView-test.tsx +++ b/test/components/views/voip/PipView-test.tsx @@ -91,18 +91,16 @@ describe("PipView", () => { client.getRooms.mockReturnValue([room, room2]); client.reEmitter.reEmit(room, [RoomStateEvent.Events]); - room.currentState.setStateEvents([ - mkRoomCreateEvent(alice.userId, room.roomId), - ]); - jest.spyOn(room, "getMember").mockImplementation(userId => userId === alice.userId ? alice : null); + room.currentState.setStateEvents([mkRoomCreateEvent(alice.userId, room.roomId)]); + jest.spyOn(room, "getMember").mockImplementation((userId) => (userId === alice.userId ? alice : null)); - room2.currentState.setStateEvents([ - mkRoomCreateEvent(alice.userId, room2.roomId), - ]); + room2.currentState.setStateEvents([mkRoomCreateEvent(alice.userId, room2.roomId)]); - await Promise.all([CallStore.instance, WidgetMessagingStore.instance].map( - store => setupAsyncStoreWithClient(store, client), - )); + await Promise.all( + [CallStore.instance, WidgetMessagingStore.instance].map((store) => + setupAsyncStoreWithClient(store, client), + ), + ); sdkContext = new TestSdkContext(); voiceBroadcastRecordingsStore = new VoiceBroadcastRecordingsStore(); @@ -122,18 +120,19 @@ describe("PipView", () => { }); const renderPip = () => { - const PipView = wrapInMatrixClientContext( - wrapInSdkContext(UnwrappedPipView, sdkContext), - ); + const PipView = wrapInMatrixClientContext(wrapInSdkContext(UnwrappedPipView, sdkContext)); render(); }; const viewRoom = (roomId: string) => - defaultDispatcher.dispatch({ - action: Action.ViewRoom, - room_id: roomId, - metricsTrigger: undefined, - }, true); + defaultDispatcher.dispatch( + { + action: Action.ViewRoom, + room_id: roomId, + metricsTrigger: undefined, + }, + true, + ); const withCall = async (fn: () => Promise): Promise => { MockedCall.create(room, "1"); @@ -197,12 +196,15 @@ describe("PipView", () => { const startVoiceBroadcastPlayback = (room: Room): MatrixEvent => { const infoEvent = makeVoiceBroadcastInfoStateEvent(); room.currentState.setStateEvents([infoEvent]); - defaultDispatcher.dispatch({ - action: "MatrixActions.RoomState.events", - event: infoEvent, - state: room.currentState, - lastStateEvent: null, - }, true); + defaultDispatcher.dispatch( + { + action: "MatrixActions.RoomState.events", + event: infoEvent, + state: room.currentState, + lastStateEvent: null, + }, + true, + ); return infoEvent; }; @@ -224,11 +226,13 @@ describe("PipView", () => { const dispatcherSpy = jest.fn(); const dispatcherRef = defaultDispatcher.register(dispatcherSpy); fireEvent.click(screen.getByRole("button", { name: "Fill screen" })); - await waitFor(() => expect(dispatcherSpy).toHaveBeenCalledWith({ - action: Action.ViewRoom, - room_id: room.roomId, - view_call: true, - })); + await waitFor(() => + expect(dispatcherSpy).toHaveBeenCalledWith({ + action: Action.ViewRoom, + room_id: room.roomId, + view_call: true, + }), + ); defaultDispatcher.unregister(dispatcherRef); }); }); @@ -323,12 +327,15 @@ describe("PipView", () => { startEvent, ); room.currentState.setStateEvents([stopEvent]); - defaultDispatcher.dispatch({ - action: "MatrixActions.RoomState.events", - event: stopEvent, - state: room.currentState, - lastStateEvent: stopEvent, - }, true); + defaultDispatcher.dispatch( + { + action: "MatrixActions.RoomState.events", + event: stopEvent, + state: room.currentState, + lastStateEvent: stopEvent, + }, + true, + ); }); }); diff --git a/test/createRoom-test.ts b/test/createRoom-test.ts index 735f75c873..6ff690b41e 100644 --- a/test/createRoom-test.ts +++ b/test/createRoom-test.ts @@ -24,7 +24,7 @@ import { MatrixClientPeg } from "../src/MatrixClientPeg"; import WidgetStore from "../src/stores/WidgetStore"; import WidgetUtils from "../src/utils/WidgetUtils"; import { JitsiCall, ElementCall } from "../src/models/Call"; -import createRoom, { canEncryptToAllUsers } from '../src/createRoom'; +import createRoom, { canEncryptToAllUsers } from "../src/createRoom"; import SettingsStore from "../src/settings/SettingsStore"; describe("createRoom", () => { @@ -46,17 +46,19 @@ describe("createRoom", () => { const userId = client.getUserId()!; const roomId = await createRoom({ roomType: RoomType.ElementVideo }); - const [[{ - power_level_content_override: { - users: { - [userId]: userPower, + const [ + [ + { + power_level_content_override: { + users: { [userId]: userPower }, + events: { + "im.vector.modular.widgets": widgetPower, + [JitsiCall.MEMBER_EVENT_TYPE]: callMemberPower, + }, + }, }, - events: { - "im.vector.modular.widgets": widgetPower, - [JitsiCall.MEMBER_EVENT_TYPE]: callMemberPower, - }, - }, - }]] = client.createRoom.mock.calls as any; // no good type + ], + ] = client.createRoom.mock.calls as any; // no good type // We should have had enough power to be able to set up the widget expect(userPower).toBeGreaterThanOrEqual(widgetPower); @@ -76,17 +78,19 @@ describe("createRoom", () => { const createCallSpy = jest.spyOn(ElementCall, "create"); const roomId = await createRoom({ roomType: RoomType.UnstableCall }); - const [[{ - power_level_content_override: { - users: { - [userId]: userPower, + const [ + [ + { + power_level_content_override: { + users: { [userId]: userPower }, + events: { + [ElementCall.CALL_EVENT_TYPE.name]: callPower, + [ElementCall.MEMBER_EVENT_TYPE.name]: callMemberPower, + }, + }, }, - events: { - [ElementCall.CALL_EVENT_TYPE.name]: callPower, - [ElementCall.MEMBER_EVENT_TYPE.name]: callMemberPower, - }, - }, - }]] = client.createRoom.mock.calls; + ], + ] = client.createRoom.mock.calls; // We should have had enough power to be able to set up the call expect(userPower).toBeGreaterThanOrEqual(callPower); @@ -118,14 +122,18 @@ describe("createRoom", () => { await createRoom({}); - const [[{ - power_level_content_override: { - events: { - [ElementCall.CALL_EVENT_TYPE.name]: callPower, - [ElementCall.MEMBER_EVENT_TYPE.name]: callMemberPower, + const [ + [ + { + power_level_content_override: { + events: { + [ElementCall.CALL_EVENT_TYPE.name]: callPower, + [ElementCall.MEMBER_EVENT_TYPE.name]: callMemberPower, + }, + }, }, - }, - }]] = client.createRoom.mock.calls; + ], + ] = client.createRoom.mock.calls; expect(callPower).toBe(100); expect(callMemberPower).toBe(100); @@ -135,22 +143,26 @@ describe("createRoom", () => { client.uploadContent.mockResolvedValue({ content_uri: "mxc://foobar" }); const avatar = new File([], "avatar.png"); await createRoom({ avatar }); - expect(client.createRoom).toHaveBeenCalledWith(expect.objectContaining({ - initial_state: expect.arrayContaining([{ - content: { - url: "mxc://foobar", - }, - type: "m.room.avatar", - }]), - })); + expect(client.createRoom).toHaveBeenCalledWith( + expect.objectContaining({ + initial_state: expect.arrayContaining([ + { + content: { + url: "mxc://foobar", + }, + type: "m.room.avatar", + }, + ]), + }), + ); }); }); describe("canEncryptToAllUsers", () => { const trueUser = { "@goodUser:localhost": { - "DEV1": {} as unknown as IDevice, - "DEV2": {} as unknown as IDevice, + DEV1: {} as unknown as IDevice, + DEV2: {} as unknown as IDevice, }, }; const falseUser = { diff --git a/test/editor/caret-test.ts b/test/editor/caret-test.ts index 2e7ba0ba05..caa8b09295 100644 --- a/test/editor/caret-test.ts +++ b/test/editor/caret-test.ts @@ -18,183 +18,139 @@ import { getLineAndNodePosition } from "../../src/editor/caret"; import EditorModel from "../../src/editor/model"; import { createPartCreator } from "./mock"; -describe('editor/caret: DOM position for caret', function() { - describe('basic text handling', function() { - it('at end of single line', function() { +describe("editor/caret: DOM position for caret", function () { + describe("basic text handling", function () { + it("at end of single line", function () { const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("hello"), - ], pc); - const { offset, lineIndex, nodeIndex } = - getLineAndNodePosition(model, { index: 0, offset: 5 }); + const model = new EditorModel([pc.plain("hello")], pc); + const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 0, offset: 5 }); expect(lineIndex).toBe(0); expect(nodeIndex).toBe(0); expect(offset).toBe(5); }); - it('at start of single line', function() { + it("at start of single line", function () { const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("hello"), - ], pc); - const { offset, lineIndex, nodeIndex } = - getLineAndNodePosition(model, { index: 0, offset: 0 }); + const model = new EditorModel([pc.plain("hello")], pc); + const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 0, offset: 0 }); expect(lineIndex).toBe(0); expect(nodeIndex).toBe(0); expect(offset).toBe(0); }); - it('at middle of single line', function() { + it("at middle of single line", function () { const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("hello"), - ], pc); - const { offset, lineIndex, nodeIndex } = - getLineAndNodePosition(model, { index: 0, offset: 2 }); + const model = new EditorModel([pc.plain("hello")], pc); + const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 0, offset: 2 }); expect(lineIndex).toBe(0); expect(nodeIndex).toBe(0); expect(offset).toBe(2); }); }); - describe('handling line breaks', function() { - it('at end of last line', function() { + describe("handling line breaks", function () { + it("at end of last line", function () { const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("hello"), - pc.newline(), - pc.plain("world"), - ], pc); - const { offset, lineIndex, nodeIndex } = - getLineAndNodePosition(model, { index: 2, offset: 5 }); + const model = new EditorModel([pc.plain("hello"), pc.newline(), pc.plain("world")], pc); + const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 2, offset: 5 }); expect(lineIndex).toBe(1); expect(nodeIndex).toBe(0); expect(offset).toBe(5); }); - it('at start of last line', function() { + it("at start of last line", function () { const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("hello"), - pc.newline(), - pc.plain("world"), - ], pc); - const { offset, lineIndex, nodeIndex } = - getLineAndNodePosition(model, { index: 2, offset: 0 }); + const model = new EditorModel([pc.plain("hello"), pc.newline(), pc.plain("world")], pc); + const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 2, offset: 0 }); expect(lineIndex).toBe(1); expect(nodeIndex).toBe(0); expect(offset).toBe(0); }); - it('in empty line', function() { + it("in empty line", function () { const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("hello"), - pc.newline(), - pc.newline(), - pc.plain("world"), - ], pc); - const { offset, lineIndex, nodeIndex } = - getLineAndNodePosition(model, { index: 1, offset: 1 }); + const model = new EditorModel([pc.plain("hello"), pc.newline(), pc.newline(), pc.plain("world")], pc); + const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 1, offset: 1 }); expect(lineIndex).toBe(1); expect(nodeIndex).toBe(-1); expect(offset).toBe(0); }); - it('after empty line', function() { + it("after empty line", function () { const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("hello"), - pc.newline(), - pc.newline(), - pc.plain("world"), - ], pc); - const { offset, lineIndex, nodeIndex } = - getLineAndNodePosition(model, { index: 3, offset: 0 }); + const model = new EditorModel([pc.plain("hello"), pc.newline(), pc.newline(), pc.plain("world")], pc); + const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 3, offset: 0 }); expect(lineIndex).toBe(2); expect(nodeIndex).toBe(0); expect(offset).toBe(0); }); }); - describe('handling non-editable parts and caret nodes', function() { - it('at start of non-editable part (with plain text around)', function() { + describe("handling non-editable parts and caret nodes", function () { + it("at start of non-editable part (with plain text around)", function () { const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("hello"), - pc.userPill("Alice", "@alice:hs.tld"), - pc.plain("!"), - ], pc); - const { offset, lineIndex, nodeIndex } = - getLineAndNodePosition(model, { index: 1, offset: 0 }); + const model = new EditorModel( + [pc.plain("hello"), pc.userPill("Alice", "@alice:hs.tld"), pc.plain("!")], + pc, + ); + const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 1, offset: 0 }); expect(lineIndex).toBe(0); expect(nodeIndex).toBe(0); expect(offset).toBe(5); }); - it('in middle of non-editable part (with plain text around)', function() { + it("in middle of non-editable part (with plain text around)", function () { const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("hello"), - pc.userPill("Alice", "@alice:hs.tld"), - pc.plain("!"), - ], pc); - const { offset, lineIndex, nodeIndex } = - getLineAndNodePosition(model, { index: 1, offset: 2 }); + const model = new EditorModel( + [pc.plain("hello"), pc.userPill("Alice", "@alice:hs.tld"), pc.plain("!")], + pc, + ); + const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 1, offset: 2 }); expect(lineIndex).toBe(0); expect(nodeIndex).toBe(2); expect(offset).toBe(0); }); - it('at start of non-editable part (without plain text around)', function() { + it("at start of non-editable part (without plain text around)", function () { const pc = createPartCreator(); - const model = new EditorModel([ - pc.userPill("Alice", "@alice:hs.tld"), - ], pc); - const { offset, lineIndex, nodeIndex } = - getLineAndNodePosition(model, { index: 0, offset: 0 }); + const model = new EditorModel([pc.userPill("Alice", "@alice:hs.tld")], pc); + const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 0, offset: 0 }); expect(lineIndex).toBe(0); //presumed nodes on line are (caret, pill, caret) expect(nodeIndex).toBe(0); expect(offset).toBe(0); }); - it('in middle of non-editable part (without plain text around)', function() { + it("in middle of non-editable part (without plain text around)", function () { const pc = createPartCreator(); - const model = new EditorModel([ - pc.userPill("Alice", "@alice:hs.tld"), - ], pc); - const { offset, lineIndex, nodeIndex } = - getLineAndNodePosition(model, { index: 0, offset: 1 }); + const model = new EditorModel([pc.userPill("Alice", "@alice:hs.tld")], pc); + const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 0, offset: 1 }); expect(lineIndex).toBe(0); //presumed nodes on line are (caret, pill, caret) expect(nodeIndex).toBe(2); expect(offset).toBe(0); }); - it('in middle of a first non-editable part, with another one following', function() { + it("in middle of a first non-editable part, with another one following", function () { const pc = createPartCreator(); - const model = new EditorModel([ - pc.userPill("Alice", "@alice:hs.tld"), - pc.userPill("Bob", "@bob:hs.tld"), - ], pc); - const { offset, lineIndex, nodeIndex } = - getLineAndNodePosition(model, { index: 0, offset: 1 }); + const model = new EditorModel( + [pc.userPill("Alice", "@alice:hs.tld"), pc.userPill("Bob", "@bob:hs.tld")], + pc, + ); + const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 0, offset: 1 }); expect(lineIndex).toBe(0); //presumed nodes on line are (caret, pill, caret, pill, caret) expect(nodeIndex).toBe(2); expect(offset).toBe(0); }); - it('in start of a second non-editable part, with another one before it', function() { + it("in start of a second non-editable part, with another one before it", function () { const pc = createPartCreator(); - const model = new EditorModel([ - pc.userPill("Alice", "@alice:hs.tld"), - pc.userPill("Bob", "@bob:hs.tld"), - ], pc); - const { offset, lineIndex, nodeIndex } = - getLineAndNodePosition(model, { index: 1, offset: 0 }); + const model = new EditorModel( + [pc.userPill("Alice", "@alice:hs.tld"), pc.userPill("Bob", "@bob:hs.tld")], + pc, + ); + const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 1, offset: 0 }); expect(lineIndex).toBe(0); //presumed nodes on line are (caret, pill, caret, pill, caret) expect(nodeIndex).toBe(2); expect(offset).toBe(0); }); - it('in middle of a second non-editable part, with another one before it', function() { + it("in middle of a second non-editable part, with another one before it", function () { const pc = createPartCreator(); - const model = new EditorModel([ - pc.userPill("Alice", "@alice:hs.tld"), - pc.userPill("Bob", "@bob:hs.tld"), - ], pc); - const { offset, lineIndex, nodeIndex } = - getLineAndNodePosition(model, { index: 1, offset: 1 }); + const model = new EditorModel( + [pc.userPill("Alice", "@alice:hs.tld"), pc.userPill("Bob", "@bob:hs.tld")], + pc, + ); + const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 1, offset: 1 }); expect(lineIndex).toBe(0); //presumed nodes on line are (caret, pill, caret, pill, caret) expect(nodeIndex).toBe(4); diff --git a/test/editor/deserialize-test.ts b/test/editor/deserialize-test.ts index a6713b3139..8c32900d84 100644 --- a/test/editor/deserialize-test.ts +++ b/test/editor/deserialize-test.ts @@ -13,7 +13,7 @@ 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 { MatrixEvent } from 'matrix-js-sdk/src/matrix'; +import { MatrixEvent } from "matrix-js-sdk/src/matrix"; import { parseEvent } from "../../src/editor/deserialize"; import { createPartCreator } from "./mock"; @@ -73,46 +73,46 @@ function normalize(parts) { // plain parts are returned is an implementation detail mergeAdjacentParts(parts); // convert to data objects for easier asserting - return parts.map(p => p.serialize()); + return parts.map((p) => p.serialize()); } -describe('editor/deserialize', function() { - describe('text messages', function() { - it('test with newlines', function() { +describe("editor/deserialize", function () { + describe("text messages", function () { + it("test with newlines", function () { const parts = normalize(parseEvent(textMessage("hello\nworld"), createPartCreator())); expect(parts[0]).toStrictEqual({ type: "plain", text: "hello" }); expect(parts[1]).toStrictEqual({ type: "newline", text: "\n" }); expect(parts[2]).toStrictEqual({ type: "plain", text: "world" }); expect(parts.length).toBe(3); }); - it('@room pill', function() { + it("@room pill", function () { const parts = normalize(parseEvent(textMessage("text message for @room"), createPartCreator())); expect(parts.length).toBe(2); expect(parts[0]).toStrictEqual({ type: "plain", text: "text message for " }); expect(parts[1]).toStrictEqual({ type: "at-room-pill", text: "@room" }); }); - it('emote', function() { + it("emote", function () { const text = "says DON'T SHOUT!"; const parts = normalize(parseEvent(textMessage(text, "m.emote"), createPartCreator())); expect(parts.length).toBe(1); expect(parts[0]).toStrictEqual({ type: "plain", text: "/me says DON'T SHOUT!" }); }); }); - describe('html messages', function() { - it('inline styling', function() { + describe("html messages", function () { + it("inline styling", function () { const html = "bold and emphasized text"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(1); expect(parts[0]).toStrictEqual({ type: "plain", text: "**bold** and _emphasized_ text" }); }); - it('hyperlink', function() { + it("hyperlink", function () { const html = 'click this!'; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(1); expect(parts[0]).toStrictEqual({ type: "plain", text: "click [this](http://example.com/)!" }); }); - it('multiple lines with paragraphs', function() { - const html = '

hello

world

'; + it("multiple lines with paragraphs", function () { + const html = "

hello

world

"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(4); expect(parts[0]).toStrictEqual({ type: "plain", text: "hello" }); @@ -120,16 +120,16 @@ describe('editor/deserialize', function() { expect(parts[2]).toStrictEqual({ type: "newline", text: "\n" }); expect(parts[3]).toStrictEqual({ type: "plain", text: "world" }); }); - it('multiple lines with line breaks', function() { - const html = 'hello
world'; + it("multiple lines with line breaks", function () { + const html = "hello
world"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(3); expect(parts[0]).toStrictEqual({ type: "plain", text: "hello" }); expect(parts[1]).toStrictEqual({ type: "newline", text: "\n" }); expect(parts[2]).toStrictEqual({ type: "plain", text: "world" }); }); - it('multiple lines mixing paragraphs and line breaks', function() { - const html = '

hello
warm

world

'; + it("multiple lines mixing paragraphs and line breaks", function () { + const html = "

hello
warm

world

"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(6); expect(parts[0]).toStrictEqual({ type: "plain", text: "hello" }); @@ -139,8 +139,8 @@ describe('editor/deserialize', function() { expect(parts[4]).toStrictEqual({ type: "newline", text: "\n" }); expect(parts[5]).toStrictEqual({ type: "plain", text: "world" }); }); - it('quote', function() { - const html = '

wise
words

indeed

'; + it("quote", function () { + const html = "

wise
words

indeed

"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(6); expect(parts[0]).toStrictEqual({ type: "plain", text: "> _wise_" }); @@ -150,60 +150,60 @@ describe('editor/deserialize', function() { expect(parts[4]).toStrictEqual({ type: "newline", text: "\n" }); expect(parts[5]).toStrictEqual({ type: "plain", text: "indeed" }); }); - it('user pill', function() { - const html = "Hi Alice!"; + it("user pill", function () { + const html = 'Hi Alice!'; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(3); expect(parts[0]).toStrictEqual({ type: "plain", text: "Hi " }); expect(parts[1]).toStrictEqual({ type: "user-pill", text: "Alice", resourceId: "@alice:hs.tld" }); expect(parts[2]).toStrictEqual({ type: "plain", text: "!" }); }); - it('user pill with displayname containing backslash', function() { - const html = "Hi Alice\\!"; + it("user pill with displayname containing backslash", function () { + const html = 'Hi Alice\\!'; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(3); expect(parts[0]).toStrictEqual({ type: "plain", text: "Hi " }); expect(parts[1]).toStrictEqual({ type: "user-pill", text: "Alice\\", resourceId: "@alice:hs.tld" }); expect(parts[2]).toStrictEqual({ type: "plain", text: "!" }); }); - it('user pill with displayname containing opening square bracket', function() { - const html = "Hi Alice[[!"; + it("user pill with displayname containing opening square bracket", function () { + const html = 'Hi Alice[[!'; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(3); expect(parts[0]).toStrictEqual({ type: "plain", text: "Hi " }); expect(parts[1]).toStrictEqual({ type: "user-pill", text: "Alice[[", resourceId: "@alice:hs.tld" }); expect(parts[2]).toStrictEqual({ type: "plain", text: "!" }); }); - it('user pill with displayname containing closing square bracket', function() { - const html = "Hi Alice]!"; + it("user pill with displayname containing closing square bracket", function () { + const html = 'Hi Alice]!'; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(3); expect(parts[0]).toStrictEqual({ type: "plain", text: "Hi " }); expect(parts[1]).toStrictEqual({ type: "user-pill", text: "Alice]", resourceId: "@alice:hs.tld" }); expect(parts[2]).toStrictEqual({ type: "plain", text: "!" }); }); - it('room pill', function() { - const html = "Try #room:hs.tld?"; + it("room pill", function () { + const html = 'Try #room:hs.tld?'; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(3); expect(parts[0]).toStrictEqual({ type: "plain", text: "Try " }); expect(parts[1]).toStrictEqual({ type: "room-pill", text: "#room:hs.tld", resourceId: "#room:hs.tld" }); expect(parts[2]).toStrictEqual({ type: "plain", text: "?" }); }); - it('@room pill', function() { + it("@room pill", function () { const html = "formatted message for @room"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(2); expect(parts[0]).toStrictEqual({ type: "plain", text: "_formatted_ message for " }); expect(parts[1]).toStrictEqual({ type: "at-room-pill", text: "@room" }); }); - it('inline code', function() { + it("inline code", function () { const html = "there is no place like 127.0.0.1!"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(1); expect(parts[0]).toStrictEqual({ type: "plain", text: "there is no place like `127.0.0.1`!" }); }); - it('code block with no trailing text', function() { + it("code block with no trailing text", function () { const html = "
0xDEADBEEF\n
\n"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(5); @@ -214,7 +214,7 @@ describe('editor/deserialize', function() { expect(parts[4]).toStrictEqual({ type: "plain", text: "```" }); }); // failing likely because of https://github.com/vector-im/element-web/issues/10316 - xit('code block with no trailing text and no newlines', function() { + xit("code block with no trailing text and no newlines", function () { const html = "
0xDEADBEEF
"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(5); @@ -224,7 +224,7 @@ describe('editor/deserialize', function() { expect(parts[3]).toStrictEqual({ type: "newline", text: "\n" }); expect(parts[4]).toStrictEqual({ type: "plain", text: "```" }); }); - it('unordered lists', function() { + it("unordered lists", function () { const html = "
  • Oak
  • Spruce
  • Birch
"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(5); @@ -234,7 +234,7 @@ describe('editor/deserialize', function() { expect(parts[3]).toStrictEqual({ type: "newline", text: "\n" }); expect(parts[4]).toStrictEqual({ type: "plain", text: "- Birch" }); }); - it('ordered lists', function() { + it("ordered lists", function () { const html = "
  1. Start
  2. Continue
  3. Finish
"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(5); @@ -244,7 +244,7 @@ describe('editor/deserialize', function() { expect(parts[3]).toStrictEqual({ type: "newline", text: "\n" }); expect(parts[4]).toStrictEqual({ type: "plain", text: "3. Finish" }); }); - it('nested unordered lists', () => { + it("nested unordered lists", () => { const html = "
  • Oak
    • Spruce
      • Birch
"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(5); @@ -254,7 +254,7 @@ describe('editor/deserialize', function() { expect(parts[3]).toStrictEqual({ type: "newline", text: "\n" }); expect(parts[4]).toStrictEqual({ type: "plain", text: `${FOUR_SPACES.repeat(2)}- Birch` }); }); - it('nested ordered lists', () => { + it("nested ordered lists", () => { const html = "
  1. Oak
    1. Spruce
      1. Birch
"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(5); @@ -264,7 +264,7 @@ describe('editor/deserialize', function() { expect(parts[3]).toStrictEqual({ type: "newline", text: "\n" }); expect(parts[4]).toStrictEqual({ type: "plain", text: `${FOUR_SPACES.repeat(2)}1. Birch` }); }); - it('nested lists', () => { + it("nested lists", () => { const html = "
  1. Oak\n
    1. Spruce\n
      1. Birch
"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(5); @@ -274,73 +274,73 @@ describe('editor/deserialize', function() { expect(parts[3]).toStrictEqual({ type: "newline", text: "\n" }); expect(parts[4]).toStrictEqual({ type: "plain", text: `${FOUR_SPACES.repeat(2)}1. Birch` }); }); - it('mx-reply is stripped', function() { + it("mx-reply is stripped", function () { const html = "foobar"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts.length).toBe(1); expect(parts[0]).toStrictEqual({ type: "plain", text: "bar" }); }); - it('emote', function() { + it("emote", function () { const html = "says DON'T SHOUT!"; const parts = normalize(parseEvent(htmlMessage(html, "m.emote"), createPartCreator())); expect(parts.length).toBe(1); expect(parts[0]).toStrictEqual({ type: "plain", text: "/me says _DON'T SHOUT_!" }); }); - it('preserves nested quotes', () => { + it("preserves nested quotes", () => { const html = "
foo
bar
"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts).toMatchSnapshot(); }); - it('surrounds lists with newlines', () => { + it("surrounds lists with newlines", () => { const html = "foo
  • bar
baz"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts).toMatchSnapshot(); }); - it('preserves nested formatting', () => { + it("preserves nested formatting", () => { const html = "abcde"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts).toMatchSnapshot(); }); - it('escapes backticks in code blocks', () => { - const html = "

this → ` is a backtick

" + - "
and here are 3 of them:\n```
"; + it("escapes backticks in code blocks", () => { + const html = + "

this → ` is a backtick

" + "
and here are 3 of them:\n```
"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts).toMatchSnapshot(); }); - it('escapes backticks outside of code blocks', () => { + it("escapes backticks outside of code blocks", () => { const html = "some `backticks`"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts).toMatchSnapshot(); }); - it('escapes backslashes', () => { + it("escapes backslashes", () => { const html = "C:\\My Documents"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts).toMatchSnapshot(); }); - it('escapes asterisks', () => { + it("escapes asterisks", () => { const html = "*hello*"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts).toMatchSnapshot(); }); - it('escapes underscores', () => { + it("escapes underscores", () => { const html = "__emphasis__"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts).toMatchSnapshot(); }); - it('escapes square brackets', () => { + it("escapes square brackets", () => { const html = "[not an actual link](https://example.org)"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts).toMatchSnapshot(); }); - it('escapes angle brackets', () => { + it("escapes angle brackets", () => { const html = "> \\no formatting here\\"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator())); expect(parts).toMatchSnapshot(); }); }); - describe('plaintext messages', function() { - it('turns html tags back into markdown', function() { - const html = "bold and emphasized text this!"; + describe("plaintext messages", function () { + it("turns html tags back into markdown", function () { + const html = 'bold and emphasized text this!'; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator(), { shouldEscape: false })); expect(parts.length).toBe(1); expect(parts[0]).toStrictEqual({ @@ -348,7 +348,7 @@ describe('editor/deserialize', function() { text: "**bold** and _emphasized_ text [this](http://example.com/)!", }); }); - it('keeps backticks unescaped', () => { + it("keeps backticks unescaped", () => { const html = "this → ` is a backtick and here are 3 of them:\n```"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator(), { shouldEscape: false })); expect(parts.length).toBe(1); @@ -357,7 +357,7 @@ describe('editor/deserialize', function() { text: "this → ` is a backtick and here are 3 of them:\n```", }); }); - it('keeps backticks outside of code blocks', () => { + it("keeps backticks outside of code blocks", () => { const html = "some `backticks`"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator(), { shouldEscape: false })); expect(parts.length).toBe(1); @@ -366,7 +366,7 @@ describe('editor/deserialize', function() { text: "some `backticks`", }); }); - it('keeps backslashes', () => { + it("keeps backslashes", () => { const html = "C:\\My Documents"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator(), { shouldEscape: false })); expect(parts.length).toBe(1); @@ -375,7 +375,7 @@ describe('editor/deserialize', function() { text: "C:\\My Documents", }); }); - it('keeps asterisks', () => { + it("keeps asterisks", () => { const html = "*hello*"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator(), { shouldEscape: false })); expect(parts.length).toBe(1); @@ -384,7 +384,7 @@ describe('editor/deserialize', function() { text: "*hello*", }); }); - it('keeps underscores', () => { + it("keeps underscores", () => { const html = "__emphasis__"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator(), { shouldEscape: false })); expect(parts.length).toBe(1); @@ -393,7 +393,7 @@ describe('editor/deserialize', function() { text: "__emphasis__", }); }); - it('keeps square brackets', () => { + it("keeps square brackets", () => { const html = "[not an actual link](https://example.org)"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator(), { shouldEscape: false })); expect(parts.length).toBe(1); @@ -402,7 +402,7 @@ describe('editor/deserialize', function() { text: "[not an actual link](https://example.org)", }); }); - it('escapes angle brackets', () => { + it("escapes angle brackets", () => { const html = "> <del>no formatting here</del>"; const parts = normalize(parseEvent(htmlMessage(html), createPartCreator(), { shouldEscape: false })); expect(parts.length).toBe(1); diff --git a/test/editor/diff-test.ts b/test/editor/diff-test.ts index e525731340..36c357cd9f 100644 --- a/test/editor/diff-test.ts +++ b/test/editor/diff-test.ts @@ -16,126 +16,126 @@ limitations under the License. import { diffDeletion, diffAtCaret } from "../../src/editor/diff"; -describe('editor/diff', function() { - describe('diffDeletion', function() { - describe('with a single character removed', function() { - it('at start of string', function() { +describe("editor/diff", function () { + describe("diffDeletion", function () { + describe("with a single character removed", function () { + it("at start of string", function () { const diff = diffDeletion("hello", "ello"); expect(diff.at).toBe(0); expect(diff.removed).toBe("h"); }); - it('in middle of string', function() { + it("in middle of string", function () { const diff = diffDeletion("hello", "hllo"); expect(diff.at).toBe(1); expect(diff.removed).toBe("e"); }); - it('in middle of string with duplicate character', function() { + it("in middle of string with duplicate character", function () { const diff = diffDeletion("hello", "helo"); expect(diff.at).toBe(3); expect(diff.removed).toBe("l"); }); - it('at end of string', function() { + it("at end of string", function () { const diff = diffDeletion("hello", "hell"); expect(diff.at).toBe(4); expect(diff.removed).toBe("o"); }); }); - describe('with a multiple removed', function() { - it('at start of string', function() { + describe("with a multiple removed", function () { + it("at start of string", function () { const diff = diffDeletion("hello", "llo"); expect(diff.at).toBe(0); expect(diff.removed).toBe("he"); }); - it('removing whole string', function() { + it("removing whole string", function () { const diff = diffDeletion("hello", ""); expect(diff.at).toBe(0); expect(diff.removed).toBe("hello"); }); - it('in middle of string', function() { + it("in middle of string", function () { const diff = diffDeletion("hello", "hlo"); expect(diff.at).toBe(1); expect(diff.removed).toBe("el"); }); - it('in middle of string with duplicate character', function() { + it("in middle of string with duplicate character", function () { const diff = diffDeletion("hello", "heo"); expect(diff.at).toBe(2); expect(diff.removed).toBe("ll"); }); - it('at end of string', function() { + it("at end of string", function () { const diff = diffDeletion("hello", "hel"); expect(diff.at).toBe(3); expect(diff.removed).toBe("lo"); }); }); }); - describe('diffAtCaret', function() { - it('insert at start', function() { + describe("diffAtCaret", function () { + it("insert at start", function () { const diff = diffAtCaret("world", "hello world", 6); expect(diff.at).toBe(0); expect(diff.added).toBe("hello "); expect(diff.removed).toBeFalsy(); }); - it('insert at end', function() { + it("insert at end", function () { const diff = diffAtCaret("hello", "hello world", 11); expect(diff.at).toBe(5); expect(diff.added).toBe(" world"); expect(diff.removed).toBeFalsy(); }); - it('insert in middle', function() { + it("insert in middle", function () { const diff = diffAtCaret("hello world", "hello cruel world", 12); expect(diff.at).toBe(6); expect(diff.added).toBe("cruel "); expect(diff.removed).toBeFalsy(); }); - it('replace at start', function() { + it("replace at start", function () { const diff = diffAtCaret("morning, world!", "afternoon, world!", 9); expect(diff.at).toBe(0); expect(diff.removed).toBe("morning"); expect(diff.added).toBe("afternoon"); }); - it('replace at end', function() { + it("replace at end", function () { const diff = diffAtCaret("morning, world!", "morning, mars?", 14); expect(diff.at).toBe(9); expect(diff.removed).toBe("world!"); expect(diff.added).toBe("mars?"); }); - it('replace in middle', function() { + it("replace in middle", function () { const diff = diffAtCaret("morning, blue planet", "morning, red planet", 12); expect(diff.at).toBe(9); expect(diff.removed).toBe("blue"); expect(diff.added).toBe("red"); }); - it('remove at start of string', function() { + it("remove at start of string", function () { const diff = diffAtCaret("hello", "ello", 0); expect(diff.at).toBe(0); expect(diff.removed).toBe("h"); expect(diff.added).toBeFalsy(); }); - it('removing whole string', function() { + it("removing whole string", function () { const diff = diffAtCaret("hello", "", 0); expect(diff.at).toBe(0); expect(diff.removed).toBe("hello"); expect(diff.added).toBeFalsy(); }); - it('remove in middle of string', function() { + it("remove in middle of string", function () { const diff = diffAtCaret("hello", "hllo", 1); expect(diff.at).toBe(1); expect(diff.removed).toBe("e"); expect(diff.added).toBeFalsy(); }); - it('forwards remove in middle of string', function() { + it("forwards remove in middle of string", function () { const diff = diffAtCaret("hello", "hell", 4); expect(diff.at).toBe(4); expect(diff.removed).toBe("o"); expect(diff.added).toBeFalsy(); }); - it('forwards remove in middle of string with duplicate character', function() { + it("forwards remove in middle of string with duplicate character", function () { const diff = diffAtCaret("hello", "helo", 3); expect(diff.at).toBe(3); expect(diff.removed).toBe("l"); expect(diff.added).toBeFalsy(); }); - it('remove at end of string', function() { + it("remove at end of string", function () { const diff = diffAtCaret("hello", "hell", 4); expect(diff.at).toBe(4); expect(diff.removed).toBe("o"); diff --git a/test/editor/history-test.ts b/test/editor/history-test.ts index 94e22ed290..25c2dca895 100644 --- a/test/editor/history-test.ts +++ b/test/editor/history-test.ts @@ -18,30 +18,30 @@ import HistoryManager, { MAX_STEP_LENGTH } from "../../src/editor/history"; import EditorModel from "../../src/editor/model"; import DocumentPosition from "../../src/editor/position"; -describe('editor/history', function() { - it('push, then undo', function() { +describe("editor/history", function () { + it("push, then undo", function () { const history = new HistoryManager(); const parts = ["hello"]; const model = { serializeParts: () => parts.slice() } as unknown as EditorModel; const caret1 = new DocumentPosition(0, 0); - const result1 = history.tryPush(model, caret1, 'insertText', {}); + const result1 = history.tryPush(model, caret1, "insertText", {}); expect(result1).toEqual(true); parts[0] = "hello world"; - history.tryPush(model, new DocumentPosition(0, 0), 'insertText', {}); + history.tryPush(model, new DocumentPosition(0, 0), "insertText", {}); expect(history.canUndo()).toEqual(true); const undoState = history.undo(model); expect(undoState.caret).toBe(caret1); expect(undoState.parts).toEqual(["hello"]); expect(history.canUndo()).toEqual(false); }); - it('push, undo, then redo', function() { + it("push, undo, then redo", function () { const history = new HistoryManager(); const parts = ["hello"]; const model = { serializeParts: () => parts.slice() } as unknown as EditorModel; - history.tryPush(model, new DocumentPosition(0, 0), 'insertText', {}); + history.tryPush(model, new DocumentPosition(0, 0), "insertText", {}); parts[0] = "hello world"; const caret2 = new DocumentPosition(0, 0); - history.tryPush(model, caret2, 'insertText', {}); + history.tryPush(model, caret2, "insertText", {}); history.undo(model); expect(history.canRedo()).toEqual(true); const redoState = history.redo(); @@ -50,7 +50,7 @@ describe('editor/history', function() { expect(history.canRedo()).toEqual(false); expect(history.canUndo()).toEqual(true); }); - it('push, undo, push, ensure you can`t redo', function() { + it("push, undo, push, ensure you can`t redo", function () { const history = new HistoryManager(); const parts = ["hello"]; const model = { serializeParts: () => parts.slice() } as unknown as EditorModel; @@ -62,7 +62,7 @@ describe('editor/history', function() { history.tryPush(model, new DocumentPosition(0, 0), "insertText", {}); expect(history.canRedo()).toEqual(false); }); - it('not every keystroke stores a history step', function() { + it("not every keystroke stores a history step", function () { const history = new HistoryManager(); const parts = ["hello"]; const model = { serializeParts: () => parts.slice() } as unknown as EditorModel; @@ -80,7 +80,7 @@ describe('editor/history', function() { expect(history.canUndo()).toEqual(false); expect(keystrokeCount).toEqual(MAX_STEP_LENGTH + 1); // +1 before we type before checking }); - it('history step is added at word boundary', function() { + it("history step is added at word boundary", function () { const history = new HistoryManager(); const model = { serializeParts: () => parts.slice() } as unknown as EditorModel; const parts = ["h"]; @@ -108,7 +108,7 @@ describe('editor/history', function() { expect(undoResult.caret).toEqual(spaceCaret); expect(undoResult.parts).toEqual(["hi "]); }); - it('keystroke that didn\'t add a step can undo', function() { + it("keystroke that didn't add a step can undo", function () { const history = new HistoryManager(); const parts = ["hello"]; const model = { serializeParts: () => parts.slice() } as unknown as EditorModel; @@ -122,7 +122,7 @@ describe('editor/history', function() { expect(undoState.caret).toEqual(firstCaret); expect(undoState.parts).toEqual(["hello"]); }); - it('undo after keystroke that didn\'t add a step is able to redo', function() { + it("undo after keystroke that didn't add a step is able to redo", function () { const history = new HistoryManager(); const parts = ["hello"]; const model = { serializeParts: () => parts.slice() } as unknown as EditorModel; @@ -137,12 +137,12 @@ describe('editor/history', function() { expect(redoState.parts).toEqual(["helloo"]); }); - it('overwriting text always stores a step', function() { + it("overwriting text always stores a step", function () { const history = new HistoryManager(); const parts = ["hello"]; const model = { serializeParts: () => parts.slice() } as unknown as EditorModel; const firstCaret = new DocumentPosition(0, 0); - history.tryPush(model, firstCaret, 'insertText', {}); + history.tryPush(model, firstCaret, "insertText", {}); const diff = { at: 1, added: "a", removed: "e" }; const secondCaret = new DocumentPosition(1, 1); const result = history.tryPush(model, secondCaret, "insertText", diff); diff --git a/test/editor/mock.ts b/test/editor/mock.ts index bddddbf7cb..b8e42ef705 100644 --- a/test/editor/mock.ts +++ b/test/editor/mock.ts @@ -38,7 +38,7 @@ class MockAutoComplete { } tryComplete(close = true) { - const matches = this._completions.filter(o => { + const matches = this._completions.filter((o) => { return o.resourceId.startsWith(this._part.text); }); if (matches.length === 1 && this._part.text.length > 1) { @@ -62,7 +62,9 @@ class MockAutoComplete { // MockClient & MockRoom are only used for avatars in room and user pills, // which is not tested class MockRoom { - getMember() { return null; } + getMember() { + return null; + } } export function createPartCreator(completions = []) { diff --git a/test/editor/model-test.ts b/test/editor/model-test.ts index 6b3bd8fb2c..3b4515f067 100644 --- a/test/editor/model-test.ts +++ b/test/editor/model-test.ts @@ -18,9 +18,9 @@ import EditorModel from "../../src/editor/model"; import { createPartCreator, createRenderer } from "./mock"; import DocumentOffset from "../../src/editor/offset"; -describe('editor/model', function() { - describe('plain text manipulation', function() { - it('insert text into empty document', function() { +describe("editor/model", function () { + describe("plain text manipulation", function () { + it("insert text into empty document", function () { const renderer = createRenderer(); const model = new EditorModel([], createPartCreator(), renderer); model.update("hello", "insertText", new DocumentOffset(5, true)); @@ -31,7 +31,7 @@ describe('editor/model', function() { expect(model.parts[0].type).toBe("plain"); expect(model.parts[0].text).toBe("hello"); }); - it('append text to existing document', function() { + it("append text to existing document", function () { const renderer = createRenderer(); const pc = createPartCreator(); const model = new EditorModel([pc.plain("hello")], pc, renderer); @@ -43,7 +43,7 @@ describe('editor/model', function() { expect(model.parts[0].type).toBe("plain"); expect(model.parts[0].text).toBe("hello world"); }); - it('prepend text to existing document', function() { + it("prepend text to existing document", function () { const renderer = createRenderer(); const pc = createPartCreator(); const model = new EditorModel([pc.plain("world")], pc, renderer); @@ -56,8 +56,8 @@ describe('editor/model', function() { expect(model.parts[0].text).toBe("hello world"); }); }); - describe('handling line breaks', function() { - it('insert new line into existing document', function() { + describe("handling line breaks", function () { + it("insert new line into existing document", function () { const renderer = createRenderer(); const pc = createPartCreator(); const model = new EditorModel([pc.plain("hello")], pc, renderer); @@ -71,7 +71,7 @@ describe('editor/model', function() { expect(model.parts[1].type).toBe("newline"); expect(model.parts[1].text).toBe("\n"); }); - it('insert multiple new lines into existing document', function() { + it("insert multiple new lines into existing document", function () { const renderer = createRenderer(); const pc = createPartCreator(); const model = new EditorModel([pc.plain("hello")], pc, renderer); @@ -91,15 +91,14 @@ describe('editor/model', function() { expect(model.parts[4].type).toBe("plain"); expect(model.parts[4].text).toBe("world!"); }); - it('type in empty line', function() { + it("type in empty line", function () { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("hello"), - pc.newline(), - pc.newline(), - pc.plain("world"), - ], pc, renderer); + const model = new EditorModel( + [pc.plain("hello"), pc.newline(), pc.newline(), pc.plain("world")], + pc, + renderer, + ); model.update("hello\nwarm\nworld", "insertText", new DocumentOffset(10, true)); expect(renderer.count).toBe(1); expect(renderer.caret.index).toBe(2); @@ -117,14 +116,11 @@ describe('editor/model', function() { expect(model.parts[4].text).toBe("world"); }); }); - describe('non-editable part manipulation', function() { - it('typing at start of non-editable part prepends', function() { + describe("non-editable part manipulation", function () { + it("typing at start of non-editable part prepends", function () { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("try "), - pc.roomPill("#someroom"), - ], pc, renderer); + const model = new EditorModel([pc.plain("try "), pc.roomPill("#someroom")], pc, renderer); model.update("try foo#someroom", "insertText", new DocumentOffset(7, false)); expect(renderer.caret.index).toBe(0); expect(renderer.caret.offset).toBe(7); @@ -134,14 +130,10 @@ describe('editor/model', function() { expect(model.parts[1].type).toBe("room-pill"); expect(model.parts[1].text).toBe("#someroom"); }); - it('typing in middle of non-editable part appends', function() { + it("typing in middle of non-editable part appends", function () { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("try "), - pc.roomPill("#someroom"), - pc.plain("?"), - ], pc, renderer); + const model = new EditorModel([pc.plain("try "), pc.roomPill("#someroom"), pc.plain("?")], pc, renderer); model.update("try #some perhapsroom?", "insertText", new DocumentOffset(17, false)); expect(renderer.caret.index).toBe(2); expect(renderer.caret.offset).toBe(8); @@ -153,7 +145,7 @@ describe('editor/model', function() { expect(model.parts[2].type).toBe("plain"); expect(model.parts[2].text).toBe(" perhaps?"); }); - it('remove non-editable part with backspace', function() { + it("remove non-editable part with backspace", function () { const renderer = createRenderer(); const pc = createPartCreator(); const model = new EditorModel([pc.roomPill("#someroom")], pc, renderer); @@ -163,7 +155,7 @@ describe('editor/model', function() { expect(renderer.caret.offset).toBe(0); expect(model.parts.length).toBe(0); }); - it('remove non-editable part with delete', function() { + it("remove non-editable part with delete", function () { const renderer = createRenderer(); const pc = createPartCreator(); const model = new EditorModel([pc.roomPill("#someroom")], pc, renderer); @@ -174,8 +166,8 @@ describe('editor/model', function() { expect(model.parts.length).toBe(0); }); }); - describe('auto-complete', function() { - it('insert user pill', function() { + describe("auto-complete", function () { + it("insert user pill", function () { const renderer = createRenderer(); const pc = createPartCreator([{ resourceId: "@alice", label: "Alice" }]); const model = new EditorModel([pc.plain("hello ")], pc, renderer); @@ -205,7 +197,7 @@ describe('editor/model', function() { expect(model.parts[1].text).toBe("Alice"); }); - it('insert room pill', function() { + it("insert room pill", function () { const renderer = createRenderer(); const pc = createPartCreator([{ resourceId: "#riot-dev" }]); const model = new EditorModel([pc.plain("hello ")], pc, renderer); @@ -235,7 +227,7 @@ describe('editor/model', function() { expect(model.parts[1].text).toBe("#riot-dev"); }); - it('type after inserting pill', function() { + it("type after inserting pill", function () { const renderer = createRenderer(); const pc = createPartCreator([{ resourceId: "#riot-dev" }]); const model = new EditorModel([pc.plain("hello ")], pc, renderer); @@ -258,7 +250,7 @@ describe('editor/model', function() { expect(model.parts[2].text).toBe("!!"); }); - it('pasting text does not trigger auto-complete', function() { + it("pasting text does not trigger auto-complete", function () { const renderer = createRenderer(); const pc = createPartCreator([{ resourceId: "#define-room" }]); const model = new EditorModel([pc.plain("try ")], pc, renderer); @@ -273,7 +265,7 @@ describe('editor/model', function() { expect(model.parts[0].text).toBe("try #define"); }); - it('dropping text does not trigger auto-complete', function() { + it("dropping text does not trigger auto-complete", function () { const renderer = createRenderer(); const pc = createPartCreator([{ resourceId: "#define-room" }]); const model = new EditorModel([pc.plain("try ")], pc, renderer); @@ -288,7 +280,7 @@ describe('editor/model', function() { expect(model.parts[0].text).toBe("try #define"); }); - it('insert room pill without splitting at the colon', () => { + it("insert room pill without splitting at the colon", () => { const renderer = createRenderer(); const pc = createPartCreator([{ resourceId: "#room:server" }]); const model = new EditorModel([], pc, renderer); @@ -308,7 +300,7 @@ describe('editor/model', function() { expect(model.parts[0].text).toBe("#room:s"); }); - it('allow typing e-mail addresses without splitting at the @', () => { + it("allow typing e-mail addresses without splitting at the @", () => { const renderer = createRenderer(); const pc = createPartCreator([{ resourceId: "@alice", label: "Alice" }]); const model = new EditorModel([], pc, renderer); diff --git a/test/editor/operations-test.ts b/test/editor/operations-test.ts index 6af732e5bd..abcb8832a4 100644 --- a/test/editor/operations-test.ts +++ b/test/editor/operations-test.ts @@ -24,54 +24,48 @@ import { toggleInlineFormat, } from "../../src/editor/operations"; import { Formatting } from "../../src/components/views/rooms/MessageComposerFormatBar"; -import { longestBacktickSequence } from '../../src/editor/deserialize'; +import { longestBacktickSequence } from "../../src/editor/deserialize"; -const SERIALIZED_NEWLINE = { "text": "\n", "type": "newline" }; +const SERIALIZED_NEWLINE = { text: "\n", type: "newline" }; describe("editor/operations: formatting operations", () => { const renderer = createRenderer(); const pc = createPartCreator(); describe("formatRange", () => { - it.each([ - [Formatting.Bold, "hello **world**!"], - ])("should correctly wrap format %s", (formatting: Formatting, expected: string) => { - const model = new EditorModel([ - pc.plain("hello world!"), - ], pc, renderer); + it.each([[Formatting.Bold, "hello **world**!"]])( + "should correctly wrap format %s", + (formatting: Formatting, expected: string) => { + const model = new EditorModel([pc.plain("hello world!")], pc, renderer); - const range = model.startRange(model.positionForOffset(6, false), - model.positionForOffset(11, false)); // around "world" + const range = model.startRange(model.positionForOffset(6, false), model.positionForOffset(11, false)); // around "world" - expect(range.parts[0].text).toBe("world"); - expect(model.serializeParts()).toEqual([{ "text": "hello world!", "type": "plain" }]); - formatRange(range, formatting); - expect(model.serializeParts()).toEqual([{ "text": expected, "type": "plain" }]); - }); + expect(range.parts[0].text).toBe("world"); + expect(model.serializeParts()).toEqual([{ text: "hello world!", type: "plain" }]); + formatRange(range, formatting); + expect(model.serializeParts()).toEqual([{ text: expected, type: "plain" }]); + }, + ); it("should apply to word range is within if length 0", () => { - const model = new EditorModel([ - pc.plain("hello world!"), - ], pc, renderer); + const model = new EditorModel([pc.plain("hello world!")], pc, renderer); const range = model.startRange(model.positionForOffset(6, false)); - expect(model.serializeParts()).toEqual([{ "text": "hello world!", "type": "plain" }]); + expect(model.serializeParts()).toEqual([{ text: "hello world!", type: "plain" }]); formatRange(range, Formatting.Bold); - expect(model.serializeParts()).toEqual([{ "text": "hello **world!**", "type": "plain" }]); + expect(model.serializeParts()).toEqual([{ text: "hello **world!**", type: "plain" }]); }); it("should do nothing for a range with length 0 at initialisation", () => { - const model = new EditorModel([ - pc.plain("hello world!"), - ], pc, renderer); + const model = new EditorModel([pc.plain("hello world!")], pc, renderer); const range = model.startRange(model.positionForOffset(6, false)); range.setWasEmpty(false); - expect(model.serializeParts()).toEqual([{ "text": "hello world!", "type": "plain" }]); + expect(model.serializeParts()).toEqual([{ text: "hello world!", type: "plain" }]); formatRange(range, Formatting.Bold); - expect(model.serializeParts()).toEqual([{ "text": "hello world!", "type": "plain" }]); + expect(model.serializeParts()).toEqual([{ text: "hello world!", type: "plain" }]); }); }); @@ -83,12 +77,12 @@ describe("editor/operations: formatting operations", () => { ["[testing]()", "testing|", ""], ["[testing](foobar)", "testing|", ""], ])("converts %s -> %s", (input: string, expectation: string, text: string) => { - const model = new EditorModel([ - pc.plain(`foo ${input} bar`), - ], pc, renderer); + const model = new EditorModel([pc.plain(`foo ${input} bar`)], pc, renderer); - const range = model.startRange(model.positionForOffset(4, false), - model.positionForOffset(4 + input.length, false)); // around input + const range = model.startRange( + model.positionForOffset(4, false), + model.positionForOffset(4 + input.length, false), + ); // around input expect(range.parts[0].text).toBe(input); formatRangeAsLink(range, text); @@ -99,192 +93,173 @@ describe("editor/operations: formatting operations", () => { describe("toggleInlineFormat", () => { it("works for words", () => { - const model = new EditorModel([ - pc.plain("hello world!"), - ], pc, renderer); + const model = new EditorModel([pc.plain("hello world!")], pc, renderer); - const range = model.startRange(model.positionForOffset(6, false), - model.positionForOffset(11, false)); // around "world" + const range = model.startRange(model.positionForOffset(6, false), model.positionForOffset(11, false)); // around "world" expect(range.parts[0].text).toBe("world"); - expect(model.serializeParts()).toEqual([{ "text": "hello world!", "type": "plain" }]); + expect(model.serializeParts()).toEqual([{ text: "hello world!", type: "plain" }]); formatRange(range, Formatting.Italics); - expect(model.serializeParts()).toEqual([{ "text": "hello _world_!", "type": "plain" }]); + expect(model.serializeParts()).toEqual([{ text: "hello _world_!", type: "plain" }]); }); - describe('escape backticks', () => { - it('works for escaping backticks in between texts', () => { + describe("escape backticks", () => { + it("works for escaping backticks in between texts", () => { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("hello ` world!"), - ], pc, renderer); + const model = new EditorModel([pc.plain("hello ` world!")], pc, renderer); - const range = model.startRange(model.positionForOffset(0, false), - model.positionForOffset(13, false)); // hello ` world + const range = model.startRange(model.positionForOffset(0, false), model.positionForOffset(13, false)); // hello ` world expect(range.parts[0].text.trim().includes("`")).toBeTruthy(); expect(longestBacktickSequence(range.parts[0].text.trim())).toBe(1); - expect(model.serializeParts()).toEqual([{ "text": "hello ` world!", "type": "plain" }]); + expect(model.serializeParts()).toEqual([{ text: "hello ` world!", type: "plain" }]); formatRangeAsCode(range); - expect(model.serializeParts()).toEqual([{ "text": "``hello ` world``!", "type": "plain" }]); + expect(model.serializeParts()).toEqual([{ text: "``hello ` world``!", type: "plain" }]); }); - it('escapes longer backticks in between text', () => { + it("escapes longer backticks in between text", () => { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("hello```world"), - ], pc, renderer); + const model = new EditorModel([pc.plain("hello```world")], pc, renderer); - const range = model.startRange(model.positionForOffset(0, false), - model.getPositionAtEnd()); // hello```world + const range = model.startRange(model.positionForOffset(0, false), model.getPositionAtEnd()); // hello```world expect(range.parts[0].text.includes("`")).toBeTruthy(); expect(longestBacktickSequence(range.parts[0].text)).toBe(3); - expect(model.serializeParts()).toEqual([{ "text": "hello```world", "type": "plain" }]); + expect(model.serializeParts()).toEqual([{ text: "hello```world", type: "plain" }]); formatRangeAsCode(range); - expect(model.serializeParts()).toEqual([{ "text": "````hello```world````", "type": "plain" }]); + expect(model.serializeParts()).toEqual([{ text: "````hello```world````", type: "plain" }]); }); - it('escapes non-consecutive with varying length backticks in between text', () => { + it("escapes non-consecutive with varying length backticks in between text", () => { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("hell```o`w`o``rld"), - ], pc, renderer); + const model = new EditorModel([pc.plain("hell```o`w`o``rld")], pc, renderer); - const range = model.startRange(model.positionForOffset(0, false), - model.getPositionAtEnd()); // hell```o`w`o``rld + const range = model.startRange(model.positionForOffset(0, false), model.getPositionAtEnd()); // hell```o`w`o``rld expect(range.parts[0].text.includes("`")).toBeTruthy(); expect(longestBacktickSequence(range.parts[0].text)).toBe(3); - expect(model.serializeParts()).toEqual([{ "text": "hell```o`w`o``rld", "type": "plain" }]); + expect(model.serializeParts()).toEqual([{ text: "hell```o`w`o``rld", type: "plain" }]); formatRangeAsCode(range); - expect(model.serializeParts()).toEqual([{ "text": "````hell```o`w`o``rld````", "type": "plain" }]); + expect(model.serializeParts()).toEqual([{ text: "````hell```o`w`o``rld````", type: "plain" }]); }); - it('untoggles correctly if its already formatted', () => { + it("untoggles correctly if its already formatted", () => { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("```hello``world```"), - ], pc, renderer); + const model = new EditorModel([pc.plain("```hello``world```")], pc, renderer); - const range = model.startRange(model.positionForOffset(0, false), - model.getPositionAtEnd()); // hello``world + const range = model.startRange(model.positionForOffset(0, false), model.getPositionAtEnd()); // hello``world expect(range.parts[0].text.includes("`")).toBeTruthy(); expect(longestBacktickSequence(range.parts[0].text)).toBe(3); - expect(model.serializeParts()).toEqual([{ "text": "```hello``world```", "type": "plain" }]); + expect(model.serializeParts()).toEqual([{ text: "```hello``world```", type: "plain" }]); formatRangeAsCode(range); - expect(model.serializeParts()).toEqual([{ "text": "hello``world", "type": "plain" }]); + expect(model.serializeParts()).toEqual([{ text: "hello``world", type: "plain" }]); }); - it('untoggles correctly it contains varying length of backticks between text', () => { + it("untoggles correctly it contains varying length of backticks between text", () => { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("````hell```o`w`o``rld````"), - ], pc, renderer); + const model = new EditorModel([pc.plain("````hell```o`w`o``rld````")], pc, renderer); - const range = model.startRange(model.positionForOffset(0, false), - model.getPositionAtEnd()); // hell```o`w`o``rld + const range = model.startRange(model.positionForOffset(0, false), model.getPositionAtEnd()); // hell```o`w`o``rld expect(range.parts[0].text.includes("`")).toBeTruthy(); expect(longestBacktickSequence(range.parts[0].text)).toBe(4); - expect(model.serializeParts()).toEqual([{ "text": "````hell```o`w`o``rld````", "type": "plain" }]); + expect(model.serializeParts()).toEqual([{ text: "````hell```o`w`o``rld````", type: "plain" }]); formatRangeAsCode(range); - expect(model.serializeParts()).toEqual([{ "text": "hell```o`w`o``rld", "type": "plain" }]); + expect(model.serializeParts()).toEqual([{ text: "hell```o`w`o``rld", type: "plain" }]); }); }); - it('works for parts of words', () => { + it("works for parts of words", () => { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("hello world!"), - ], pc, renderer); + const model = new EditorModel([pc.plain("hello world!")], pc, renderer); - const range = model.startRange(model.positionForOffset(7, false), - model.positionForOffset(10, false)); // around "orl" + const range = model.startRange(model.positionForOffset(7, false), model.positionForOffset(10, false)); // around "orl" expect(range.parts[0].text).toBe("orl"); - expect(model.serializeParts()).toEqual([{ "text": "hello world!", "type": "plain" }]); + expect(model.serializeParts()).toEqual([{ text: "hello world!", type: "plain" }]); toggleInlineFormat(range, "*"); - expect(model.serializeParts()).toEqual([{ "text": "hello w*orl*d!", "type": "plain" }]); + expect(model.serializeParts()).toEqual([{ text: "hello w*orl*d!", type: "plain" }]); }); - it('works for around pills', () => { + it("works for around pills", () => { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("hello there "), - pc.atRoomPill("@room"), - pc.plain(", how are you doing?"), - ], pc, renderer); + const model = new EditorModel( + [pc.plain("hello there "), pc.atRoomPill("@room"), pc.plain(", how are you doing?")], + pc, + renderer, + ); - const range = model.startRange(model.positionForOffset(6, false), - model.positionForOffset(30, false)); // around "there @room, how are you" + const range = model.startRange(model.positionForOffset(6, false), model.positionForOffset(30, false)); // around "there @room, how are you" - expect(range.parts.map(p => p.text).join("")).toBe("there @room, how are you"); + expect(range.parts.map((p) => p.text).join("")).toBe("there @room, how are you"); expect(model.serializeParts()).toEqual([ - { "text": "hello there ", "type": "plain" }, - { "text": "@room", "type": "at-room-pill" }, - { "text": ", how are you doing?", "type": "plain" }, + { text: "hello there ", type: "plain" }, + { text: "@room", type: "at-room-pill" }, + { text: ", how are you doing?", type: "plain" }, ]); formatRange(range, Formatting.Italics); expect(model.serializeParts()).toEqual([ - { "text": "hello _there ", "type": "plain" }, - { "text": "@room", "type": "at-room-pill" }, - { "text": ", how are you_ doing?", "type": "plain" }, + { text: "hello _there ", type: "plain" }, + { text: "@room", type: "at-room-pill" }, + { text: ", how are you_ doing?", type: "plain" }, ]); }); - it('works for a paragraph', () => { + it("works for a paragraph", () => { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("hello world,"), - pc.newline(), - pc.plain("how are you doing?"), - ], pc, renderer); + const model = new EditorModel( + [pc.plain("hello world,"), pc.newline(), pc.plain("how are you doing?")], + pc, + renderer, + ); - const range = model.startRange(model.positionForOffset(6, false), - model.positionForOffset(16, false)); // around "world,\nhow" + const range = model.startRange(model.positionForOffset(6, false), model.positionForOffset(16, false)); // around "world,\nhow" - expect(range.parts.map(p => p.text).join("")).toBe("world,\nhow"); + expect(range.parts.map((p) => p.text).join("")).toBe("world,\nhow"); expect(model.serializeParts()).toEqual([ - { "text": "hello world,", "type": "plain" }, + { text: "hello world,", type: "plain" }, SERIALIZED_NEWLINE, - { "text": "how are you doing?", "type": "plain" }, + { text: "how are you doing?", type: "plain" }, ]); formatRange(range, Formatting.Bold); expect(model.serializeParts()).toEqual([ - { "text": "hello **world,", "type": "plain" }, + { text: "hello **world,", type: "plain" }, SERIALIZED_NEWLINE, - { "text": "how** are you doing?", "type": "plain" }, + { text: "how** are you doing?", type: "plain" }, ]); }); - it('works for a paragraph with spurious breaks around it in selected range', () => { + it("works for a paragraph with spurious breaks around it in selected range", () => { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.newline(), - pc.newline(), - pc.plain("hello world,"), - pc.newline(), - pc.plain("how are you doing?"), - pc.newline(), - pc.newline(), - ], pc, renderer); + const model = new EditorModel( + [ + pc.newline(), + pc.newline(), + pc.plain("hello world,"), + pc.newline(), + pc.plain("how are you doing?"), + pc.newline(), + pc.newline(), + ], + pc, + renderer, + ); - const range = model.startRange(model.positionForOffset(0, false), model.getPositionAtEnd()); // select-all + const range = model.startRange(model.positionForOffset(0, false), model.getPositionAtEnd()); // select-all - expect(range.parts.map(p => p.text).join("")).toBe("\n\nhello world,\nhow are you doing?\n\n"); + expect(range.parts.map((p) => p.text).join("")).toBe("\n\nhello world,\nhow are you doing?\n\n"); expect(model.serializeParts()).toEqual([ SERIALIZED_NEWLINE, SERIALIZED_NEWLINE, - { "text": "hello world,", "type": "plain" }, + { text: "hello world,", type: "plain" }, SERIALIZED_NEWLINE, - { "text": "how are you doing?", "type": "plain" }, + { text: "how are you doing?", type: "plain" }, SERIALIZED_NEWLINE, SERIALIZED_NEWLINE, ]); @@ -292,64 +267,65 @@ describe("editor/operations: formatting operations", () => { expect(model.serializeParts()).toEqual([ SERIALIZED_NEWLINE, SERIALIZED_NEWLINE, - { "text": "**hello world,", "type": "plain" }, + { text: "**hello world,", type: "plain" }, SERIALIZED_NEWLINE, - { "text": "how are you doing?**", "type": "plain" }, + { text: "how are you doing?**", type: "plain" }, SERIALIZED_NEWLINE, SERIALIZED_NEWLINE, ]); }); - it('works for multiple paragraph', () => { + it("works for multiple paragraph", () => { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("hello world,"), - pc.newline(), - pc.plain("how are you doing?"), - pc.newline(), - pc.newline(), - pc.plain("new paragraph"), - ], pc, renderer); + const model = new EditorModel( + [ + pc.plain("hello world,"), + pc.newline(), + pc.plain("how are you doing?"), + pc.newline(), + pc.newline(), + pc.plain("new paragraph"), + ], + pc, + renderer, + ); let range = model.startRange(model.positionForOffset(0, true), model.getPositionAtEnd()); // select-all expect(model.serializeParts()).toEqual([ - { "text": "hello world,", "type": "plain" }, + { text: "hello world,", type: "plain" }, SERIALIZED_NEWLINE, - { "text": "how are you doing?", "type": "plain" }, + { text: "how are you doing?", type: "plain" }, SERIALIZED_NEWLINE, SERIALIZED_NEWLINE, - { "text": "new paragraph", "type": "plain" }, + { text: "new paragraph", type: "plain" }, ]); toggleInlineFormat(range, "__"); expect(model.serializeParts()).toEqual([ - { "text": "__hello world,", "type": "plain" }, + { text: "__hello world,", type: "plain" }, SERIALIZED_NEWLINE, - { "text": "how are you doing?__", "type": "plain" }, + { text: "how are you doing?__", type: "plain" }, SERIALIZED_NEWLINE, SERIALIZED_NEWLINE, - { "text": "__new paragraph__", "type": "plain" }, + { text: "__new paragraph__", type: "plain" }, ]); range = model.startRange(model.positionForOffset(0, true), model.getPositionAtEnd()); // select-all toggleInlineFormat(range, "__"); expect(model.serializeParts()).toEqual([ - { "text": "hello world,", "type": "plain" }, + { text: "hello world,", type: "plain" }, SERIALIZED_NEWLINE, - { "text": "how are you doing?", "type": "plain" }, + { text: "how are you doing?", type: "plain" }, SERIALIZED_NEWLINE, SERIALIZED_NEWLINE, - { "text": "new paragraph", "type": "plain" }, + { text: "new paragraph", type: "plain" }, ]); }); - it('format word at caret position at beginning of new line without previous selection', () => { + it("format word at caret position at beginning of new line without previous selection", () => { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.newline(), - pc.plain("hello!"), - ], pc, renderer); + const model = new EditorModel([pc.newline(), pc.plain("hello!")], pc, renderer); let range = model.startRange(model.positionForOffset(1, false)); @@ -359,17 +335,11 @@ describe("editor/operations: formatting operations", () => { formatRange(range, Formatting.Bold); // Toggle - expect(model.serializeParts()).toEqual([ - SERIALIZED_NEWLINE, - { "text": "**hello!**", "type": "plain" }, - ]); + expect(model.serializeParts()).toEqual([SERIALIZED_NEWLINE, { text: "**hello!**", type: "plain" }]); formatRange(range, Formatting.Bold); // Untoggle - expect(model.serializeParts()).toEqual([ - SERIALIZED_NEWLINE, - { "text": "hello!", "type": "plain" }, - ]); + expect(model.serializeParts()).toEqual([SERIALIZED_NEWLINE, { text: "hello!", type: "plain" }]); // Check if it also works for code as it uses toggleInlineFormatting only indirectly range = model.startRange(model.positionForOffset(1, false)); @@ -377,31 +347,25 @@ describe("editor/operations: formatting operations", () => { formatRange(range, Formatting.Code); // Toggle - expect(model.serializeParts()).toEqual([ - SERIALIZED_NEWLINE, - { "text": "`hello!`", "type": "plain" }, - ]); + expect(model.serializeParts()).toEqual([SERIALIZED_NEWLINE, { text: "`hello!`", type: "plain" }]); formatRange(range, Formatting.Code); // Untoggle - expect(model.serializeParts()).toEqual([ - SERIALIZED_NEWLINE, - { "text": "hello!", "type": "plain" }, - ]); + expect(model.serializeParts()).toEqual([SERIALIZED_NEWLINE, { text: "hello!", type: "plain" }]); }); - it('caret resets correctly to current line when untoggling formatting while caret at line end', () => { + it("caret resets correctly to current line when untoggling formatting while caret at line end", () => { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("hello **hello!**"), - pc.newline(), - pc.plain("world"), - ], pc, renderer); + const model = new EditorModel( + [pc.plain("hello **hello!**"), pc.newline(), pc.plain("world")], + pc, + renderer, + ); expect(model.serializeParts()).toEqual([ - { "text": "hello **hello!**", "type": "plain" }, + { text: "hello **hello!**", type: "plain" }, SERIALIZED_NEWLINE, - { "text": "world", "type": "plain" }, + { text: "world", type: "plain" }, ]); const endOfFirstLine = 16; @@ -412,121 +376,118 @@ describe("editor/operations: formatting operations", () => { // We expect formatting to still happen in the first line as the caret should not jump down expect(model.serializeParts()).toEqual([ - { "text": "hello _hello!_", "type": "plain" }, + { text: "hello _hello!_", type: "plain" }, SERIALIZED_NEWLINE, - { "text": "world", "type": "plain" }, + { text: "world", type: "plain" }, ]); }); - it('format link in front of new line part', () => { + it("format link in front of new line part", () => { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("hello!"), - pc.newline(), - pc.plain("world!"), - pc.newline(), - ], pc, renderer); + const model = new EditorModel( + [pc.plain("hello!"), pc.newline(), pc.plain("world!"), pc.newline()], + pc, + renderer, + ); let range = model.startRange(model.getPositionAtEnd().asOffset(model).add(-1).asPosition(model)); // select-all expect(model.serializeParts()).toEqual([ - { "text": "hello!", "type": "plain" }, + { text: "hello!", type: "plain" }, SERIALIZED_NEWLINE, - { "text": "world!", "type": "plain" }, + { text: "world!", type: "plain" }, SERIALIZED_NEWLINE, ]); formatRange(range, Formatting.InsertLink); // Toggle expect(model.serializeParts()).toEqual([ - { "text": "hello!", "type": "plain" }, + { text: "hello!", type: "plain" }, SERIALIZED_NEWLINE, - { "text": "[world!]()", "type": "plain" }, + { text: "[world!]()", type: "plain" }, SERIALIZED_NEWLINE, ]); range = model.startRange(model.getPositionAtEnd().asOffset(model).add(-1).asPosition(model)); // select-all formatRange(range, Formatting.InsertLink); // Untoggle expect(model.serializeParts()).toEqual([ - { "text": "hello!", "type": "plain" }, + { text: "hello!", type: "plain" }, SERIALIZED_NEWLINE, - { "text": "world!", "type": "plain" }, + { text: "world!", type: "plain" }, SERIALIZED_NEWLINE, ]); }); - it('format multi line code', () => { + it("format multi line code", () => { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("int x = 1;"), - pc.newline(), - pc.newline(), - pc.plain("int y = 42;"), - ], pc, renderer); + const model = new EditorModel( + [pc.plain("int x = 1;"), pc.newline(), pc.newline(), pc.plain("int y = 42;")], + pc, + renderer, + ); let range = model.startRange(model.positionForOffset(0), model.getPositionAtEnd()); // select-all - expect(range.parts.map(p => p.text).join("")).toBe("int x = 1;\n\nint y = 42;"); + expect(range.parts.map((p) => p.text).join("")).toBe("int x = 1;\n\nint y = 42;"); expect(model.serializeParts()).toEqual([ - { "text": "int x = 1;", "type": "plain" }, + { text: "int x = 1;", type: "plain" }, SERIALIZED_NEWLINE, SERIALIZED_NEWLINE, - { "text": "int y = 42;", "type": "plain" }, + { text: "int y = 42;", type: "plain" }, ]); formatRange(range, Formatting.Code); // Toggle expect(model.serializeParts()).toEqual([ - { "text": "```", "type": "plain" }, + { text: "```", type: "plain" }, SERIALIZED_NEWLINE, - { "text": "int x = 1;", "type": "plain" }, + { text: "int x = 1;", type: "plain" }, SERIALIZED_NEWLINE, SERIALIZED_NEWLINE, - { "text": "int y = 42;", "type": "plain" }, + { text: "int y = 42;", type: "plain" }, SERIALIZED_NEWLINE, - { "text": "```", "type": "plain" }, + { text: "```", type: "plain" }, ]); range = model.startRange(model.positionForOffset(0, false), model.getPositionAtEnd()); // select-all formatRange(range, Formatting.Code); // Untoggle expect(model.serializeParts()).toEqual([ - { "text": "int x = 1;", "type": "plain" }, + { text: "int x = 1;", type: "plain" }, SERIALIZED_NEWLINE, SERIALIZED_NEWLINE, - { "text": "int y = 42;", "type": "plain" }, + { text: "int y = 42;", type: "plain" }, ]); }); - it('does not format pure white space', () => { + it("does not format pure white space", () => { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain(" "), - pc.newline(), - pc.newline(), - pc.plain(" "), - ], pc, renderer); + const model = new EditorModel( + [pc.plain(" "), pc.newline(), pc.newline(), pc.plain(" ")], + pc, + renderer, + ); const range = model.startRange(model.positionForOffset(0), model.getPositionAtEnd()); // select-all - expect(range.parts.map(p => p.text).join("")).toBe(" \n\n "); + expect(range.parts.map((p) => p.text).join("")).toBe(" \n\n "); expect(model.serializeParts()).toEqual([ - { "text": " ", "type": "plain" }, + { text: " ", type: "plain" }, SERIALIZED_NEWLINE, SERIALIZED_NEWLINE, - { "text": " ", "type": "plain" }, + { text: " ", type: "plain" }, ]); formatRange(range, Formatting.Bold); expect(model.serializeParts()).toEqual([ - { "text": " ", "type": "plain" }, + { text: " ", type: "plain" }, SERIALIZED_NEWLINE, SERIALIZED_NEWLINE, - { "text": " ", "type": "plain" }, + { text: " ", type: "plain" }, ]); }); }); diff --git a/test/editor/position-test.ts b/test/editor/position-test.ts index fe7395fdc4..ca638df021 100644 --- a/test/editor/position-test.ts +++ b/test/editor/position-test.ts @@ -27,52 +27,64 @@ function createRenderer() { return render; } -describe('editor/position', function() { - it('move first position backward in empty model', function() { +describe("editor/position", function () { + it("move first position backward in empty model", function () { const model = new EditorModel([], createPartCreator(), createRenderer()); const pos = model.positionForOffset(0, true); const pos2 = pos.backwardsWhile(model, () => true); expect(pos).toBe(pos2); }); - it('move first position forwards in empty model', function() { + it("move first position forwards in empty model", function () { const model = new EditorModel([], createPartCreator(), createRenderer()); const pos = model.positionForOffset(0, true); const pos2 = pos.forwardsWhile(model, () => true); expect(pos).toBe(pos2); }); - it('move forwards within one part', function() { + it("move forwards within one part", function () { const pc = createPartCreator(); const model = new EditorModel([pc.plain("hello")], pc, createRenderer()); const pos = model.positionForOffset(1); let n = 3; - const pos2 = pos.forwardsWhile(model, () => { n -= 1; return n >= 0; }); + const pos2 = pos.forwardsWhile(model, () => { + n -= 1; + return n >= 0; + }); expect(pos2.index).toBe(0); expect(pos2.offset).toBe(4); }); - it('move forwards crossing to other part', function() { + it("move forwards crossing to other part", function () { const pc = createPartCreator(); const model = new EditorModel([pc.plain("hello"), pc.plain(" world")], pc, createRenderer()); const pos = model.positionForOffset(4); let n = 3; - const pos2 = pos.forwardsWhile(model, () => { n -= 1; return n >= 0; }); + const pos2 = pos.forwardsWhile(model, () => { + n -= 1; + return n >= 0; + }); expect(pos2.index).toBe(1); expect(pos2.offset).toBe(2); }); - it('move backwards within one part', function() { + it("move backwards within one part", function () { const pc = createPartCreator(); const model = new EditorModel([pc.plain("hello")], pc, createRenderer()); const pos = model.positionForOffset(4); let n = 3; - const pos2 = pos.backwardsWhile(model, () => { n -= 1; return n >= 0; }); + const pos2 = pos.backwardsWhile(model, () => { + n -= 1; + return n >= 0; + }); expect(pos2.index).toBe(0); expect(pos2.offset).toBe(1); }); - it('move backwards crossing to other part', function() { + it("move backwards crossing to other part", function () { const pc = createPartCreator(); const model = new EditorModel([pc.plain("hello"), pc.plain(" world")], pc, createRenderer()); const pos = model.positionForOffset(7); let n = 3; - const pos2 = pos.backwardsWhile(model, () => { n -= 1; return n >= 0; }); + const pos2 = pos.backwardsWhile(model, () => { + n -= 1; + return n >= 0; + }); expect(pos2.index).toBe(0); expect(pos2.offset).toBe(4); }); diff --git a/test/editor/range-test.ts b/test/editor/range-test.ts index d0122146a5..b0dfd58b63 100644 --- a/test/editor/range-test.ts +++ b/test/editor/range-test.ts @@ -19,25 +19,25 @@ import { createPartCreator, createRenderer } from "./mock"; const pillChannel = "#riot-dev:matrix.org"; -describe('editor/range', function() { - it('range on empty model', function() { +describe("editor/range", function () { + it("range on empty model", function () { const renderer = createRenderer(); const pc = createPartCreator(); const model = new EditorModel([], pc, renderer); - const range = model.startRange(model.positionForOffset(0, true)); // after "world" + const range = model.startRange(model.positionForOffset(0, true)); // after "world" let called = false; - range.expandBackwardsWhile(chr => { + range.expandBackwardsWhile((chr) => { called = true; return true; }); expect(called).toBe(false); expect(range.text).toBe(""); }); - it('range replace within a part', function() { + it("range replace within a part", function () { const renderer = createRenderer(); const pc = createPartCreator(); const model = new EditorModel([pc.plain("hello world!!!!")], pc, renderer); - const range = model.startRange(model.positionForOffset(11)); // after "world" + const range = model.startRange(model.positionForOffset(11)); // after "world" range.expandBackwardsWhile((index, offset) => model.parts[index].text[offset] !== " "); expect(range.text).toBe("world"); range.replace([pc.roomPill(pillChannel)]); @@ -49,16 +49,15 @@ describe('editor/range', function() { expect(model.parts[2].text).toBe("!!!!"); expect(model.parts.length).toBe(3); }); - it('range replace across parts', function() { + it("range replace across parts", function () { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("try to re"), - pc.plain("pla"), - pc.plain("ce "), - pc.plain("me"), - ], pc, renderer); - const range = model.startRange(model.positionForOffset(14)); // after "replace" + const model = new EditorModel( + [pc.plain("try to re"), pc.plain("pla"), pc.plain("ce "), pc.plain("me")], + pc, + renderer, + ); + const range = model.startRange(model.positionForOffset(14)); // after "replace" range.expandBackwardsWhile((index, offset) => model.parts[index].text[offset] !== " "); expect(range.text).toBe("replace"); range.replace([pc.roomPill(pillChannel)]); @@ -71,14 +70,11 @@ describe('editor/range', function() { expect(model.parts.length).toBe(3); }); // bug found while implementing tab completion - it('replace a part with an identical part with start position at end of previous part', function() { + it("replace a part with an identical part with start position at end of previous part", function () { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("hello "), - pc.pillCandidate("man"), - ], pc, renderer); - const range = model.startRange(model.positionForOffset(9, true)); // before "man" + const model = new EditorModel([pc.plain("hello "), pc.pillCandidate("man")], pc, renderer); + const range = model.startRange(model.positionForOffset(9, true)); // before "man" range.expandBackwardsWhile((index, offset) => model.parts[index].text[offset] !== " "); expect(range.text).toBe("man"); range.replace([pc.pillCandidate(range.text)]); @@ -88,12 +84,10 @@ describe('editor/range', function() { expect(model.parts[1].text).toBe("man"); expect(model.parts.length).toBe(2); }); - it('range trim spaces off both ends', () => { + it("range trim spaces off both ends", () => { const renderer = createRenderer(); const pc = createPartCreator(); - const model = new EditorModel([ - pc.plain("abc abc abc"), - ], pc, renderer); + const model = new EditorModel([pc.plain("abc abc abc")], pc, renderer); const range = model.startRange( model.positionForOffset(3, false), // at end of first `abc` model.positionForOffset(8, false), // at start of last `abc` @@ -104,17 +98,12 @@ describe('editor/range', function() { expect(range.parts[0].text).toBe("abc"); }); // test for edge case when the selection just consists of whitespace - it('range trim just whitespace', () => { + it("range trim just whitespace", () => { const renderer = createRenderer(); const pc = createPartCreator(); const whitespace = " \n \n\n"; - const model = new EditorModel([ - pc.plain(whitespace), - ], pc, renderer); - const range = model.startRange( - model.positionForOffset(0, false), - model.getPositionAtEnd(), - ); + const model = new EditorModel([pc.plain(whitespace)], pc, renderer); + const range = model.startRange(model.positionForOffset(0, false), model.getPositionAtEnd()); expect(range.text).toBe(whitespace); range.trim(); diff --git a/test/editor/roundtrip-test.ts b/test/editor/roundtrip-test.ts index 424f639233..b9a597b806 100644 --- a/test/editor/roundtrip-test.ts +++ b/test/editor/roundtrip-test.ts @@ -14,12 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MatrixEvent } from 'matrix-js-sdk/src/matrix'; +import { MatrixEvent } from "matrix-js-sdk/src/matrix"; import { parseEvent } from "../../src/editor/deserialize"; -import EditorModel from '../../src/editor/model'; -import DocumentOffset from '../../src/editor/offset'; -import { htmlSerializeIfNeeded, textSerialize } from '../../src/editor/serialize'; +import EditorModel from "../../src/editor/model"; +import DocumentOffset from "../../src/editor/offset"; +import { htmlSerializeIfNeeded, textSerialize } from "../../src/editor/serialize"; import { createPartCreator } from "./mock"; function htmlMessage(formattedBody: string, msgtype = "m.text") { @@ -37,11 +37,7 @@ function htmlMessage(formattedBody: string, msgtype = "m.text") { async function md2html(markdown: string): Promise { const pc = createPartCreator(); const oldModel = new EditorModel([], pc, () => {}); - await oldModel.update( - markdown, - "insertText", - new DocumentOffset(markdown.length, false), - ); + await oldModel.update(markdown, "insertText", new DocumentOffset(markdown.length, false)); return htmlSerializeIfNeeded(oldModel, { forceHTML: true }); } @@ -60,8 +56,8 @@ async function roundTripHtml(html: string): Promise { return await md2html(html2md(html)); } -describe('editor/roundtrip', function() { - describe('markdown messages should round-trip if they contain', function() { +describe("editor/roundtrip", function () { + describe("markdown messages should round-trip if they contain", function () { test.each([ ["newlines", "hello\nworld"], ["pills", "text message for @room"], @@ -85,7 +81,7 @@ describe('editor/roundtrip', function() { ["nested quotations", "saying\n\n> > foo\n\n> NO\n\nis valid"], ["quotations", "saying\n\n> NO\n\nis valid"], ["links", "click [this](http://example.com/)!"], - ])('%s', async (_name, markdown) => { + ])("%s", async (_name, markdown) => { expect(await roundTripMarkdown(markdown)).toEqual(markdown); }); @@ -109,7 +105,7 @@ describe('editor/roundtrip', function() { // Backslashes get doubled ["backslashes", "C:\\Program Files"], // Deletes the whitespace - ['newlines with trailing and leading whitespace', "hello \n world"], + ["newlines with trailing and leading whitespace", "hello \n world"], // Escapes the underscores ["underscores within a word", "abso_fragging_lutely"], // Includes the trailing text into the quotation @@ -117,17 +113,16 @@ describe('editor/roundtrip', function() { ["quotations without separating newlines", "saying\n> NO\nis valid"], // Removes trailing and leading whitespace ["quotations with trailing and leading whitespace", "saying \n\n> NO\n\n is valid"], - ])('%s', async (_name, markdown) => { + ])("%s", async (_name, markdown) => { expect(await roundTripMarkdown(markdown)).toEqual(markdown); }); - it('styling, but * becomes _ and __ becomes **', async function() { - expect(await roundTripMarkdown("__bold__ and *emphasised*")) - .toEqual("**bold** and _emphasised_"); + it("styling, but * becomes _ and __ becomes **", async function () { + expect(await roundTripMarkdown("__bold__ and *emphasised*")).toEqual("**bold** and _emphasised_"); }); }); - describe('HTML messages should round-trip if they contain', function() { + describe("HTML messages should round-trip if they contain", function () { test.each([ ["backslashes", "C:\\Program Files"], [ @@ -140,7 +135,7 @@ describe('editor/roundtrip', function() { ["code blocks with surrounding text", "

a

\n
a\ny;\n
\n

b

\n"], ["code blocks", "
a\ny;\n
\n"], ["code blocks containing markdown", "
__init__.py\n
\n"], - ["code blocks with language specifier", "
__init__.py\n
\n"], + ["code blocks with language specifier", '
__init__.py\n
\n'], ["paragraphs including formatting", "

one

\n

t w o

\n"], ["paragraphs", "

one

\n

two

\n"], ["links", "http://more.example.com/"], @@ -149,7 +144,7 @@ describe('editor/roundtrip', function() { ["formatting within a word", "absofragginglutely"], ["formatting", "This is important"], ["line breaks", "one
two"], - ])('%s', async (_name, html) => { + ])("%s", async (_name, html) => { expect(await roundTripHtml(html)).toEqual(html); }); @@ -163,7 +158,7 @@ describe('editor/roundtrip', function() { ["paragraphs without newlines", "

one

two

"], // Inserts a code block ["nested lists", "
    \n
  1. asd
  2. \n
  3. \n
      \n
    • fgd
    • \n
    • sdf
    • \n
    \n
  4. \n
\n"], - ])('%s', async (_name, html) => { + ])("%s", async (_name, html) => { expect(await roundTripHtml(html)).toEqual(html); }); }); diff --git a/test/editor/serialize-test.ts b/test/editor/serialize-test.ts index dcad03c9c8..25bfd17c93 100644 --- a/test/editor/serialize-test.ts +++ b/test/editor/serialize-test.ts @@ -18,84 +18,84 @@ import EditorModel from "../../src/editor/model"; import { htmlSerializeIfNeeded } from "../../src/editor/serialize"; import { createPartCreator } from "./mock"; -describe('editor/serialize', function() { - describe('with markdown', function() { - it('user pill turns message into html', function() { +describe("editor/serialize", function () { + describe("with markdown", function () { + it("user pill turns message into html", function () { const pc = createPartCreator(); const model = new EditorModel([pc.userPill("Alice", "@alice:hs.tld")], pc); const html = htmlSerializeIfNeeded(model, {}); - expect(html).toBe("Alice"); + expect(html).toBe('Alice'); }); - it('room pill turns message into html', function() { + it("room pill turns message into html", function () { const pc = createPartCreator(); const model = new EditorModel([pc.roomPill("#room:hs.tld")], pc); const html = htmlSerializeIfNeeded(model, {}); - expect(html).toBe("#room:hs.tld"); + expect(html).toBe('#room:hs.tld'); }); - it('@room pill turns message into html', function() { + it("@room pill turns message into html", function () { const pc = createPartCreator(); const model = new EditorModel([pc.atRoomPill("@room")], pc); const html = htmlSerializeIfNeeded(model, {}); expect(html).toBeFalsy(); }); - it('any markdown turns message into html', function() { + it("any markdown turns message into html", function () { const pc = createPartCreator(); const model = new EditorModel([pc.plain("*hello* world")], pc); const html = htmlSerializeIfNeeded(model, {}); expect(html).toBe("hello world"); }); - it('displaynames ending in a backslash work', function() { + it("displaynames ending in a backslash work", function () { const pc = createPartCreator(); const model = new EditorModel([pc.userPill("Displayname\\", "@user:server")], pc); const html = htmlSerializeIfNeeded(model, {}); - expect(html).toBe("Displayname\\"); + expect(html).toBe('Displayname\\'); }); - it('displaynames containing an opening square bracket work', function() { + it("displaynames containing an opening square bracket work", function () { const pc = createPartCreator(); const model = new EditorModel([pc.userPill("Displayname[[", "@user:server")], pc); const html = htmlSerializeIfNeeded(model, {}); - expect(html).toBe("Displayname[["); + expect(html).toBe('Displayname[['); }); - it('displaynames containing a closing square bracket work', function() { + it("displaynames containing a closing square bracket work", function () { const pc = createPartCreator(); const model = new EditorModel([pc.userPill("Displayname]", "@user:server")], pc); const html = htmlSerializeIfNeeded(model, {}); - expect(html).toBe("Displayname]"); + expect(html).toBe('Displayname]'); }); - it('escaped markdown should not retain backslashes', function() { + it("escaped markdown should not retain backslashes", function () { const pc = createPartCreator(); - const model = new EditorModel([pc.plain('\\*hello\\* world')], pc); + const model = new EditorModel([pc.plain("\\*hello\\* world")], pc); const html = htmlSerializeIfNeeded(model, {}); - expect(html).toBe('*hello* world'); + expect(html).toBe("*hello* world"); }); - it('escaped markdown should convert HTML entities', function() { + it("escaped markdown should convert HTML entities", function () { const pc = createPartCreator(); - const model = new EditorModel([pc.plain('\\*hello\\* world < hey world!')], pc); + const model = new EditorModel([pc.plain("\\*hello\\* world < hey world!")], pc); const html = htmlSerializeIfNeeded(model, {}); - expect(html).toBe('*hello* world < hey world!'); + expect(html).toBe("*hello* world < hey world!"); }); }); - describe('with plaintext', function() { - it('markdown remains plaintext', function() { + describe("with plaintext", function () { + it("markdown remains plaintext", function () { const pc = createPartCreator(); const model = new EditorModel([pc.plain("*hello* world")], pc); const html = htmlSerializeIfNeeded(model, { useMarkdown: false }); expect(html).toBe("*hello* world"); }); - it('markdown should retain backslashes', function() { + it("markdown should retain backslashes", function () { const pc = createPartCreator(); - const model = new EditorModel([pc.plain('\\*hello\\* world')], pc); + const model = new EditorModel([pc.plain("\\*hello\\* world")], pc); const html = htmlSerializeIfNeeded(model, { useMarkdown: false }); - expect(html).toBe('\\*hello\\* world'); + expect(html).toBe("\\*hello\\* world"); }); - it('markdown should convert HTML entities', function() { + it("markdown should convert HTML entities", function () { const pc = createPartCreator(); - const model = new EditorModel([pc.plain('\\*hello\\* world < hey world!')], pc); + const model = new EditorModel([pc.plain("\\*hello\\* world < hey world!")], pc); const html = htmlSerializeIfNeeded(model, { useMarkdown: false }); - expect(html).toBe('\\*hello\\* world < hey world!'); + expect(html).toBe("\\*hello\\* world < hey world!"); }); - it('plaintext remains plaintext even when forcing html', function() { + it("plaintext remains plaintext even when forcing html", function () { const pc = createPartCreator(); const model = new EditorModel([pc.plain("hello world")], pc); const html = htmlSerializeIfNeeded(model, { forceHTML: true, useMarkdown: false }); diff --git a/test/events/RelationsHelper-test.ts b/test/events/RelationsHelper-test.ts index 8b6a891834..0c2368b1fe 100644 --- a/test/events/RelationsHelper-test.ts +++ b/test/events/RelationsHelper-test.ts @@ -91,7 +91,7 @@ describe("RelationsHelper", () => { // TODO Michael W: create test utils, remove casts relations = { getRelations: jest.fn(), - on: jest.fn().mockImplementation((type, l) => relationsOnAdd = l), + on: jest.fn().mockImplementation((type, l) => (relationsOnAdd = l)), off: jest.fn(), } as unknown as Relations; timelineSet = { diff --git a/test/events/forward/getForwardableEvent-test.ts b/test/events/forward/getForwardableEvent-test.ts index 2985f527bc..397ce8e1b7 100644 --- a/test/events/forward/getForwardableEvent-test.ts +++ b/test/events/forward/getForwardableEvent-test.ts @@ -14,11 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { - EventType, - MatrixEvent, - MsgType, -} from "matrix-js-sdk/src/matrix"; +import { EventType, MatrixEvent, MsgType } from "matrix-js-sdk/src/matrix"; import { getForwardableEvent } from "../../../src/events"; import { @@ -29,53 +25,53 @@ import { makeRoomWithBeacons, } from "../../test-utils"; -describe('getForwardableEvent()', () => { - const userId = '@alice:server.org'; - const roomId = '!room:server.org'; +describe("getForwardableEvent()", () => { + const userId = "@alice:server.org"; + const roomId = "!room:server.org"; const client = getMockClientWithEventEmitter({ getRoom: jest.fn(), }); - it('returns the event for a room message', () => { + it("returns the event for a room message", () => { const alicesMessageEvent = new MatrixEvent({ type: EventType.RoomMessage, sender: userId, room_id: roomId, content: { msgtype: MsgType.Text, - body: 'Hello', + body: "Hello", }, }); expect(getForwardableEvent(alicesMessageEvent, client)).toBe(alicesMessageEvent); }); - it('returns null for a poll start event', () => { - const pollStartEvent = makePollStartEvent('test?', userId); + it("returns null for a poll start event", () => { + const pollStartEvent = makePollStartEvent("test?", userId); expect(getForwardableEvent(pollStartEvent, client)).toBe(null); }); - describe('beacons', () => { - it('returns null for a beacon that is not live', () => { + describe("beacons", () => { + it("returns null for a beacon that is not live", () => { const notLiveBeacon = makeBeaconInfoEvent(userId, roomId, { isLive: false }); makeRoomWithBeacons(roomId, client, [notLiveBeacon]); expect(getForwardableEvent(notLiveBeacon, client)).toBe(null); }); - it('returns null for a live beacon that does not have a location', () => { + it("returns null for a live beacon that does not have a location", () => { const liveBeacon = makeBeaconInfoEvent(userId, roomId, { isLive: true }); makeRoomWithBeacons(roomId, client, [liveBeacon]); expect(getForwardableEvent(liveBeacon, client)).toBe(null); }); - it('returns the latest location event for a live beacon with location', () => { - const liveBeacon = makeBeaconInfoEvent(userId, roomId, { isLive: true }, 'id'); + it("returns the latest location event for a live beacon with location", () => { + const liveBeacon = makeBeaconInfoEvent(userId, roomId, { isLive: true }, "id"); const locationEvent = makeBeaconEvent(userId, { beaconInfoId: liveBeacon.getId(), - geoUri: 'geo:52,42', + geoUri: "geo:52,42", // make sure its in live period timestamp: Date.now() + 1, }); diff --git a/test/events/location/getShareableLocationEvent-test.ts b/test/events/location/getShareableLocationEvent-test.ts index fe2d83174c..47f4b61088 100644 --- a/test/events/location/getShareableLocationEvent-test.ts +++ b/test/events/location/getShareableLocationEvent-test.ts @@ -14,11 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { - EventType, - MatrixEvent, - MsgType, -} from "matrix-js-sdk/src/matrix"; +import { EventType, MatrixEvent, MsgType } from "matrix-js-sdk/src/matrix"; import { getShareableLocationEvent } from "../../../src/events"; import { @@ -29,53 +25,53 @@ import { makeRoomWithBeacons, } from "../../test-utils"; -describe('getShareableLocationEvent()', () => { - const userId = '@alice:server.org'; - const roomId = '!room:server.org'; +describe("getShareableLocationEvent()", () => { + const userId = "@alice:server.org"; + const roomId = "!room:server.org"; const client = getMockClientWithEventEmitter({ getRoom: jest.fn(), }); - it('returns null for a non-location event', () => { + it("returns null for a non-location event", () => { const alicesMessageEvent = new MatrixEvent({ type: EventType.RoomMessage, sender: userId, room_id: roomId, content: { msgtype: MsgType.Text, - body: 'Hello', + body: "Hello", }, }); expect(getShareableLocationEvent(alicesMessageEvent, client)).toBe(null); }); - it('returns the event for a location event', () => { - const locationEvent = makeLocationEvent('geo:52,42'); + it("returns the event for a location event", () => { + const locationEvent = makeLocationEvent("geo:52,42"); expect(getShareableLocationEvent(locationEvent, client)).toBe(locationEvent); }); - describe('beacons', () => { - it('returns null for a beacon that is not live', () => { + describe("beacons", () => { + it("returns null for a beacon that is not live", () => { const notLiveBeacon = makeBeaconInfoEvent(userId, roomId, { isLive: false }); makeRoomWithBeacons(roomId, client, [notLiveBeacon]); expect(getShareableLocationEvent(notLiveBeacon, client)).toBe(null); }); - it('returns null for a live beacon that does not have a location', () => { + it("returns null for a live beacon that does not have a location", () => { const liveBeacon = makeBeaconInfoEvent(userId, roomId, { isLive: true }); makeRoomWithBeacons(roomId, client, [liveBeacon]); expect(getShareableLocationEvent(liveBeacon, client)).toBe(null); }); - it('returns the latest location event for a live beacon with location', () => { - const liveBeacon = makeBeaconInfoEvent(userId, roomId, { isLive: true }, 'id'); + it("returns the latest location event for a live beacon with location", () => { + const liveBeacon = makeBeaconInfoEvent(userId, roomId, { isLive: true }, "id"); const locationEvent = makeBeaconEvent(userId, { beaconInfoId: liveBeacon.getId(), - geoUri: 'geo:52,42', + geoUri: "geo:52,42", // make sure its in live period timestamp: Date.now() + 1, }); diff --git a/test/globalSetup.js b/test/globalSetup.js index 83b2ac971b..ebfa6a4c92 100644 --- a/test/globalSetup.js +++ b/test/globalSetup.js @@ -15,5 +15,5 @@ limitations under the License. */ module.exports = async () => { - process.env.TZ = 'UTC'; + process.env.TZ = "UTC"; }; diff --git a/test/hooks/useDebouncedCallback-test.tsx b/test/hooks/useDebouncedCallback-test.tsx index d0428358f0..0e1fc30e58 100644 --- a/test/hooks/useDebouncedCallback-test.tsx +++ b/test/hooks/useDebouncedCallback-test.tsx @@ -23,13 +23,13 @@ describe("useDebouncedCallback", () => { afterAll(() => jest.useRealTimers()); function render(enabled: boolean, callback: (...params: any) => void, params: any) { - return renderHook( - ({ enabled, callback, params }) => useDebouncedCallback(enabled, callback, params), - { initialProps: { + return renderHook(({ enabled, callback, params }) => useDebouncedCallback(enabled, callback, params), { + initialProps: { enabled, callback, params, - } }); + }, + }); } it("should be able to handle empty parameters", async () => { diff --git a/test/hooks/useLatestResult-test.tsx b/test/hooks/useLatestResult-test.tsx index f4c9280e39..2da4e27758 100644 --- a/test/hooks/useLatestResult-test.tsx +++ b/test/hooks/useLatestResult-test.tsx @@ -27,14 +27,12 @@ function LatestResultsComponent({ query, doRequest }) { const [updateQuery, updateResult] = useLatestResult(setValueInternal); useEffect(() => { updateQuery(query); - doRequest(query).then(it => { + doRequest(query).then((it) => { updateResult(query, it); }); }, [doRequest, query, updateQuery, updateResult]); - return
- { value } -
; + return
{value}
; } describe("useLatestResult", () => { diff --git a/test/hooks/useProfileInfo-test.tsx b/test/hooks/useProfileInfo-test.tsx index 0debce3799..41d3f95b48 100644 --- a/test/hooks/useProfileInfo-test.tsx +++ b/test/hooks/useProfileInfo-test.tsx @@ -27,18 +27,14 @@ import { stubClient } from "../test-utils/test-utils"; function ProfileInfoComponent({ onClick }) { const profileInfo = useProfileInfo(); - const { - ready, - loading, - profile, - } = profileInfo; + const { ready, loading, profile } = profileInfo; - return
onClick(profileInfo)}> - { (!ready || loading) && `ready: ${ready}, loading: ${loading}` } - { profile && ( - `Name: ${profile.display_name}` - ) } -
; + return ( +
onClick(profileInfo)}> + {(!ready || loading) && `ready: ${ready}, loading: ${loading}`} + {profile && `Name: ${profile.display_name}`} +
+ ); } describe("useProfileInfo", () => { @@ -58,12 +54,16 @@ describe("useProfileInfo", () => { it("should display user profile when searching", async () => { const query = "@user:home.server"; - const wrapper = mount( { - hook.search({ - limit: 1, - query, - }); - }} />); + const wrapper = mount( + { + hook.search({ + limit: 1, + query, + }); + }} + />, + ); await act(async () => { await sleep(1); @@ -75,12 +75,16 @@ describe("useProfileInfo", () => { }); it("should work with empty queries", async () => { - const wrapper = mount( { - hook.search({ - limit: 1, - query: "", - }); - }} />); + const wrapper = mount( + { + hook.search({ + limit: 1, + query: "", + }); + }} + />, + ); await act(async () => { await sleep(1); @@ -92,18 +96,19 @@ describe("useProfileInfo", () => { }); it("should treat invalid mxids as empty queries", async () => { - const queries = [ - "@user", - "user@home.server", - ]; + const queries = ["@user", "user@home.server"]; for (const query of queries) { - const wrapper = mount( { - hook.search({ - limit: 1, - query, - }); - }} />); + const wrapper = mount( + { + hook.search({ + limit: 1, + query, + }); + }} + />, + ); await act(async () => { await sleep(1); @@ -116,15 +121,21 @@ describe("useProfileInfo", () => { }); it("should recover from a server exception", async () => { - cli.getProfileInfo = () => { throw new Error("Oops"); }; + cli.getProfileInfo = () => { + throw new Error("Oops"); + }; const query = "@user:home.server"; - const wrapper = mount( { - hook.search({ - limit: 1, - query, - }); - }} />); + const wrapper = mount( + { + hook.search({ + limit: 1, + query, + }); + }} + />, + ); await act(async () => { await sleep(1); wrapper.simulate("click"); @@ -138,12 +149,16 @@ describe("useProfileInfo", () => { cli.getProfileInfo = () => null; const query = "@user:home.server"; - const wrapper = mount( { - hook.search({ - limit: 1, - query, - }); - }} />); + const wrapper = mount( + { + hook.search({ + limit: 1, + query, + }); + }} + />, + ); await act(async () => { await sleep(1); wrapper.simulate("click"); diff --git a/test/hooks/usePublicRoomDirectory-test.tsx b/test/hooks/usePublicRoomDirectory-test.tsx index 12db308bee..8836a9998c 100644 --- a/test/hooks/usePublicRoomDirectory-test.tsx +++ b/test/hooks/usePublicRoomDirectory-test.tsx @@ -27,18 +27,14 @@ import { stubClient } from "../test-utils/test-utils"; function PublicRoomComponent({ onClick }) { const roomDirectory = usePublicRoomDirectory(); - const { - ready, - loading, - publicRooms, - } = roomDirectory; + const { ready, loading, publicRooms } = roomDirectory; - return
onClick(roomDirectory)}> - { (!ready || loading) && `ready: ${ready}, loading: ${loading}` } - { publicRooms[0] && ( - `Name: ${publicRooms[0].name}` - ) } -
; + return ( +
onClick(roomDirectory)}> + {(!ready || loading) && `ready: ${ready}, loading: ${loading}`} + {publicRooms[0] && `Name: ${publicRooms[0].name}`} +
+ ); } describe("usePublicRoomDirectory", () => { @@ -50,27 +46,34 @@ describe("usePublicRoomDirectory", () => { MatrixClientPeg.getHomeserverName = () => "matrix.org"; cli.getThirdpartyProtocols = () => Promise.resolve({}); - cli.publicRooms = (({ filter: { generic_search_term: query } }) => Promise.resolve({ - chunk: [{ - room_id: "hello world!", - name: query, - world_readable: true, - guest_can_join: true, - num_joined_members: 1, - }], - total_room_count_estimate: 1, - })); + cli.publicRooms = ({ filter: { generic_search_term: query } }) => + Promise.resolve({ + chunk: [ + { + room_id: "hello world!", + name: query, + world_readable: true, + guest_can_join: true, + num_joined_members: 1, + }, + ], + total_room_count_estimate: 1, + }); }); it("should display public rooms when searching", async () => { const query = "ROOM NAME"; - const wrapper = mount( { - hook.search({ - limit: 1, - query, - }); - }} />); + const wrapper = mount( + { + hook.search({ + limit: 1, + query, + }); + }} + />, + ); expect(wrapper.text()).toBe("ready: false, loading: false"); @@ -84,12 +87,16 @@ describe("usePublicRoomDirectory", () => { }); it("should work with empty queries", async () => { - const wrapper = mount( { - hook.search({ - limit: 1, - query: "", - }); - }} />); + const wrapper = mount( + { + hook.search({ + limit: 1, + query: "", + }); + }} + />, + ); await act(async () => { await sleep(1); @@ -101,15 +108,21 @@ describe("usePublicRoomDirectory", () => { }); it("should recover from a server exception", async () => { - cli.publicRooms = () => { throw new Error("Oops"); }; + cli.publicRooms = () => { + throw new Error("Oops"); + }; const query = "ROOM NAME"; - const wrapper = mount( { - hook.search({ - limit: 1, - query, - }); - }} />); + const wrapper = mount( + { + hook.search({ + limit: 1, + query, + }); + }} + />, + ); await act(async () => { await sleep(1); wrapper.simulate("click"); diff --git a/test/hooks/useUserDirectory-test.tsx b/test/hooks/useUserDirectory-test.tsx index 3813efa472..730b1624da 100644 --- a/test/hooks/useUserDirectory-test.tsx +++ b/test/hooks/useUserDirectory-test.tsx @@ -27,19 +27,13 @@ import { stubClient } from "../test-utils"; function UserDirectoryComponent({ onClick }) { const userDirectory = useUserDirectory(); - const { - ready, - loading, - users, - } = userDirectory; + const { ready, loading, users } = userDirectory; - return
onClick(userDirectory)}> - { users[0] - ? ( - `Name: ${users[0].name}` - ) - : `ready: ${ready}, loading: ${loading}` } -
; + return ( +
onClick(userDirectory)}> + {users[0] ? `Name: ${users[0].name}` : `ready: ${ready}, loading: ${loading}`} +
+ ); } describe("useUserDirectory", () => { @@ -51,23 +45,30 @@ describe("useUserDirectory", () => { MatrixClientPeg.getHomeserverName = () => "matrix.org"; cli.getThirdpartyProtocols = () => Promise.resolve({}); - cli.searchUserDirectory = (({ term: query }) => Promise.resolve({ - results: [{ - user_id: "@bob:matrix.org", - display_name: query, - }] }, - )); + cli.searchUserDirectory = ({ term: query }) => + Promise.resolve({ + results: [ + { + user_id: "@bob:matrix.org", + display_name: query, + }, + ], + }); }); it("search for users in the identity server", async () => { const query = "Bob"; - const wrapper = mount( { - hook.search({ - limit: 1, - query, - }); - }} />); + const wrapper = mount( + { + hook.search({ + limit: 1, + query, + }); + }} + />, + ); expect(wrapper.text()).toBe("ready: true, loading: false"); @@ -83,12 +84,16 @@ describe("useUserDirectory", () => { it("should work with empty queries", async () => { const query = ""; - const wrapper = mount( { - hook.search({ - limit: 1, - query, - }); - }} />); + const wrapper = mount( + { + hook.search({ + limit: 1, + query, + }); + }} + />, + ); await act(async () => { await sleep(1); wrapper.simulate("click"); @@ -98,15 +103,21 @@ describe("useUserDirectory", () => { }); it("should recover from a server exception", async () => { - cli.searchUserDirectory = () => { throw new Error("Oops"); }; + cli.searchUserDirectory = () => { + throw new Error("Oops"); + }; const query = "Bob"; - const wrapper = mount( { - hook.search({ - limit: 1, - query, - }); - }} />); + const wrapper = mount( + { + hook.search({ + limit: 1, + query, + }); + }} + />, + ); await act(async () => { await sleep(1); wrapper.simulate("click"); diff --git a/test/i18n-test/languageHandler-test.tsx b/test/i18n-test/languageHandler-test.tsx index a69ecd7dd5..74158025c1 100644 --- a/test/i18n-test/languageHandler-test.tsx +++ b/test/i18n-test/languageHandler-test.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; import { _t, @@ -23,80 +23,62 @@ import { setLanguage, setMissingEntryGenerator, substitute, -} from '../../src/languageHandler'; -import { stubClient } from '../test-utils'; +} from "../../src/languageHandler"; +import { stubClient } from "../test-utils"; -describe('languageHandler', function() { +describe("languageHandler", function () { // See setupLanguage.ts for how we are stubbing out translations to provide fixture data for these tests - const basicString = 'Rooms'; - const selfClosingTagSub = 'Accept to continue:'; - const textInTagSub = 'Upgrade to your own domain'; - const plurals = 'and %(count)s others...'; - const variableSub = 'You are now ignoring %(userId)s'; + const basicString = "Rooms"; + const selfClosingTagSub = "Accept to continue:"; + const textInTagSub = "Upgrade to your own domain"; + const plurals = "and %(count)s others..."; + const variableSub = "You are now ignoring %(userId)s"; type TestCase = [string, string, Record, Record, TranslatedString]; const testCasesEn: TestCase[] = [ // description of the test case, translationString, variables, tags, expected result - ['translates a basic string', basicString, {}, undefined, 'Rooms'], + ["translates a basic string", basicString, {}, undefined, "Rooms"], + ["handles plurals when count is 0", plurals, { count: 0 }, undefined, "and 0 others..."], + ["handles plurals when count is 1", plurals, { count: 1 }, undefined, "and one other..."], + ["handles plurals when count is not 1", plurals, { count: 2 }, undefined, "and 2 others..."], + ["handles simple variable substitution", variableSub, { userId: "foo" }, undefined, "You are now ignoring foo"], [ - 'handles plurals when count is 0', - plurals, - { count: 0 }, - undefined, - 'and 0 others...', - ], - [ - 'handles plurals when count is 1', - plurals, - { count: 1 }, - undefined, - 'and one other...', - ], - [ - 'handles plurals when count is not 1', - plurals, - { count: 2 }, - undefined, - 'and 2 others...', - ], - [ - 'handles simple variable substitution', - variableSub, - { userId: 'foo' }, - undefined, - 'You are now ignoring foo', - ], - [ - 'handles simple tag substitution', + "handles simple tag substitution", selfClosingTagSub, {}, - { 'policyLink': () => 'foo' }, - 'Accept foo to continue:', + { policyLink: () => "foo" }, + "Accept foo to continue:", ], - ['handles text in tags', textInTagSub, {}, { 'a': (sub) => `x${sub}x` }, 'xUpgradex to your own domain'], + ["handles text in tags", textInTagSub, {}, { a: (sub) => `x${sub}x` }, "xUpgradex to your own domain"], [ - 'handles variable substitution with React function component', + "handles variable substitution with React function component", variableSub, { userId: () => foo }, undefined, // eslint-disable-next-line react/jsx-key - You are now ignoring foo, + + You are now ignoring foo + , ], [ - 'handles variable substitution with react node', + "handles variable substitution with react node", variableSub, { userId: foo }, undefined, // eslint-disable-next-line react/jsx-key - You are now ignoring foo, + + You are now ignoring foo + , ], [ - 'handles tag substitution with React function component', + "handles tag substitution with React function component", selfClosingTagSub, {}, - { 'policyLink': () => foo }, + { policyLink: () => foo }, // eslint-disable-next-line react/jsx-key - Accept foo to continue:, + + Accept foo to continue: + , ], ]; @@ -110,107 +92,108 @@ describe('languageHandler', function() { process.env.NODE_ENV = oldNodeEnv; }); - describe('when translations exist in language', () => { - beforeEach(function(done) { + describe("when translations exist in language", () => { + beforeEach(function (done) { stubClient(); - setLanguage('en').then(done); - setMissingEntryGenerator(key => key.split("|", 2)[1]); + setLanguage("en").then(done); + setMissingEntryGenerator((key) => key.split("|", 2)[1]); }); - it('translates a string to german', function(done) { - setLanguage('de').then(function() { - const translated = _t(basicString); - expect(translated).toBe('Räume'); - }).then(done); + it("translates a string to german", function (done) { + setLanguage("de") + .then(function () { + const translated = _t(basicString); + expect(translated).toBe("Räume"); + }) + .then(done); }); it.each(testCasesEn)("%s", (_d, translationString, variables, tags, result) => { expect(_t(translationString, variables, tags)).toEqual(result); }); - it('replacements in the wrong order', function() { - const text = '%(var1)s %(var2)s'; - expect(_t(text, { var2: 'val2', var1: 'val1' })).toBe('val1 val2'); + it("replacements in the wrong order", function () { + const text = "%(var1)s %(var2)s"; + expect(_t(text, { var2: "val2", var1: "val1" })).toBe("val1 val2"); }); - it('multiple replacements of the same variable', function() { - const text = '%(var1)s %(var1)s'; - expect(substitute(text, { var1: 'val1' })).toBe('val1 val1'); + it("multiple replacements of the same variable", function () { + const text = "%(var1)s %(var1)s"; + expect(substitute(text, { var1: "val1" })).toBe("val1 val1"); }); - it('multiple replacements of the same tag', function() { - const text = 'Click here to join the discussion! or here'; - expect(substitute(text, {}, { 'a': (sub) => `x${sub}x` })) - .toBe('xClick herex to join the discussion! xor herex'); + it("multiple replacements of the same tag", function () { + const text = "Click here to join the discussion! or here"; + expect(substitute(text, {}, { a: (sub) => `x${sub}x` })).toBe( + "xClick herex to join the discussion! xor herex", + ); }); }); - describe('for a non-en language', () => { + describe("for a non-en language", () => { beforeEach(() => { stubClient(); - setLanguage('lv'); + setLanguage("lv"); // counterpart doesnt expose any way to restore default config // missingEntryGenerator is mocked in the root setup file // reset to default here - const counterpartDefaultMissingEntryGen = - function(key) { return 'missing translation: ' + key; }; + const counterpartDefaultMissingEntryGen = function (key) { + return "missing translation: " + key; + }; setMissingEntryGenerator(counterpartDefaultMissingEntryGen); }); // mocked lv has only `"Uploading %(filename)s and %(count)s others|one"` - const lvExistingPlural = 'Uploading %(filename)s and %(count)s others'; - const lvNonExistingPlural = '%(spaceName)s and %(count)s others'; + const lvExistingPlural = "Uploading %(filename)s and %(count)s others"; + const lvNonExistingPlural = "%(spaceName)s and %(count)s others"; - describe('pluralization', () => { + describe("pluralization", () => { const pluralCases = [ [ - 'falls back when plural string exists but not for for count', + "falls back when plural string exists but not for for count", lvExistingPlural, - { count: 2, filename: 'test.txt' }, + { count: 2, filename: "test.txt" }, undefined, - 'Uploading test.txt and 2 others', + "Uploading test.txt and 2 others", ], [ - 'falls back when plural string does not exists at all', + "falls back when plural string does not exists at all", lvNonExistingPlural, - { count: 2, spaceName: 'test' }, + { count: 2, spaceName: "test" }, undefined, - 'test and 2 others', + "test and 2 others", ], ] as TestCase[]; - describe('_t', () => { - it('translated correctly when plural string exists for count', () => { - expect(_t( - lvExistingPlural, - { count: 1, filename: 'test.txt' }, undefined)).toEqual('Качване на test.txt и 1 друг'); + describe("_t", () => { + it("translated correctly when plural string exists for count", () => { + expect(_t(lvExistingPlural, { count: 1, filename: "test.txt" }, undefined)).toEqual( + "Качване на test.txt и 1 друг", + ); + }); + it.each(pluralCases)("%s", (_d, translationString, variables, tags, result) => { + expect(_t(translationString, variables, tags)).toEqual(result); }); - it.each(pluralCases)( - "%s", - (_d, translationString, variables, tags, result) => { - expect(_t(translationString, variables, tags)).toEqual(result); - }, - ); }); - describe('_tDom()', () => { - it('translated correctly when plural string exists for count', () => { - expect(_tDom( - lvExistingPlural, - { count: 1, filename: 'test.txt' }, undefined)).toEqual('Качване на test.txt и 1 друг'); + describe("_tDom()", () => { + it("translated correctly when plural string exists for count", () => { + expect(_tDom(lvExistingPlural, { count: 1, filename: "test.txt" }, undefined)).toEqual( + "Качване на test.txt и 1 друг", + ); }); it.each(pluralCases)( "%s and translates with fallback locale, attributes fallback locale", (_d, translationString, variables, tags, result) => { - expect(_tDom(translationString, variables, tags)).toEqual({ result }); + expect(_tDom(translationString, variables, tags)).toEqual({result}); }, ); }); }); - describe('when a translation string does not exist in active language', () => { - describe('_t', () => { + describe("when a translation string does not exist in active language", () => { + describe("_t", () => { it.each(testCasesEn)( "%s and translates with fallback locale", (_d, translationString, variables, tags, result) => { @@ -219,27 +202,28 @@ describe('languageHandler', function() { ); }); - describe('_tDom()', () => { + describe("_tDom()", () => { it.each(testCasesEn)( "%s and translates with fallback locale, attributes fallback locale", (_d, translationString, variables, tags, result) => { - expect(_tDom(translationString, variables, tags)).toEqual({ result }); + expect(_tDom(translationString, variables, tags)).toEqual({result}); }, ); }); }); }); - describe('when languages dont load', () => { - it('_t', () => { + describe("when languages dont load", () => { + it("_t", () => { const STRING_NOT_IN_THE_DICTIONARY = "a string that isn't in the translations dictionary"; expect(_t(STRING_NOT_IN_THE_DICTIONARY, {}, undefined)).toEqual(STRING_NOT_IN_THE_DICTIONARY); }); - it('_tDom', () => { + it("_tDom", () => { const STRING_NOT_IN_THE_DICTIONARY = "a string that isn't in the translations dictionary"; expect(_tDom(STRING_NOT_IN_THE_DICTIONARY, {}, undefined)).toEqual( - { STRING_NOT_IN_THE_DICTIONARY }); + {STRING_NOT_IN_THE_DICTIONARY}, + ); }); }); }); diff --git a/test/languageHandler-test.ts b/test/languageHandler-test.ts index 38a9db7be9..f0c3806422 100644 --- a/test/languageHandler-test.ts +++ b/test/languageHandler-test.ts @@ -23,20 +23,20 @@ import { setLanguage, } from "../src/languageHandler"; -describe('languageHandler', () => { +describe("languageHandler", () => { afterEach(() => { SdkConfig.unset(); CustomTranslationOptions.lookupFn = undefined; }); - it('should support overriding translations', async () => { + it("should support overriding translations", async () => { const str = "This is a test string that does not exist in the app."; const enOverride = "This is the English version of a custom string."; const deOverride = "This is the German version of a custom string."; const overrides: ICustomTranslations = { [str]: { - "en": enOverride, - "de": deOverride, + en: enOverride, + de: deOverride, }, }; diff --git a/test/linkify-matrix-test.ts b/test/linkify-matrix-test.ts index 8918854960..8fc9dffdb4 100644 --- a/test/linkify-matrix-test.ts +++ b/test/linkify-matrix-test.ts @@ -13,12 +13,12 @@ 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 { linkify, Type } from '../src/linkify-matrix'; +import { linkify, Type } from "../src/linkify-matrix"; -describe('linkify-matrix', () => { +describe("linkify-matrix", () => { const linkTypesByInitialCharacter = { - '#': 'roomalias', - '@': 'userid', + "#": "roomalias", + "@": "userid", }; /** @@ -26,297 +26,329 @@ describe('linkify-matrix', () => { * @param testName Due to all the tests using the same logic underneath, it makes to generate it in a bit smarter way * @param char */ - function genTests(char: '#' | '@' | '+') { + function genTests(char: "#" | "@" | "+") { const type = linkTypesByInitialCharacter[char]; - it('should not parse ' + char + 'foo without domain', () => { + it("should not parse " + char + "foo without domain", () => { const test = char + "foo"; const found = linkify.find(test); - expect(found).toEqual(([])); + expect(found).toEqual([]); }); - describe('ip v4 tests', () => { - it('should properly parse IPs v4 as the domain name', () => { - const test = char + 'potato:1.2.3.4'; + describe("ip v4 tests", () => { + it("should properly parse IPs v4 as the domain name", () => { + const test = char + "potato:1.2.3.4"; const found = linkify.find(test); - expect(found).toEqual(([{ - href: char + 'potato:1.2.3.4', - type, - isLink: true, - start: 0, - end: test.length, - value: char + 'potato:1.2.3.4', - }])); + expect(found).toEqual([ + { + href: char + "potato:1.2.3.4", + type, + isLink: true, + start: 0, + end: test.length, + value: char + "potato:1.2.3.4", + }, + ]); }); - it('should properly parse IPs v4 with port as the domain name with attached', () => { - const test = char + 'potato:1.2.3.4:1337'; + it("should properly parse IPs v4 with port as the domain name with attached", () => { + const test = char + "potato:1.2.3.4:1337"; const found = linkify.find(test); - expect(found).toEqual(([{ - href: char + 'potato:1.2.3.4:1337', - type, - isLink: true, - start: 0, - end: test.length, - value: char + 'potato:1.2.3.4:1337', - }])); + expect(found).toEqual([ + { + href: char + "potato:1.2.3.4:1337", + type, + isLink: true, + start: 0, + end: test.length, + value: char + "potato:1.2.3.4:1337", + }, + ]); }); - it('should properly parse IPs v4 as the domain name while ignoring missing port', () => { - const test = char + 'potato:1.2.3.4:'; + it("should properly parse IPs v4 as the domain name while ignoring missing port", () => { + const test = char + "potato:1.2.3.4:"; const found = linkify.find(test); - expect(found).toEqual(([{ - href: char + 'potato:1.2.3.4', - type, - isLink: true, - start: 0, - end: test.length - 1, - value: char + 'potato:1.2.3.4', - }])); + expect(found).toEqual([ + { + href: char + "potato:1.2.3.4", + type, + isLink: true, + start: 0, + end: test.length - 1, + value: char + "potato:1.2.3.4", + }, + ]); }); }); // Currently those tests are failing, as there's missing implementation. - describe.skip('ip v6 tests', () => { - it('should properly parse IPs v6 as the domain name', () => { + describe.skip("ip v6 tests", () => { + it("should properly parse IPs v6 as the domain name", () => { const test = char + "username:[1234:5678::abcd]"; const found = linkify.find(test); - expect(found).toEqual([{ - href: char + 'username:[1234:5678::abcd]', - type, - isLink: true, - start: 0, - end: test.length, - value: char + 'username:[1234:5678::abcd]', - }, + expect(found).toEqual([ + { + href: char + "username:[1234:5678::abcd]", + type, + isLink: true, + start: 0, + end: test.length, + value: char + "username:[1234:5678::abcd]", + }, ]); }); - it('should properly parse IPs v6 with port as the domain name', () => { + it("should properly parse IPs v6 with port as the domain name", () => { const test = char + "username:[1234:5678::abcd]:1337"; const found = linkify.find(test); - expect(found).toEqual([{ - href: char + 'username:[1234:5678::abcd]:1337', - type, - isLink: true, - start: 0, - end: test.length, - value: char + 'username:[1234:5678::abcd]:1337', - }, + expect(found).toEqual([ + { + href: char + "username:[1234:5678::abcd]:1337", + type, + isLink: true, + start: 0, + end: test.length, + value: char + "username:[1234:5678::abcd]:1337", + }, ]); }); // eslint-disable-next-line max-len - it('should properly parse IPs v6 while ignoring dangling comma when without port name as the domain name', () => { + it("should properly parse IPs v6 while ignoring dangling comma when without port name as the domain name", () => { const test = char + "username:[1234:5678::abcd]:"; const found = linkify.find(test); - expect(found).toEqual([{ - href: char + 'username:[1234:5678::abcd]:', - type, - isLink: true, - start: 0, - end: test.length - 1, - value: char + 'username:[1234:5678::abcd]:', - }, + expect(found).toEqual([ + { + href: char + "username:[1234:5678::abcd]:", + type, + isLink: true, + start: 0, + end: test.length - 1, + value: char + "username:[1234:5678::abcd]:", + }, ]); }); }); - it('properly parses ' + char + '_foonetic_xkcd:matrix.org', () => { - const test = '' + char + '_foonetic_xkcd:matrix.org'; + it("properly parses " + char + "_foonetic_xkcd:matrix.org", () => { + const test = "" + char + "_foonetic_xkcd:matrix.org"; const found = linkify.find(test); - expect(found).toEqual(([{ - href: char + "_foonetic_xkcd:matrix.org", - type, - value: char + "_foonetic_xkcd:matrix.org", - start: 0, - end: test.length, - isLink: true, - }])); + expect(found).toEqual([ + { + href: char + "_foonetic_xkcd:matrix.org", + type, + value: char + "_foonetic_xkcd:matrix.org", + start: 0, + end: test.length, + isLink: true, + }, + ]); }); - it('properly parses ' + char + 'foo:localhost', () => { + it("properly parses " + char + "foo:localhost", () => { const test = char + "foo:localhost"; const found = linkify.find(test); - expect(found).toEqual(([{ - href: char + "foo:localhost", - type, - value: char + "foo:localhost", - start: 0, - end: test.length, - isLink: true, - }])); + expect(found).toEqual([ + { + href: char + "foo:localhost", + type, + value: char + "foo:localhost", + start: 0, + end: test.length, + isLink: true, + }, + ]); }); - it('accept ' + char + 'foo:bar.com', () => { - const test = '' + char + 'foo:bar.com'; + it("accept " + char + "foo:bar.com", () => { + const test = "" + char + "foo:bar.com"; const found = linkify.find(test); - expect(found).toEqual(([{ - href: char + "foo:bar.com", - type, - value: char + "foo:bar.com", - start: 0, - end: test.length, + expect(found).toEqual([ + { + href: char + "foo:bar.com", + type, + value: char + "foo:bar.com", + start: 0, + end: test.length, - isLink: true, - }])); + isLink: true, + }, + ]); }); - it('accept ' + char + 'foo:com (mostly for (TLD|DOMAIN)+ mixing)', () => { - const test = '' + char + 'foo:com'; + it("accept " + char + "foo:com (mostly for (TLD|DOMAIN)+ mixing)", () => { + const test = "" + char + "foo:com"; const found = linkify.find(test); - expect(found).toEqual(([{ - href: char + "foo:com", - type, - value: char + "foo:com", - start: 0, - end: test.length, - isLink: true, - }])); + expect(found).toEqual([ + { + href: char + "foo:com", + type, + value: char + "foo:com", + start: 0, + end: test.length, + isLink: true, + }, + ]); }); - it('accept repeated TLDs (e.g .org.uk)', () => { - const test = '' + char + 'foo:bar.org.uk'; + it("accept repeated TLDs (e.g .org.uk)", () => { + const test = "" + char + "foo:bar.org.uk"; const found = linkify.find(test); - expect(found).toEqual(([{ - href: char + "foo:bar.org.uk", - type, - value: char + "foo:bar.org.uk", - start: 0, - end: test.length, - isLink: true, - }])); + expect(found).toEqual([ + { + href: char + "foo:bar.org.uk", + type, + value: char + "foo:bar.org.uk", + start: 0, + end: test.length, + isLink: true, + }, + ]); }); - it('accept hyphens in name ' + char + 'foo-bar:server.com', () => { - const test = '' + char + 'foo-bar:server.com'; + it("accept hyphens in name " + char + "foo-bar:server.com", () => { + const test = "" + char + "foo-bar:server.com"; const found = linkify.find(test); - expect(found).toEqual(([{ - href: char + "foo-bar:server.com", - type, - value: char + "foo-bar:server.com", - start: 0, - end: test.length, - isLink: true, - }])); + expect(found).toEqual([ + { + href: char + "foo-bar:server.com", + type, + value: char + "foo-bar:server.com", + start: 0, + end: test.length, + isLink: true, + }, + ]); }); - it('ignores trailing `:`', () => { - const test = '' + char + 'foo:bar.com:'; + it("ignores trailing `:`", () => { + const test = "" + char + "foo:bar.com:"; const found = linkify.find(test); - expect(found).toEqual(([{ - type, - value: char + "foo:bar.com", - href: char + 'foo:bar.com', - start: 0, - end: test.length - ":".length, + expect(found).toEqual([ + { + type, + value: char + "foo:bar.com", + href: char + "foo:bar.com", + start: 0, + end: test.length - ":".length, - isLink: true, - }])); + isLink: true, + }, + ]); }); - it('accept :NUM (port specifier)', () => { - const test = '' + char + 'foo:bar.com:2225'; + it("accept :NUM (port specifier)", () => { + const test = "" + char + "foo:bar.com:2225"; const found = linkify.find(test); - expect(found).toEqual(([{ - href: char + "foo:bar.com:2225", - type, - value: char + "foo:bar.com:2225", - start: 0, - end: test.length, - isLink: true, - }])); + expect(found).toEqual([ + { + href: char + "foo:bar.com:2225", + type, + value: char + "foo:bar.com:2225", + start: 0, + end: test.length, + isLink: true, + }, + ]); }); - it('ignores all the trailing :', () => { - const test = '' + char + 'foo:bar.com::::'; + it("ignores all the trailing :", () => { + const test = "" + char + "foo:bar.com::::"; const found = linkify.find(test); - expect(found).toEqual(([{ - href: char + "foo:bar.com", - type, - value: char + "foo:bar.com", - end: test.length - 4, - start: 0, - isLink: true, - }])); + expect(found).toEqual([ + { + href: char + "foo:bar.com", + type, + value: char + "foo:bar.com", + end: test.length - 4, + start: 0, + isLink: true, + }, + ]); }); - it('properly parses room alias with dots in name', () => { - const test = '' + char + 'foo.asdf:bar.com::::'; + it("properly parses room alias with dots in name", () => { + const test = "" + char + "foo.asdf:bar.com::::"; const found = linkify.find(test); - expect(found).toEqual(([{ - href: char + "foo.asdf:bar.com", - type, - value: char + "foo.asdf:bar.com", - start: 0, - end: test.length - ":".repeat(4).length, + expect(found).toEqual([ + { + href: char + "foo.asdf:bar.com", + type, + value: char + "foo.asdf:bar.com", + start: 0, + end: test.length - ":".repeat(4).length, - isLink: true, - }])); + isLink: true, + }, + ]); }); - it('does not parse room alias with too many separators', () => { - const test = '' + char + 'foo:::bar.com'; + it("does not parse room alias with too many separators", () => { + const test = "" + char + "foo:::bar.com"; const found = linkify.find(test); - expect(found).toEqual(([{ - href: "http://bar.com", - type: "url", - value: "bar.com", - isLink: true, - start: 7, - end: test.length, - }])); + expect(found).toEqual([ + { + href: "http://bar.com", + type: "url", + value: "bar.com", + isLink: true, + start: 7, + end: test.length, + }, + ]); }); - it('does not parse multiple room aliases in one string', () => { - const test = '' + char + 'foo:bar.com-baz.com'; + it("does not parse multiple room aliases in one string", () => { + const test = "" + char + "foo:bar.com-baz.com"; const found = linkify.find(test); - expect(found).toEqual(([{ - href: char + "foo:bar.com-baz.com", - type, - value: char + "foo:bar.com-baz.com", - end: 20, - start: 0, - isLink: true, - }])); + expect(found).toEqual([ + { + href: char + "foo:bar.com-baz.com", + type, + value: char + "foo:bar.com-baz.com", + end: 20, + start: 0, + isLink: true, + }, + ]); }); } - describe('roomalias plugin', () => { - genTests('#'); + describe("roomalias plugin", () => { + genTests("#"); }); - describe('userid plugin', () => { - genTests('@'); + describe("userid plugin", () => { + genTests("@"); }); - describe('matrix uri', () => { + describe("matrix uri", () => { const acceptedMatrixUris = [ - 'matrix:u/foo_bar:server.uk', - 'matrix:r/foo-bar:server.uk', - 'matrix:roomid/somewhere:example.org?via=elsewhere.ca', - 'matrix:r/somewhere:example.org', - 'matrix:r/somewhere:example.org/e/event', - 'matrix:roomid/somewhere:example.org/e/event?via=elsewhere.ca', - 'matrix:u/alice:example.org?action=chat', + "matrix:u/foo_bar:server.uk", + "matrix:r/foo-bar:server.uk", + "matrix:roomid/somewhere:example.org?via=elsewhere.ca", + "matrix:r/somewhere:example.org", + "matrix:r/somewhere:example.org/e/event", + "matrix:roomid/somewhere:example.org/e/event?via=elsewhere.ca", + "matrix:u/alice:example.org?action=chat", ]; for (const matrixUri of acceptedMatrixUris) { - it('accepts ' + matrixUri, () => { + it("accepts " + matrixUri, () => { const test = matrixUri; const found = linkify.find(test); - expect(found).toEqual(([{ - href: matrixUri, - type: Type.URL, - value: matrixUri, - end: matrixUri.length, - start: 0, - isLink: true, - }])); + expect(found).toEqual([ + { + href: matrixUri, + type: Type.URL, + value: matrixUri, + end: matrixUri.length, + start: 0, + isLink: true, + }, + ]); }); } }); describe("matrix-prefixed domains", () => { - const acceptedDomains = [ - 'matrix.org', - 'matrix.to', - 'matrix-help.org', - 'matrix123.org', - ]; + const acceptedDomains = ["matrix.org", "matrix.to", "matrix-help.org", "matrix123.org"]; for (const domain of acceptedDomains) { - it('accepts ' + domain, () => { + it("accepts " + domain, () => { const test = domain; const found = linkify.find(test); - expect(found).toEqual(([{ - href: `http://${domain}`, - type: Type.URL, - value: domain, - end: domain.length, - start: 0, - isLink: true, - }])); + expect(found).toEqual([ + { + href: `http://${domain}`, + type: Type.URL, + value: domain, + end: domain.length, + start: 0, + isLink: true, + }, + ]); }); } }); diff --git a/test/models/Call-test.ts b/test/models/Call-test.ts index 785b9eea58..dd8f02a014 100644 --- a/test/models/Call-test.ts +++ b/test/models/Call-test.ts @@ -43,10 +43,10 @@ import PlatformPeg from "../../src/PlatformPeg"; jest.spyOn(MediaDeviceHandler, "getDevices").mockResolvedValue({ [MediaDeviceKindEnum.AudioInput]: [ - { deviceId: "1", groupId: "1", kind: "audioinput", label: "Headphones", toJSON: () => { } }, + { deviceId: "1", groupId: "1", kind: "audioinput", label: "Headphones", toJSON: () => {} }, ], [MediaDeviceKindEnum.VideoInput]: [ - { deviceId: "2", groupId: "2", kind: "videoinput", label: "Built-in webcam", toJSON: () => { } }, + { deviceId: "2", groupId: "2", kind: "videoinput", label: "Built-in webcam", toJSON: () => {} }, ], [MediaDeviceKindEnum.AudioOutput]: [], }); @@ -55,7 +55,7 @@ jest.spyOn(MediaDeviceHandler, "getVideoInput").mockReturnValue("2"); const enabledSettings = new Set(["feature_group_calls", "feature_video_rooms", "feature_element_call_video_rooms"]); jest.spyOn(SettingsStore, "getValue").mockImplementation( - settingName => enabledSettings.has(settingName) || undefined, + (settingName) => enabledSettings.has(settingName) || undefined, ); const setUpClientRoomAndStores = (): { @@ -75,17 +75,21 @@ const setUpClientRoomAndStores = (): { const alice = mkRoomMember(room.roomId, "@alice:example.org"); const bob = mkRoomMember(room.roomId, "@bob:example.org"); const carol = mkRoomMember(room.roomId, "@carol:example.org"); - jest.spyOn(room, "getMember").mockImplementation(userId => { + jest.spyOn(room, "getMember").mockImplementation((userId) => { switch (userId) { - case alice.userId: return alice; - case bob.userId: return bob; - case carol.userId: return carol; - default: return null; + case alice.userId: + return alice; + case bob.userId: + return bob; + case carol.userId: + return carol; + default: + return null; } }); jest.spyOn(room, "getMyMembership").mockReturnValue("join"); - client.getRoom.mockImplementation(roomId => roomId === room.roomId ? room : null); + client.getRoom.mockImplementation((roomId) => (roomId === room.roomId ? room : null)); client.getRooms.mockReturnValue([room]); client.getUserId.mockReturnValue(alice.userId); client.getDeviceId.mockReturnValue("alices_device"); @@ -110,14 +114,13 @@ const setUpClientRoomAndStores = (): { return { client, room, alice, bob, carol }; }; -const cleanUpClientRoomAndStores = ( - client: MatrixClient, - room: Room, -) => { +const cleanUpClientRoomAndStores = (client: MatrixClient, room: Room) => { client.reEmitter.stopReEmitting(room, [RoomStateEvent.Events]); }; -const setUpWidget = (call: Call): { +const setUpWidget = ( + call: Call, +): { widget: Widget; messaging: Mocked; audioMutedSpy: jest.SpyInstance; @@ -361,10 +364,12 @@ describe("JitsiCall", () => { // Now, stub out client.sendStateEvent so we can test our local echo client.sendStateEvent.mockReset(); await call.connect(); - expect(call.participants).toEqual(new Map([ - [alice, new Set(["alices_device"])], - [bob, new Set(["bobweb", "bobdesktop"])], - ])); + expect(call.participants).toEqual( + new Map([ + [alice, new Set(["alices_device"])], + [bob, new Set(["bobweb", "bobdesktop"])], + ]), + ); await call.disconnect(); expect(call.participants).toEqual(new Map([[bob, new Set(["bobweb", "bobdesktop"])]])); @@ -373,40 +378,56 @@ describe("JitsiCall", () => { it("updates room state when connecting and disconnecting", async () => { const now1 = Date.now(); await call.connect(); - await waitFor(() => expect( - room.currentState.getStateEvents(JitsiCall.MEMBER_EVENT_TYPE, alice.userId).getContent(), - ).toEqual({ - devices: [client.getDeviceId()], - expires_ts: now1 + call.STUCK_DEVICE_TIMEOUT_MS, - }), { interval: 5 }); + await waitFor( + () => + expect( + room.currentState.getStateEvents(JitsiCall.MEMBER_EVENT_TYPE, alice.userId).getContent(), + ).toEqual({ + devices: [client.getDeviceId()], + expires_ts: now1 + call.STUCK_DEVICE_TIMEOUT_MS, + }), + { interval: 5 }, + ); const now2 = Date.now(); await call.disconnect(); - await waitFor(() => expect( - room.currentState.getStateEvents(JitsiCall.MEMBER_EVENT_TYPE, alice.userId).getContent(), - ).toEqual({ - devices: [], - expires_ts: now2 + call.STUCK_DEVICE_TIMEOUT_MS, - }), { interval: 5 }); + await waitFor( + () => + expect( + room.currentState.getStateEvents(JitsiCall.MEMBER_EVENT_TYPE, alice.userId).getContent(), + ).toEqual({ + devices: [], + expires_ts: now2 + call.STUCK_DEVICE_TIMEOUT_MS, + }), + { interval: 5 }, + ); }); it("repeatedly updates room state while connected", async () => { await call.connect(); - await waitFor(() => expect(client.sendStateEvent).toHaveBeenLastCalledWith( - room.roomId, - JitsiCall.MEMBER_EVENT_TYPE, - { devices: [client.getDeviceId()], expires_ts: expect.any(Number) }, - alice.userId, - ), { interval: 5 }); + await waitFor( + () => + expect(client.sendStateEvent).toHaveBeenLastCalledWith( + room.roomId, + JitsiCall.MEMBER_EVENT_TYPE, + { devices: [client.getDeviceId()], expires_ts: expect.any(Number) }, + alice.userId, + ), + { interval: 5 }, + ); client.sendStateEvent.mockClear(); jest.advanceTimersByTime(call.STUCK_DEVICE_TIMEOUT_MS); - await waitFor(() => expect(client.sendStateEvent).toHaveBeenLastCalledWith( - room.roomId, - JitsiCall.MEMBER_EVENT_TYPE, - { devices: [client.getDeviceId()], expires_ts: expect.any(Number) }, - alice.userId, - ), { interval: 5 }); + await waitFor( + () => + expect(client.sendStateEvent).toHaveBeenLastCalledWith( + room.roomId, + JitsiCall.MEMBER_EVENT_TYPE, + { devices: [client.getDeviceId()], expires_ts: expect.any(Number) }, + alice.userId, + ), + { interval: 5 }, + ); }); it("emits events when connection state changes", async () => { @@ -468,24 +489,20 @@ describe("JitsiCall", () => { const mkContent = (devices: IMyDevice[]): JitsiCallMemberContent => ({ expires_ts: 1000 * 60 * 10, - devices: devices.map(d => d.device_id), - }); - const expectDevices = (devices: IMyDevice[]) => expect( - room.currentState.getStateEvents(JitsiCall.MEMBER_EVENT_TYPE, alice.userId).getContent(), - ).toEqual({ - expires_ts: expect.any(Number), - devices: devices.map(d => d.device_id), + devices: devices.map((d) => d.device_id), }); + const expectDevices = (devices: IMyDevice[]) => + expect( + room.currentState.getStateEvents(JitsiCall.MEMBER_EVENT_TYPE, alice.userId).getContent(), + ).toEqual({ + expires_ts: expect.any(Number), + devices: devices.map((d) => d.device_id), + }); beforeEach(() => { client.getDeviceId.mockReturnValue(aliceWeb.device_id); client.getDevices.mockResolvedValue({ - devices: [ - aliceWeb, - aliceDesktop, - aliceDesktopOffline, - aliceDesktopNeverOnline, - ], + devices: [aliceWeb, aliceDesktop, aliceDesktopOffline, aliceDesktopNeverOnline], }); }); @@ -700,13 +717,15 @@ describe("ElementCall", () => { room.roomId, ElementCall.MEMBER_EVENT_TYPE.name, { - "m.calls": [{ - "m.call_id": call.groupCall.groupCallId, - "m.devices": [ - { device_id: "bobweb", session_id: "1", feeds: [], expires_ts: 1000 * 60 * 10 }, - { device_id: "bobdesktop", session_id: "1", feeds: [], expires_ts: 1000 * 60 * 10 }, - ], - }], + "m.calls": [ + { + "m.call_id": call.groupCall.groupCallId, + "m.devices": [ + { device_id: "bobweb", session_id: "1", feeds: [], expires_ts: 1000 * 60 * 10 }, + { device_id: "bobdesktop", session_id: "1", feeds: [], expires_ts: 1000 * 60 * 10 }, + ], + }, + ], }, bob.userId, ); @@ -715,12 +734,14 @@ describe("ElementCall", () => { room.roomId, ElementCall.MEMBER_EVENT_TYPE.name, { - "m.calls": [{ - "m.call_id": call.groupCall.groupCallId, - "m.devices": [ - { device_id: "carolandroid", session_id: "1", feeds: [], expires_ts: -1000 * 60 }, - ], - }], + "m.calls": [ + { + "m.call_id": call.groupCall.groupCallId, + "m.devices": [ + { device_id: "carolandroid", session_id: "1", feeds: [], expires_ts: -1000 * 60 }, + ], + }, + ], }, carol.userId, ); @@ -728,10 +749,12 @@ describe("ElementCall", () => { // Now, stub out client.sendStateEvent so we can test our local echo client.sendStateEvent.mockReset(); await call.connect(); - expect(call.participants).toEqual(new Map([ - [alice, new Set(["alices_device"])], - [bob, new Set(["bobweb", "bobdesktop"])], - ])); + expect(call.participants).toEqual( + new Map([ + [alice, new Set(["alices_device"])], + [bob, new Set(["bobweb", "bobdesktop"])], + ]), + ); await call.disconnect(); expect(call.participants).toEqual(new Map([[bob, new Set(["bobweb", "bobdesktop"])]])); @@ -815,9 +838,9 @@ describe("ElementCall", () => { describe("screensharing", () => { it("passes source id if we can get it", async () => { const sourceId = "source_id"; - jest.spyOn(Modal, "createDialog").mockReturnValue( - { finished: new Promise((r) => r([sourceId])) } as IHandle, - ); + jest.spyOn(Modal, "createDialog").mockReturnValue({ + finished: new Promise((r) => r([sourceId])), + } as IHandle); jest.spyOn(PlatformPeg.get(), "supportsDesktopCapturer").mockReturnValue(true); await call.connect(); @@ -836,15 +859,16 @@ describe("ElementCall", () => { await waitFor(() => { expect(messaging!.transport.send).toHaveBeenCalledWith( - "io.element.screenshare_start", expect.objectContaining({ desktopCapturerSourceId: sourceId }), + "io.element.screenshare_start", + expect.objectContaining({ desktopCapturerSourceId: sourceId }), ); }); }); it("sends ScreenshareStop if we couldn't get a source id", async () => { - jest.spyOn(Modal, "createDialog").mockReturnValue( - { finished: new Promise((r) => r([null])) } as IHandle, - ); + jest.spyOn(Modal, "createDialog").mockReturnValue({ + finished: new Promise((r) => r([null])), + } as IHandle); jest.spyOn(PlatformPeg.get(), "supportsDesktopCapturer").mockReturnValue(true); await call.connect(); @@ -863,7 +887,8 @@ describe("ElementCall", () => { await waitFor(() => { expect(messaging!.transport.send).toHaveBeenCalledWith( - "io.element.screenshare_stop", expect.objectContaining({ }), + "io.element.screenshare_stop", + expect.objectContaining({}), ); }); }); @@ -902,12 +927,14 @@ describe("ElementCall", () => { room.roomId, ElementCall.MEMBER_EVENT_TYPE.name, { - "m.calls": [{ - "m.call_id": call.groupCall.groupCallId, - "m.devices": [ - { device_id: "bobweb", session_id: "1", feeds: [], expires_ts: 1000 * 60 * 10 }, - ], - }], + "m.calls": [ + { + "m.call_id": call.groupCall.groupCallId, + "m.devices": [ + { device_id: "bobweb", session_id: "1", feeds: [], expires_ts: 1000 * 60 * 10 }, + ], + }, + ], }, bob.userId, ); @@ -920,10 +947,12 @@ describe("ElementCall", () => { room.roomId, ElementCall.MEMBER_EVENT_TYPE.name, { - "m.calls": [{ - "m.call_id": call.groupCall.groupCallId, - "m.devices": [], - }], + "m.calls": [ + { + "m.call_id": call.groupCall.groupCallId, + "m.devices": [], + }, + ], }, bob.userId, ); diff --git a/test/modules/MockModule.ts b/test/modules/MockModule.ts index 6496437989..b4b905fcbf 100644 --- a/test/modules/MockModule.ts +++ b/test/modules/MockModule.ts @@ -31,7 +31,7 @@ export class MockModule extends RuntimeModule { export function registerMockModule(): MockModule { let module: MockModule; - ModuleRunner.instance.registerModule(api => { + ModuleRunner.instance.registerModule((api) => { if (module) { throw new Error("State machine error: ModuleRunner created the module twice"); } diff --git a/test/modules/ModuleRunner-test.ts b/test/modules/ModuleRunner-test.ts index 400d970519..175c62c9e6 100644 --- a/test/modules/ModuleRunner-test.ts +++ b/test/modules/ModuleRunner-test.ts @@ -31,15 +31,13 @@ describe("ModuleRunner", () => { const module1 = registerMockModule(); const module2 = registerMockModule(); - const wrapEmit = (module: MockModule) => new Promise((resolve) => { - module.on(RoomViewLifecycle.PreviewRoomNotLoggedIn, (val1, val2) => { - resolve([val1, val2]); + const wrapEmit = (module: MockModule) => + new Promise((resolve) => { + module.on(RoomViewLifecycle.PreviewRoomNotLoggedIn, (val1, val2) => { + resolve([val1, val2]); + }); }); - }); - const promises = Promise.all([ - wrapEmit(module1), - wrapEmit(module2), - ]); + const promises = Promise.all([wrapEmit(module1), wrapEmit(module2)]); const roomId = "!room:example.org"; const opts: RoomPreviewOpts = { canJoin: false }; diff --git a/test/modules/ProxiedModuleApi-test.ts b/test/modules/ProxiedModuleApi-test.ts index 80890acfb1..4a29e37453 100644 --- a/test/modules/ProxiedModuleApi-test.ts +++ b/test/modules/ProxiedModuleApi-test.ts @@ -36,8 +36,8 @@ describe("ProxiedApiModule", () => { const translations: TranslationStringsObject = { ["custom string"]: { - "en": "custom string", - "fr": "custom french string", + en: "custom string", + fr: "custom french string", }, }; api.registerTranslations(translations); @@ -60,12 +60,12 @@ describe("ProxiedApiModule", () => { expect(module.apiInstance).toBeInstanceOf(ProxiedModuleApi); module.apiInstance.registerTranslations({ [en]: { - "en": en, - "de": de, + en: en, + de: de, }, [enVars]: { - "en": enVars, - "de": deVars, + en: enVars, + de: deVars, }, }); await setLanguage("de"); // calls `registerCustomTranslations()` for us diff --git a/test/notifications/ContentRules-test.ts b/test/notifications/ContentRules-test.ts index 9881a1c149..3c7d913771 100644 --- a/test/notifications/ContentRules-test.ts +++ b/test/notifications/ContentRules-test.ts @@ -20,10 +20,7 @@ import { TweakName, PushRuleActionName, TweakHighlight, TweakSound } from "matri import { ContentRules, PushRuleVectorState } from "../../src/notifications"; const NORMAL_RULE = { - actions: [ - PushRuleActionName.Notify, - { set_tweak: TweakName.Highlight, value: false } as TweakHighlight, - ], + actions: [PushRuleActionName.Notify, { set_tweak: TweakName.Highlight, value: false } as TweakHighlight], default: false, enabled: true, pattern: "vdh2", @@ -54,23 +51,18 @@ const USERNAME_RULE = { rule_id: ".m.rule.contains_user_name", }; -describe("ContentRules", function() { - describe("parseContentRules", function() { - it("should handle there being no keyword rules", function() { - const rules = { 'global': { 'content': [ - USERNAME_RULE, - ] } }; +describe("ContentRules", function () { + describe("parseContentRules", function () { + it("should handle there being no keyword rules", function () { + const rules = { global: { content: [USERNAME_RULE] } }; const parsed = ContentRules.parseContentRules(rules); expect(parsed.rules).toEqual([]); expect(parsed.vectorState).toEqual(PushRuleVectorState.ON); expect(parsed.externalRules).toEqual([]); }); - it("should parse regular keyword notifications", function() { - const rules = { 'global': { 'content': [ - NORMAL_RULE, - USERNAME_RULE, - ] } }; + it("should parse regular keyword notifications", function () { + const rules = { global: { content: [NORMAL_RULE, USERNAME_RULE] } }; const parsed = ContentRules.parseContentRules(rules); expect(parsed.rules.length).toEqual(1); @@ -79,11 +71,8 @@ describe("ContentRules", function() { expect(parsed.externalRules).toEqual([]); }); - it("should parse loud keyword notifications", function() { - const rules = { 'global': { 'content': [ - LOUD_RULE, - USERNAME_RULE, - ] } }; + it("should parse loud keyword notifications", function () { + const rules = { global: { content: [LOUD_RULE, USERNAME_RULE] } }; const parsed = ContentRules.parseContentRules(rules); expect(parsed.rules.length).toEqual(1); @@ -92,12 +81,8 @@ describe("ContentRules", function() { expect(parsed.externalRules).toEqual([]); }); - it("should parse mixed keyword notifications", function() { - const rules = { 'global': { 'content': [ - LOUD_RULE, - NORMAL_RULE, - USERNAME_RULE, - ] } }; + it("should parse mixed keyword notifications", function () { + const rules = { global: { content: [LOUD_RULE, NORMAL_RULE, USERNAME_RULE] } }; const parsed = ContentRules.parseContentRules(rules); expect(parsed.rules.length).toEqual(1); diff --git a/test/notifications/PushRuleVectorState-test.ts b/test/notifications/PushRuleVectorState-test.ts index 031944b84c..f6c04729f8 100644 --- a/test/notifications/PushRuleVectorState-test.ts +++ b/test/notifications/PushRuleVectorState-test.ts @@ -15,32 +15,24 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { - PushRuleActionName, - TweakHighlight, - TweakName, - TweakSound, -} from "matrix-js-sdk/src/matrix"; +import { PushRuleActionName, TweakHighlight, TweakName, TweakSound } from "matrix-js-sdk/src/matrix"; import { PushRuleVectorState } from "../../src/notifications"; -describe("PushRuleVectorState", function() { - describe("contentRuleVectorStateKind", function() { - it("should understand normal notifications", function() { +describe("PushRuleVectorState", function () { + describe("contentRuleVectorStateKind", function () { + it("should understand normal notifications", function () { const rule = { - actions: [ - PushRuleActionName.Notify, - ], + actions: [PushRuleActionName.Notify], default: false, enabled: false, - rule_id: '1', + rule_id: "1", }; - expect(PushRuleVectorState.contentRuleVectorStateKind(rule)). - toEqual(PushRuleVectorState.ON); + expect(PushRuleVectorState.contentRuleVectorStateKind(rule)).toEqual(PushRuleVectorState.ON); }); - it("should handle loud notifications", function() { + it("should handle loud notifications", function () { const rule = { actions: [ PushRuleActionName.Notify, @@ -49,14 +41,13 @@ describe("PushRuleVectorState", function() { ], default: false, enabled: false, - rule_id: '1', + rule_id: "1", }; - expect(PushRuleVectorState.contentRuleVectorStateKind(rule)). - toEqual(PushRuleVectorState.LOUD); + expect(PushRuleVectorState.contentRuleVectorStateKind(rule)).toEqual(PushRuleVectorState.LOUD); }); - it("should understand missing highlight.value", function() { + it("should understand missing highlight.value", function () { const rule = { actions: [ PushRuleActionName.Notify, @@ -65,11 +56,10 @@ describe("PushRuleVectorState", function() { ], default: false, enabled: false, - rule_id: '1', + rule_id: "1", }; - expect(PushRuleVectorState.contentRuleVectorStateKind(rule)). - toEqual(PushRuleVectorState.LOUD); + expect(PushRuleVectorState.contentRuleVectorStateKind(rule)).toEqual(PushRuleVectorState.LOUD); }); }); }); diff --git a/test/settings/SettingsStore-test.ts b/test/settings/SettingsStore-test.ts index e35f2a25b1..0f89fc9bba 100644 --- a/test/settings/SettingsStore-test.ts +++ b/test/settings/SettingsStore-test.ts @@ -44,13 +44,13 @@ describe("SettingsStore", () => { }), } as unknown as BasePlatform); - TEST_DATA.forEach(d => { + TEST_DATA.forEach((d) => { SettingsStore.setValue(d.name, null, d.level, d.value); }); }); describe("getValueAt", () => { - TEST_DATA.forEach(d => { + TEST_DATA.forEach((d) => { it(`should return the value "${d.level}"."${d.name}"`, () => { expect(SettingsStore.getValueAt(d.level, d.name)).toBe(d.value); // regression test #22545 diff --git a/test/settings/controllers/FontSizeController-test.ts b/test/settings/controllers/FontSizeController-test.ts index d42ec487ed..5d99d5b4db 100644 --- a/test/settings/controllers/FontSizeController-test.ts +++ b/test/settings/controllers/FontSizeController-test.ts @@ -19,13 +19,13 @@ import dis from "../../../src/dispatcher/dispatcher"; import FontSizeController from "../../../src/settings/controllers/FontSizeController"; import { SettingLevel } from "../../../src/settings/SettingLevel"; -const dispatchSpy = jest.spyOn(dis, 'dispatch'); +const dispatchSpy = jest.spyOn(dis, "dispatch"); -describe('FontSizeController', () => { - it('dispatches a font size action on change', () => { +describe("FontSizeController", () => { + it("dispatches a font size action on change", () => { const controller = new FontSizeController(); - controller.onChange(SettingLevel.ACCOUNT, '$room:server', 12); + controller.onChange(SettingLevel.ACCOUNT, "$room:server", 12); expect(dispatchSpy).toHaveBeenCalledWith({ action: Action.UpdateFontSize, diff --git a/test/settings/controllers/IncompatibleController-test.ts b/test/settings/controllers/IncompatibleController-test.ts index 8003f4e7a3..72b0c2788b 100644 --- a/test/settings/controllers/IncompatibleController-test.ts +++ b/test/settings/controllers/IncompatibleController-test.ts @@ -18,15 +18,15 @@ import IncompatibleController from "../../../src/settings/controllers/Incompatib import { SettingLevel } from "../../../src/settings/SettingLevel"; import SettingsStore from "../../../src/settings/SettingsStore"; -describe('IncompatibleController', () => { - const settingsGetValueSpy = jest.spyOn(SettingsStore, 'getValue'); +describe("IncompatibleController", () => { + const settingsGetValueSpy = jest.spyOn(SettingsStore, "getValue"); beforeEach(() => { settingsGetValueSpy.mockClear(); }); - describe('incompatibleSetting', () => { - describe('when incompatibleValue is not set', () => { - it('returns true when setting value is true', () => { + describe("incompatibleSetting", () => { + describe("when incompatibleValue is not set", () => { + it("returns true when setting value is true", () => { // no incompatible value set, defaulted to true const controller = new IncompatibleController("feature_spotlight", { key: null }); settingsGetValueSpy.mockReturnValue(true); @@ -36,56 +36,56 @@ describe('IncompatibleController', () => { expect(settingsGetValueSpy).toHaveBeenCalledWith("feature_spotlight"); }); - it('returns false when setting value is not true', () => { + it("returns false when setting value is not true", () => { // no incompatible value set, defaulted to true const controller = new IncompatibleController("feature_spotlight", { key: null }); - settingsGetValueSpy.mockReturnValue('test'); + settingsGetValueSpy.mockReturnValue("test"); expect(controller.incompatibleSetting).toBe(false); }); }); - describe('when incompatibleValue is set to a value', () => { - it('returns true when setting value matches incompatible value', () => { - const controller = new IncompatibleController("feature_spotlight", { key: null }, 'test'); - settingsGetValueSpy.mockReturnValue('test'); + describe("when incompatibleValue is set to a value", () => { + it("returns true when setting value matches incompatible value", () => { + const controller = new IncompatibleController("feature_spotlight", { key: null }, "test"); + settingsGetValueSpy.mockReturnValue("test"); expect(controller.incompatibleSetting).toBe(true); }); - it('returns false when setting value is not true', () => { - const controller = new IncompatibleController("feature_spotlight", { key: null }, 'test'); - settingsGetValueSpy.mockReturnValue('not test'); + it("returns false when setting value is not true", () => { + const controller = new IncompatibleController("feature_spotlight", { key: null }, "test"); + settingsGetValueSpy.mockReturnValue("not test"); expect(controller.incompatibleSetting).toBe(false); }); }); - describe('when incompatibleValue is set to a function', () => { - it('returns result from incompatibleValue function', () => { + describe("when incompatibleValue is set to a function", () => { + it("returns result from incompatibleValue function", () => { const incompatibleValueFn = jest.fn().mockReturnValue(false); const controller = new IncompatibleController("feature_spotlight", { key: null }, incompatibleValueFn); - settingsGetValueSpy.mockReturnValue('test'); + settingsGetValueSpy.mockReturnValue("test"); expect(controller.incompatibleSetting).toBe(false); - expect(incompatibleValueFn).toHaveBeenCalledWith('test'); + expect(incompatibleValueFn).toHaveBeenCalledWith("test"); }); }); }); - describe('getValueOverride()', () => { - it('returns forced value when setting is incompatible', () => { + describe("getValueOverride()", () => { + it("returns forced value when setting is incompatible", () => { settingsGetValueSpy.mockReturnValue(true); const forcedValue = { key: null }; const controller = new IncompatibleController("feature_spotlight", forcedValue); - expect(controller.getValueOverride( - SettingLevel.ACCOUNT, '$room:server', true, SettingLevel.ACCOUNT, - )).toEqual(forcedValue); + expect( + controller.getValueOverride(SettingLevel.ACCOUNT, "$room:server", true, SettingLevel.ACCOUNT), + ).toEqual(forcedValue); }); - it('returns null when setting is not incompatible', () => { + it("returns null when setting is not incompatible", () => { settingsGetValueSpy.mockReturnValue(false); const forcedValue = { key: null }; const controller = new IncompatibleController("feature_spotlight", forcedValue); - expect(controller.getValueOverride( - SettingLevel.ACCOUNT, '$room:server', true, SettingLevel.ACCOUNT, - )).toEqual(null); + expect( + controller.getValueOverride(SettingLevel.ACCOUNT, "$room:server", true, SettingLevel.ACCOUNT), + ).toEqual(null); }); }); }); diff --git a/test/settings/controllers/SystemFontController-test.ts b/test/settings/controllers/SystemFontController-test.ts index a330661231..c827557ca4 100644 --- a/test/settings/controllers/SystemFontController-test.ts +++ b/test/settings/controllers/SystemFontController-test.ts @@ -20,14 +20,14 @@ import SystemFontController from "../../../src/settings/controllers/SystemFontCo import { SettingLevel } from "../../../src/settings/SettingLevel"; import SettingsStore from "../../../src/settings/SettingsStore"; -const dispatchSpy = jest.spyOn(dis, 'dispatch'); +const dispatchSpy = jest.spyOn(dis, "dispatch"); -describe('SystemFontController', () => { - it('dispatches a font size action on change', () => { - const getValueSpy = jest.spyOn(SettingsStore, 'getValue').mockReturnValue(true); +describe("SystemFontController", () => { + it("dispatches a font size action on change", () => { + const getValueSpy = jest.spyOn(SettingsStore, "getValue").mockReturnValue(true); const controller = new SystemFontController(); - controller.onChange(SettingLevel.ACCOUNT, '$room:server', 12); + controller.onChange(SettingLevel.ACCOUNT, "$room:server", 12); expect(dispatchSpy).toHaveBeenCalledWith({ action: Action.UpdateSystemFont, diff --git a/test/settings/controllers/ThemeController-test.ts b/test/settings/controllers/ThemeController-test.ts index a90531b6b8..ca0a499047 100644 --- a/test/settings/controllers/ThemeController-test.ts +++ b/test/settings/controllers/ThemeController-test.ts @@ -19,57 +19,50 @@ import { SettingLevel } from "../../../src/settings/SettingLevel"; import SettingsStore from "../../../src/settings/SettingsStore"; import { DEFAULT_THEME } from "../../../src/theme"; -describe('ThemeController', () => { - jest.spyOn(SettingsStore, 'getValue').mockReturnValue([]); +describe("ThemeController", () => { + jest.spyOn(SettingsStore, "getValue").mockReturnValue([]); afterEach(() => { // reset ThemeController.isLogin = false; }); - it('returns null when calculatedValue is falsy', () => { + it("returns null when calculatedValue is falsy", () => { const controller = new ThemeController(); - expect(controller.getValueOverride( - SettingLevel.ACCOUNT, - '$room:server', - undefined, /* calculatedValue */ - SettingLevel.ACCOUNT, - )).toEqual(null); + expect( + controller.getValueOverride( + SettingLevel.ACCOUNT, + "$room:server", + undefined /* calculatedValue */, + SettingLevel.ACCOUNT, + ), + ).toEqual(null); }); - it('returns light when login flag is set', () => { + it("returns light when login flag is set", () => { const controller = new ThemeController(); ThemeController.isLogin = true; - expect(controller.getValueOverride( - SettingLevel.ACCOUNT, - '$room:server', - 'dark', - SettingLevel.ACCOUNT, - )).toEqual('light'); + expect(controller.getValueOverride(SettingLevel.ACCOUNT, "$room:server", "dark", SettingLevel.ACCOUNT)).toEqual( + "light", + ); }); - it('returns default theme when value is not a valid theme', () => { + it("returns default theme when value is not a valid theme", () => { const controller = new ThemeController(); - expect(controller.getValueOverride( - SettingLevel.ACCOUNT, - '$room:server', - 'my-test-theme', - SettingLevel.ACCOUNT, - )).toEqual(DEFAULT_THEME); + expect( + controller.getValueOverride(SettingLevel.ACCOUNT, "$room:server", "my-test-theme", SettingLevel.ACCOUNT), + ).toEqual(DEFAULT_THEME); }); - it('returns null when value is a valid theme', () => { + it("returns null when value is a valid theme", () => { const controller = new ThemeController(); - expect(controller.getValueOverride( - SettingLevel.ACCOUNT, - '$room:server', - 'dark', - SettingLevel.ACCOUNT, - )).toEqual(null); + expect(controller.getValueOverride(SettingLevel.ACCOUNT, "$room:server", "dark", SettingLevel.ACCOUNT)).toEqual( + null, + ); }); }); diff --git a/test/settings/controllers/UseSystemFontController-test.ts b/test/settings/controllers/UseSystemFontController-test.ts index 725bd1a442..5b5003dce6 100644 --- a/test/settings/controllers/UseSystemFontController-test.ts +++ b/test/settings/controllers/UseSystemFontController-test.ts @@ -20,14 +20,14 @@ import UseSystemFontController from "../../../src/settings/controllers/UseSystem import { SettingLevel } from "../../../src/settings/SettingLevel"; import SettingsStore from "../../../src/settings/SettingsStore"; -const dispatchSpy = jest.spyOn(dis, 'dispatch'); +const dispatchSpy = jest.spyOn(dis, "dispatch"); -describe('UseSystemFontController', () => { - it('dispatches a font size action on change', () => { - const getValueSpy = jest.spyOn(SettingsStore, 'getValue').mockReturnValue(12); +describe("UseSystemFontController", () => { + it("dispatches a font size action on change", () => { + const getValueSpy = jest.spyOn(SettingsStore, "getValue").mockReturnValue(12); const controller = new UseSystemFontController(); - controller.onChange(SettingLevel.ACCOUNT, '$room:server', true); + controller.onChange(SettingLevel.ACCOUNT, "$room:server", true); expect(dispatchSpy).toHaveBeenCalledWith({ action: Action.UpdateSystemFont, diff --git a/test/settings/watchers/FontWatcher-test.tsx b/test/settings/watchers/FontWatcher-test.tsx index 25aba6c2dd..3cc80d95a1 100644 --- a/test/settings/watchers/FontWatcher-test.tsx +++ b/test/settings/watchers/FontWatcher-test.tsx @@ -15,10 +15,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { sleep } from 'matrix-js-sdk/src/utils'; +import { sleep } from "matrix-js-sdk/src/utils"; -import SettingsStore from '../../../src/settings/SettingsStore'; -import { SettingLevel } from '../../../src/settings/SettingLevel'; +import SettingsStore from "../../../src/settings/SettingsStore"; +import { SettingLevel } from "../../../src/settings/SettingLevel"; import { FontWatcher } from "../../../src/settings/watchers/FontWatcher"; import { Action } from "../../../src/dispatcher/actions"; import { untilDispatch } from "../../test-utils"; @@ -31,7 +31,7 @@ async function setSystemFont(font: string): Promise { await sleep(1); // await the FontWatcher doing its action } -describe('FontWatcher', function() { +describe("FontWatcher", function () { it("should load font on start()", async () => { const watcher = new FontWatcher(); await setSystemFont("Font Name"); @@ -67,15 +67,15 @@ describe('FontWatcher', function() { fontWatcher.stop(); }); - it('encloses the fonts by double quotes and sets them as the system font', async () => { + it("encloses the fonts by double quotes and sets them as the system font", async () => { await setSystemFont("Fira Sans Thin, Commodore 64"); expect(document.body.style.fontFamily).toBe(`"Fira Sans Thin","Commodore 64"`); }); - it('does not add double quotes if already present and sets the font as the system font', async () => { + it("does not add double quotes if already present and sets the font as the system font", async () => { await setSystemFont(`"Commodore 64"`); expect(document.body.style.fontFamily).toBe(`"Commodore 64"`); }); - it('trims whitespace, encloses the fonts by double quotes, and sets them as the system font', async () => { + it("trims whitespace, encloses the fonts by double quotes, and sets them as the system font", async () => { await setSystemFont(` Fira Code , "Commodore 64" `); expect(document.body.style.fontFamily).toBe(`"Fira Code","Commodore 64"`); }); diff --git a/test/settings/watchers/ThemeWatcher-test.tsx b/test/settings/watchers/ThemeWatcher-test.tsx index c97ba13a33..d38c899587 100644 --- a/test/settings/watchers/ThemeWatcher-test.tsx +++ b/test/settings/watchers/ThemeWatcher-test.tsx @@ -14,9 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import SettingsStore from '../../../src/settings/SettingsStore'; -import ThemeWatcher from '../../../src/settings/watchers/ThemeWatcher'; -import { SettingLevel } from '../../../src/settings/SettingLevel'; +import SettingsStore from "../../../src/settings/SettingsStore"; +import ThemeWatcher from "../../../src/settings/watchers/ThemeWatcher"; +import { SettingLevel } from "../../../src/settings/SettingLevel"; function makeMatchMedia(values: any) { class FakeMediaQueryList { @@ -27,7 +27,9 @@ function makeMatchMedia(values: any) { removeListener() {} addEventListener() {} removeEventListener() {} - dispatchEvent() { return true; } + dispatchEvent() { + return true; + } constructor(query: string) { this.matches = values[query]; @@ -40,11 +42,7 @@ function makeMatchMedia(values: any) { } function makeGetValue(values: any) { - return function getValue( - settingName: string, - _roomId: string = null, - _excludeDefault = false, - ): T { + return function getValue(settingName: string, _roomId: string = null, _excludeDefault = false): T { return values[settingName]; }; } @@ -61,8 +59,8 @@ function makeGetValueAt(values: any) { }; } -describe('ThemeWatcher', function() { - it('should choose a light theme by default', () => { +describe("ThemeWatcher", function () { + it("should choose a light theme by default", () => { // Given no system settings global.matchMedia = makeMatchMedia({}); @@ -71,12 +69,12 @@ describe('ThemeWatcher', function() { expect(themeWatcher.getEffectiveTheme()).toBe("light"); }); - it('should choose default theme if system settings are inconclusive', () => { + it("should choose default theme if system settings are inconclusive", () => { // Given no system settings but we asked to use them global.matchMedia = makeMatchMedia({}); SettingsStore.getValue = makeGetValue({ - "use_system_theme": true, - "theme": "light", + use_system_theme: true, + theme: "light", }); // Then getEffectiveTheme returns light @@ -84,115 +82,114 @@ describe('ThemeWatcher', function() { expect(themeWatcher.getEffectiveTheme()).toBe("light"); }); - it('should choose a dark theme if that is selected', () => { + it("should choose a dark theme if that is selected", () => { // Given system says light high contrast but theme is set to dark global.matchMedia = makeMatchMedia({ "(prefers-contrast: more)": true, "(prefers-color-scheme: light)": true, }); - SettingsStore.getValueAt = makeGetValueAt({ "theme": "dark" }); + SettingsStore.getValueAt = makeGetValueAt({ theme: "dark" }); // Then getEffectiveTheme returns dark const themeWatcher = new ThemeWatcher(); expect(themeWatcher.getEffectiveTheme()).toBe("dark"); }); - it('should choose a light theme if that is selected', () => { + it("should choose a light theme if that is selected", () => { // Given system settings say dark high contrast but theme set to light global.matchMedia = makeMatchMedia({ "(prefers-contrast: more)": true, "(prefers-color-scheme: dark)": true, }); - SettingsStore.getValueAt = makeGetValueAt({ "theme": "light" }); + SettingsStore.getValueAt = makeGetValueAt({ theme: "light" }); // Then getEffectiveTheme returns light const themeWatcher = new ThemeWatcher(); expect(themeWatcher.getEffectiveTheme()).toBe("light"); }); - it('should choose a light-high-contrast theme if that is selected', () => { + it("should choose a light-high-contrast theme if that is selected", () => { // Given system settings say dark and theme set to light-high-contrast global.matchMedia = makeMatchMedia({ "(prefers-color-scheme: dark)": true }); - SettingsStore.getValueAt = makeGetValueAt({ "theme": "light-high-contrast" }); + SettingsStore.getValueAt = makeGetValueAt({ theme: "light-high-contrast" }); // Then getEffectiveTheme returns light-high-contrast const themeWatcher = new ThemeWatcher(); expect(themeWatcher.getEffectiveTheme()).toBe("light-high-contrast"); }); - it('should choose a light theme if system prefers it (via default)', () => { + it("should choose a light theme if system prefers it (via default)", () => { // Given system prefers lightness, even though we did not // click "Use system theme" or choose a theme explicitly global.matchMedia = makeMatchMedia({ "(prefers-color-scheme: light)": true }); SettingsStore.getValueAt = makeGetValueAt({}); - SettingsStore.getValue = makeGetValue({ "use_system_theme": true }); + SettingsStore.getValue = makeGetValue({ use_system_theme: true }); // Then getEffectiveTheme returns light const themeWatcher = new ThemeWatcher(); expect(themeWatcher.getEffectiveTheme()).toBe("light"); }); - it('should choose a dark theme if system prefers it (via default)', () => { + it("should choose a dark theme if system prefers it (via default)", () => { // Given system prefers darkness, even though we did not // click "Use system theme" or choose a theme explicitly global.matchMedia = makeMatchMedia({ "(prefers-color-scheme: dark)": true }); SettingsStore.getValueAt = makeGetValueAt({}); - SettingsStore.getValue = makeGetValue({ "use_system_theme": true }); + SettingsStore.getValue = makeGetValue({ use_system_theme: true }); // Then getEffectiveTheme returns dark const themeWatcher = new ThemeWatcher(); expect(themeWatcher.getEffectiveTheme()).toBe("dark"); }); - it('should choose a light theme if system prefers it (explicit)', () => { + it("should choose a light theme if system prefers it (explicit)", () => { // Given system prefers lightness global.matchMedia = makeMatchMedia({ "(prefers-color-scheme: light)": true }); - SettingsStore.getValueAt = makeGetValueAt({ "use_system_theme": true }); - SettingsStore.getValue = makeGetValue({ "use_system_theme": true }); + SettingsStore.getValueAt = makeGetValueAt({ use_system_theme: true }); + SettingsStore.getValue = makeGetValue({ use_system_theme: true }); // Then getEffectiveTheme returns light const themeWatcher = new ThemeWatcher(); expect(themeWatcher.getEffectiveTheme()).toBe("light"); }); - it('should choose a dark theme if system prefers it (explicit)', () => { + it("should choose a dark theme if system prefers it (explicit)", () => { // Given system prefers darkness global.matchMedia = makeMatchMedia({ "(prefers-color-scheme: dark)": true }); - SettingsStore.getValueAt = makeGetValueAt({ "use_system_theme": true }); - SettingsStore.getValue = makeGetValue({ "use_system_theme": true }); + SettingsStore.getValueAt = makeGetValueAt({ use_system_theme: true }); + SettingsStore.getValue = makeGetValue({ use_system_theme: true }); // Then getEffectiveTheme returns dark const themeWatcher = new ThemeWatcher(); expect(themeWatcher.getEffectiveTheme()).toBe("dark"); }); - it('should choose a high-contrast theme if system prefers it', () => { + it("should choose a high-contrast theme if system prefers it", () => { // Given system prefers high contrast and light global.matchMedia = makeMatchMedia({ "(prefers-contrast: more)": true, "(prefers-color-scheme: light)": true, }); - SettingsStore.getValueAt = makeGetValueAt({ "use_system_theme": true }); - SettingsStore.getValue = makeGetValue({ "use_system_theme": true }); + SettingsStore.getValueAt = makeGetValueAt({ use_system_theme: true }); + SettingsStore.getValue = makeGetValue({ use_system_theme: true }); // Then getEffectiveTheme returns light-high-contrast const themeWatcher = new ThemeWatcher(); expect(themeWatcher.getEffectiveTheme()).toBe("light-high-contrast"); }); - it('should not choose a high-contrast theme if not available', () => { + it("should not choose a high-contrast theme if not available", () => { // Given system prefers high contrast and dark, but we don't (yet) // have a high-contrast dark theme global.matchMedia = makeMatchMedia({ "(prefers-contrast: more)": true, "(prefers-color-scheme: dark)": true, }); - SettingsStore.getValueAt = makeGetValueAt({ "use_system_theme": true }); - SettingsStore.getValue = makeGetValue({ "use_system_theme": true }); + SettingsStore.getValueAt = makeGetValueAt({ use_system_theme: true }); + SettingsStore.getValue = makeGetValue({ use_system_theme: true }); // Then getEffectiveTheme returns dark const themeWatcher = new ThemeWatcher(); expect(themeWatcher.getEffectiveTheme()).toBe("dark"); }); }); - diff --git a/test/setup/setupConfig.ts b/test/setup/setupConfig.ts index e67493412d..46cc12a4ef 100644 --- a/test/setup/setupConfig.ts +++ b/test/setup/setupConfig.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import SdkConfig, { DEFAULTS } from '../../src/SdkConfig'; +import SdkConfig, { DEFAULTS } from "../../src/SdkConfig"; // uninitialised SdkConfig causes lots of warnings in console // init with defaults diff --git a/test/setup/setupLanguage.ts b/test/setup/setupLanguage.ts index bd07616ab3..86cb80ab4d 100644 --- a/test/setup/setupLanguage.ts +++ b/test/setup/setupLanguage.ts @@ -34,7 +34,7 @@ function weblateToCounterpart(inTrs: object): object { const outTrs = {}; for (const key of Object.keys(inTrs)) { - const keyParts = key.split('|', 2); + const keyParts = key.split("|", 2); if (keyParts.length === 2) { let obj = outTrs[keyParts[0]]; if (obj === undefined) { @@ -43,7 +43,7 @@ function weblateToCounterpart(inTrs: object): object { // This is a transitional edge case if a string went from singular to pluralised and both still remain // in the translation json file. Use the singular translation as `other` and merge pluralisation atop. obj = outTrs[keyParts[0]] = { - "other": inTrs[key], + other: inTrs[key], }; console.warn("Found entry in i18n file in both singular and pluralised form", keyParts[0]); } @@ -58,22 +58,22 @@ function weblateToCounterpart(inTrs: object): object { fetchMock .get("/i18n/languages.json", { - "en": { - "fileName": "en_EN.json", - "label": "English", + en: { + fileName: "en_EN.json", + label: "English", }, - "de": { - "fileName": "de_DE.json", - "label": "German", + de: { + fileName: "de_DE.json", + label: "German", }, - "lv": { - "fileName": "lv.json", - "label": "Latvian", + lv: { + fileName: "lv.json", + label: "Latvian", }, }) .get("end:en_EN.json", weblateToCounterpart(en)) .get("end:de_DE.json", weblateToCounterpart(de)) .get("end:lv.json", weblateToCounterpart(lv)); -languageHandler.setLanguage('en'); -languageHandler.setMissingEntryGenerator(key => key.split("|", 2)[1]); +languageHandler.setLanguage("en"); +languageHandler.setMissingEntryGenerator((key) => key.split("|", 2)[1]); diff --git a/test/setup/setupManualMocks.ts b/test/setup/setupManualMocks.ts index 8ee7750c9c..ee941817bc 100644 --- a/test/setup/setupManualMocks.ts +++ b/test/setup/setupManualMocks.ts @@ -16,12 +16,12 @@ limitations under the License. import fetchMock from "fetch-mock-jest"; import { TextDecoder, TextEncoder } from "util"; -import fetch from 'node-fetch'; +import fetch from "node-fetch"; // jest 27 removes setImmediate from jsdom // polyfill until setImmediate use in client can be removed // @ts-ignore - we know the contract is wrong. That's why we're stubbing it. -global.setImmediate = callback => window.setTimeout(callback, 0); +global.setImmediate = (callback) => window.setTimeout(callback, 0); // Stub ResizeObserver // @ts-ignore - we know it's a duplicate (that's why we're stubbing it) @@ -56,7 +56,7 @@ class MyClipboardEvent extends Event {} window.ClipboardEvent = MyClipboardEvent as any; // matchMedia is not included in jsdom -const mockMatchMedia = jest.fn().mockImplementation(query => ({ +const mockMatchMedia = jest.fn().mockImplementation((query) => ({ matches: false, media: query, onchange: null, diff --git a/test/slowReporter.js b/test/slowReporter.js index 7b4a5deb82..f63f218c36 100644 --- a/test/slowReporter.js +++ b/test/slowReporter.js @@ -14,4 +14,4 @@ See the License for the specific language governing permissions and limitations under the License. */ -module.exports = require('matrix-js-sdk/spec/slowReporter'); +module.exports = require("matrix-js-sdk/spec/slowReporter"); diff --git a/test/stores/MemberListStore-test.ts b/test/stores/MemberListStore-test.ts index a2202e2ef4..a97b00ba0f 100644 --- a/test/stores/MemberListStore-test.ts +++ b/test/stores/MemberListStore-test.ts @@ -123,9 +123,12 @@ describe("MemberListStore", () => { addMember(room, doris, "join", "AAAAA"); ({ invited, joined } = await store.loadMemberList(roomId)); expect(invited).toEqual([]); - expect(joined).toEqual( - [room.getMember(doris), room.getMember(alice), room.getMember(bob), room.getMember(charlie)], - ); + expect(joined).toEqual([ + room.getMember(doris), + room.getMember(alice), + room.getMember(bob), + room.getMember(charlie), + ]); }); it("filters based on a search query", async () => { @@ -164,7 +167,7 @@ describe("MemberListStore", () => { describe("sliding sync", () => { beforeEach(() => { - jest.spyOn(SettingsStore, 'getValue').mockImplementation((settingName, roomId, value) => { + jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName, roomId, value) => { return settingName === "feature_sliding_sync"; // this is enabled, everything else is disabled. }); client.members = jest.fn(); @@ -210,26 +213,32 @@ function addEventToRoom(room: Room, ev: MatrixEvent) { } function setPowerLevels(room: Room, pl: IContent) { - addEventToRoom(room, new MatrixEvent({ - type: EventType.RoomPowerLevels, - state_key: "", - content: pl, - sender: room.getCreator()!, - room_id: room.roomId, - event_id: "$" + Math.random(), - })); + addEventToRoom( + room, + new MatrixEvent({ + type: EventType.RoomPowerLevels, + state_key: "", + content: pl, + sender: room.getCreator()!, + room_id: room.roomId, + event_id: "$" + Math.random(), + }), + ); } function addMember(room: Room, userId: string, membership: string, displayName?: string) { - addEventToRoom(room, new MatrixEvent({ - type: EventType.RoomMember, - state_key: userId, - content: { - membership: membership, - displayname: displayName, - }, - sender: userId, - room_id: room.roomId, - event_id: "$" + Math.random(), - })); + addEventToRoom( + room, + new MatrixEvent({ + type: EventType.RoomMember, + state_key: userId, + content: { + membership: membership, + displayname: displayName, + }, + sender: userId, + room_id: room.roomId, + event_id: "$" + Math.random(), + }), + ); } diff --git a/test/stores/OwnBeaconStore-test.ts b/test/stores/OwnBeaconStore-test.ts index 9835ddfb46..b8950a7693 100644 --- a/test/stores/OwnBeaconStore-test.ts +++ b/test/stores/OwnBeaconStore-test.ts @@ -35,74 +35,50 @@ import { resetAsyncStoreWithClient, setupAsyncStoreWithClient, } from "../test-utils"; -import { - makeBeaconInfoEvent, - mockGeolocation, - watchPositionMockImplementation, -} from "../test-utils/beacon"; +import { makeBeaconInfoEvent, mockGeolocation, watchPositionMockImplementation } from "../test-utils/beacon"; import { getMockClientWithEventEmitter } from "../test-utils/client"; // modern fake timers and lodash.debounce are a faff // short circuit it jest.mock("lodash", () => ({ - ...jest.requireActual("lodash") as object, - debounce: jest.fn().mockImplementation(callback => callback), + ...(jest.requireActual("lodash") as object), + debounce: jest.fn().mockImplementation((callback) => callback), })); jest.useFakeTimers(); -describe('OwnBeaconStore', () => { +describe("OwnBeaconStore", () => { let geolocation; // 14.03.2022 16:15 const now = 1647270879403; const HOUR_MS = 3600000; - const aliceId = '@alice:server.org'; - const bobId = '@bob:server.org'; + const aliceId = "@alice:server.org"; + const bobId = "@bob:server.org"; const mockClient = getMockClientWithEventEmitter({ getUserId: jest.fn().mockReturnValue(aliceId), getVisibleRooms: jest.fn().mockReturnValue([]), - unstable_setLiveBeacon: jest.fn().mockResolvedValue({ event_id: '1' }), - sendEvent: jest.fn().mockResolvedValue({ event_id: '1' }), - unstable_createLiveBeacon: jest.fn().mockResolvedValue({ event_id: '1' }), + unstable_setLiveBeacon: jest.fn().mockResolvedValue({ event_id: "1" }), + sendEvent: jest.fn().mockResolvedValue({ event_id: "1" }), + unstable_createLiveBeacon: jest.fn().mockResolvedValue({ event_id: "1" }), }); - const room1Id = '$room1:server.org'; - const room2Id = '$room2:server.org'; + const room1Id = "$room1:server.org"; + const room2Id = "$room2:server.org"; // returned by default geolocation mocks - const defaultLocationUri = 'geo:54.001927,-8.253491;u=1'; + const defaultLocationUri = "geo:54.001927,-8.253491;u=1"; // beacon_info events // created 'an hour ago' // with timeout of 3 hours // event creation sets timestamp to Date.now() - jest.spyOn(global.Date, 'now').mockReturnValue(now - HOUR_MS); - const alicesRoom1BeaconInfo = makeBeaconInfoEvent(aliceId, - room1Id, - { isLive: true }, - '$alice-room1-1', - ); - const alicesRoom2BeaconInfo = makeBeaconInfoEvent(aliceId, - room2Id, - { isLive: true }, - '$alice-room2-1', - ); - const alicesOldRoomIdBeaconInfo = makeBeaconInfoEvent(aliceId, - room1Id, - { isLive: false }, - '$alice-room1-2', - ); - const bobsRoom1BeaconInfo = makeBeaconInfoEvent(bobId, - room1Id, - { isLive: true }, - '$bob-room1-1', - ); - const bobsOldRoom1BeaconInfo = makeBeaconInfoEvent(bobId, - room1Id, - { isLive: false }, - '$bob-room1-2', - ); + jest.spyOn(global.Date, "now").mockReturnValue(now - HOUR_MS); + const alicesRoom1BeaconInfo = makeBeaconInfoEvent(aliceId, room1Id, { isLive: true }, "$alice-room1-1"); + const alicesRoom2BeaconInfo = makeBeaconInfoEvent(aliceId, room2Id, { isLive: true }, "$alice-room2-1"); + const alicesOldRoomIdBeaconInfo = makeBeaconInfoEvent(aliceId, room1Id, { isLive: false }, "$alice-room1-2"); + const bobsRoom1BeaconInfo = makeBeaconInfoEvent(bobId, room1Id, { isLive: true }, "$bob-room1-1"); + const bobsOldRoom1BeaconInfo = makeBeaconInfoEvent(bobId, room1Id, { isLive: false }, "$bob-room1-2"); // make fresh rooms every time // as we update room state @@ -144,7 +120,7 @@ describe('OwnBeaconStore', () => { beaconInfoEvent.getSender(), beaconInfoEvent.getRoomId(), { isLive, timeout: beacon.beaconInfo.timeout }, - 'update-event-id', + "update-event-id", ); beacon.update(updateEvent); @@ -157,17 +133,17 @@ describe('OwnBeaconStore', () => { mockClient.emit(BeaconEvent.New, beaconInfoEvent, beacon); }; - const localStorageGetSpy = jest.spyOn(localStorage.__proto__, 'getItem').mockReturnValue(undefined); - const localStorageSetSpy = jest.spyOn(localStorage.__proto__, 'setItem').mockImplementation(() => {}); + const localStorageGetSpy = jest.spyOn(localStorage.__proto__, "getItem").mockReturnValue(undefined); + const localStorageSetSpy = jest.spyOn(localStorage.__proto__, "setItem").mockImplementation(() => {}); beforeEach(() => { geolocation = mockGeolocation(); mockClient.getVisibleRooms.mockReturnValue([]); - mockClient.unstable_setLiveBeacon.mockClear().mockResolvedValue({ event_id: '1' }); - mockClient.sendEvent.mockReset().mockResolvedValue({ event_id: '1' }); - jest.spyOn(global.Date, 'now').mockReturnValue(now); - jest.spyOn(OwnBeaconStore.instance, 'emit').mockRestore(); - jest.spyOn(logger, 'error').mockRestore(); + mockClient.unstable_setLiveBeacon.mockClear().mockResolvedValue({ event_id: "1" }); + mockClient.sendEvent.mockReset().mockResolvedValue({ event_id: "1" }); + jest.spyOn(global.Date, "now").mockReturnValue(now); + jest.spyOn(OwnBeaconStore.instance, "emit").mockRestore(); + jest.spyOn(logger, "error").mockRestore(); localStorageGetSpy.mockClear().mockReturnValue(undefined); localStorageSetSpy.mockClear(); @@ -183,22 +159,22 @@ describe('OwnBeaconStore', () => { localStorageGetSpy.mockRestore(); }); - describe('onReady()', () => { - it('initialises correctly with no beacons', async () => { + describe("onReady()", () => { + it("initialises correctly with no beacons", async () => { makeRoomsWithStateEvents(); const store = await makeOwnBeaconStore(); expect(store.hasLiveBeacons()).toBe(false); expect(store.getLiveBeaconIds()).toEqual([]); }); - it('does not add other users beacons to beacon state', async () => { + it("does not add other users beacons to beacon state", async () => { makeRoomsWithStateEvents([bobsRoom1BeaconInfo, bobsOldRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); expect(store.hasLiveBeacons()).toBe(false); expect(store.getLiveBeaconIds()).toEqual([]); }); - it('adds own users beacons to state', async () => { + it("adds own users beacons to state", async () => { makeRoomsWithStateEvents([ alicesRoom1BeaconInfo, alicesRoom2BeaconInfo, @@ -206,19 +182,18 @@ describe('OwnBeaconStore', () => { bobsOldRoom1BeaconInfo, ]); const store = await makeOwnBeaconStore(); - expect(store.beaconsByRoomId.get(room1Id)).toEqual(new Set([ - getBeaconInfoIdentifier(alicesRoom1BeaconInfo), - ])); - expect(store.beaconsByRoomId.get(room2Id)).toEqual(new Set([ - getBeaconInfoIdentifier(alicesRoom2BeaconInfo), - ])); + expect(store.beaconsByRoomId.get(room1Id)).toEqual( + new Set([getBeaconInfoIdentifier(alicesRoom1BeaconInfo)]), + ); + expect(store.beaconsByRoomId.get(room2Id)).toEqual( + new Set([getBeaconInfoIdentifier(alicesRoom2BeaconInfo)]), + ); }); - it('updates live beacon ids when users own beacons were created on device', async () => { - localStorageGetSpy.mockReturnValue(JSON.stringify([ - alicesRoom1BeaconInfo.getId(), - alicesRoom2BeaconInfo.getId(), - ])); + it("updates live beacon ids when users own beacons were created on device", async () => { + localStorageGetSpy.mockReturnValue( + JSON.stringify([alicesRoom1BeaconInfo.getId(), alicesRoom2BeaconInfo.getId()]), + ); makeRoomsWithStateEvents([ alicesRoom1BeaconInfo, alicesRoom2BeaconInfo, @@ -233,7 +208,7 @@ describe('OwnBeaconStore', () => { ]); }); - it('does not do any geolocation when user has no live beacons', async () => { + it("does not do any geolocation when user has no live beacons", async () => { makeRoomsWithStateEvents([bobsRoom1BeaconInfo, bobsOldRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); expect(store.hasLiveBeacons()).toBe(false); @@ -244,15 +219,11 @@ describe('OwnBeaconStore', () => { expect(mockClient.sendEvent).not.toHaveBeenCalled(); }); - it('does geolocation and sends location immediately when user has live beacons', async () => { - localStorageGetSpy.mockReturnValue(JSON.stringify([ - alicesRoom1BeaconInfo.getId(), - alicesRoom2BeaconInfo.getId(), - ])); - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - alicesRoom2BeaconInfo, - ]); + it("does geolocation and sends location immediately when user has live beacons", async () => { + localStorageGetSpy.mockReturnValue( + JSON.stringify([alicesRoom1BeaconInfo.getId(), alicesRoom2BeaconInfo.getId()]), + ); + makeRoomsWithStateEvents([alicesRoom1BeaconInfo, alicesRoom2BeaconInfo]); await makeOwnBeaconStore(); await flushPromisesWithFakeTimers(); @@ -270,10 +241,10 @@ describe('OwnBeaconStore', () => { }); }); - describe('onNotReady()', () => { - it('removes listeners', async () => { + describe("onNotReady()", () => { + it("removes listeners", async () => { const store = await makeOwnBeaconStore(); - const removeSpy = jest.spyOn(mockClient, 'removeListener'); + const removeSpy = jest.spyOn(mockClient, "removeListener"); // @ts-ignore store.onNotReady(); @@ -284,13 +255,11 @@ describe('OwnBeaconStore', () => { expect(removeSpy.mock.calls[4]).toEqual(expect.arrayContaining([RoomStateEvent.Members])); }); - it('destroys beacons', async () => { - const [room1] = makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + it("destroys beacons", async () => { + const [room1] = makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); const beacon = room1.currentState.beacons.get(getBeaconInfoIdentifier(alicesRoom1BeaconInfo)); - const destroySpy = jest.spyOn(beacon, 'destroy'); + const destroySpy = jest.spyOn(beacon, "destroy"); // @ts-ignore store.onNotReady(); @@ -298,7 +267,7 @@ describe('OwnBeaconStore', () => { }); }); - describe('hasLiveBeacons()', () => { + describe("hasLiveBeacons()", () => { beforeEach(() => { makeRoomsWithStateEvents([ alicesRoom1BeaconInfo, @@ -306,53 +275,37 @@ describe('OwnBeaconStore', () => { bobsRoom1BeaconInfo, bobsOldRoom1BeaconInfo, ]); - localStorageGetSpy.mockReturnValue(JSON.stringify([ - alicesRoom1BeaconInfo.getId(), - alicesRoom2BeaconInfo.getId(), - ])); + localStorageGetSpy.mockReturnValue( + JSON.stringify([alicesRoom1BeaconInfo.getId(), alicesRoom2BeaconInfo.getId()]), + ); }); - it('returns true when user has live beacons', async () => { - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - bobsRoom1BeaconInfo, - bobsOldRoom1BeaconInfo, - ]); + it("returns true when user has live beacons", async () => { + makeRoomsWithStateEvents([alicesRoom1BeaconInfo, bobsRoom1BeaconInfo, bobsOldRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); expect(store.hasLiveBeacons()).toBe(true); }); - it('returns false when user does not have live beacons', async () => { - makeRoomsWithStateEvents([ - alicesOldRoomIdBeaconInfo, - bobsOldRoom1BeaconInfo, - ]); + it("returns false when user does not have live beacons", async () => { + makeRoomsWithStateEvents([alicesOldRoomIdBeaconInfo, bobsOldRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); expect(store.hasLiveBeacons()).toBe(false); }); - it('returns true when user has live beacons for roomId', async () => { - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - bobsRoom1BeaconInfo, - bobsOldRoom1BeaconInfo, - ]); + it("returns true when user has live beacons for roomId", async () => { + makeRoomsWithStateEvents([alicesRoom1BeaconInfo, bobsRoom1BeaconInfo, bobsOldRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); expect(store.hasLiveBeacons(room1Id)).toBe(true); }); - it('returns false when user does not have live beacons for roomId', async () => { - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - bobsRoom1BeaconInfo, - bobsOldRoom1BeaconInfo, - ]); + it("returns false when user does not have live beacons for roomId", async () => { + makeRoomsWithStateEvents([alicesRoom1BeaconInfo, bobsRoom1BeaconInfo, bobsOldRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); expect(store.hasLiveBeacons(room2Id)).toBe(false); }); }); - describe('getLiveBeaconIds()', () => { + describe("getLiveBeaconIds()", () => { beforeEach(() => { makeRoomsWithStateEvents([ alicesRoom1BeaconInfo, @@ -360,34 +313,24 @@ describe('OwnBeaconStore', () => { bobsRoom1BeaconInfo, bobsOldRoom1BeaconInfo, ]); - localStorageGetSpy.mockReturnValue(JSON.stringify([ - alicesRoom1BeaconInfo.getId(), - alicesRoom2BeaconInfo.getId(), - ])); + localStorageGetSpy.mockReturnValue( + JSON.stringify([alicesRoom1BeaconInfo.getId(), alicesRoom2BeaconInfo.getId()]), + ); }); - it('returns live beacons when user has live beacons', async () => { - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - bobsRoom1BeaconInfo, - bobsOldRoom1BeaconInfo, - ]); + it("returns live beacons when user has live beacons", async () => { + makeRoomsWithStateEvents([alicesRoom1BeaconInfo, bobsRoom1BeaconInfo, bobsOldRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); - expect(store.getLiveBeaconIds()).toEqual([ - getBeaconInfoIdentifier(alicesRoom1BeaconInfo), - ]); + expect(store.getLiveBeaconIds()).toEqual([getBeaconInfoIdentifier(alicesRoom1BeaconInfo)]); }); - it('returns empty array when user does not have live beacons', async () => { - makeRoomsWithStateEvents([ - alicesOldRoomIdBeaconInfo, - bobsOldRoom1BeaconInfo, - ]); + it("returns empty array when user does not have live beacons", async () => { + makeRoomsWithStateEvents([alicesOldRoomIdBeaconInfo, bobsOldRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); expect(store.getLiveBeaconIds()).toEqual([]); }); - it('returns beacon ids for room when user has live beacons for roomId', async () => { + it("returns beacon ids for room when user has live beacons for roomId", async () => { makeRoomsWithStateEvents([ alicesRoom1BeaconInfo, alicesRoom2BeaconInfo, @@ -395,38 +338,29 @@ describe('OwnBeaconStore', () => { bobsOldRoom1BeaconInfo, ]); const store = await makeOwnBeaconStore(); - expect(store.getLiveBeaconIds(room1Id)).toEqual([ - getBeaconInfoIdentifier(alicesRoom1BeaconInfo), - ]); - expect(store.getLiveBeaconIds(room2Id)).toEqual([ - getBeaconInfoIdentifier(alicesRoom2BeaconInfo), - ]); + expect(store.getLiveBeaconIds(room1Id)).toEqual([getBeaconInfoIdentifier(alicesRoom1BeaconInfo)]); + expect(store.getLiveBeaconIds(room2Id)).toEqual([getBeaconInfoIdentifier(alicesRoom2BeaconInfo)]); }); - it('returns empty array when user does not have live beacons for roomId', async () => { - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - bobsRoom1BeaconInfo, - bobsOldRoom1BeaconInfo, - ]); + it("returns empty array when user does not have live beacons for roomId", async () => { + makeRoomsWithStateEvents([alicesRoom1BeaconInfo, bobsRoom1BeaconInfo, bobsOldRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); expect(store.getLiveBeaconIds(room2Id)).toEqual([]); }); }); - describe('on new beacon event', () => { + describe("on new beacon event", () => { // assume all beacons were created on this device beforeEach(() => { - localStorageGetSpy.mockReturnValue(JSON.stringify([ - alicesRoom1BeaconInfo.getId(), - alicesRoom2BeaconInfo.getId(), - ])); + localStorageGetSpy.mockReturnValue( + JSON.stringify([alicesRoom1BeaconInfo.getId(), alicesRoom2BeaconInfo.getId()]), + ); }); - it('ignores events for irrelevant beacons', async () => { + it("ignores events for irrelevant beacons", async () => { makeRoomsWithStateEvents([]); const store = await makeOwnBeaconStore(); const bobsLiveBeacon = new Beacon(bobsRoom1BeaconInfo); - const monitorSpy = jest.spyOn(bobsLiveBeacon, 'monitorLiveness'); + const monitorSpy = jest.spyOn(bobsLiveBeacon, "monitorLiveness"); mockClient.emit(BeaconEvent.New, bobsRoom1BeaconInfo, bobsLiveBeacon); @@ -435,11 +369,11 @@ describe('OwnBeaconStore', () => { expect(store.hasLiveBeacons()).toBe(false); }); - it('adds users beacons to state and monitors liveness', async () => { + it("adds users beacons to state and monitors liveness", async () => { makeRoomsWithStateEvents([]); const store = await makeOwnBeaconStore(); const alicesLiveBeacon = new Beacon(alicesRoom1BeaconInfo); - const monitorSpy = jest.spyOn(alicesLiveBeacon, 'monitorLiveness'); + const monitorSpy = jest.spyOn(alicesLiveBeacon, "monitorLiveness"); mockClient.emit(BeaconEvent.New, alicesRoom1BeaconInfo, alicesLiveBeacon); @@ -448,10 +382,10 @@ describe('OwnBeaconStore', () => { expect(store.hasLiveBeacons(room1Id)).toBe(true); }); - it('emits a liveness change event when new beacons change live state', async () => { + it("emits a liveness change event when new beacons change live state", async () => { makeRoomsWithStateEvents([]); const store = await makeOwnBeaconStore(); - const emitSpy = jest.spyOn(store, 'emit'); + const emitSpy = jest.spyOn(store, "emit"); const alicesLiveBeacon = new Beacon(alicesRoom1BeaconInfo); mockClient.emit(BeaconEvent.New, alicesRoom1BeaconInfo, alicesLiveBeacon); @@ -459,14 +393,12 @@ describe('OwnBeaconStore', () => { expect(emitSpy).toHaveBeenCalledWith(OwnBeaconStoreEvent.LivenessChange, [alicesLiveBeacon.identifier]); }); - it('emits a liveness change event when new beacons do not change live state', async () => { - makeRoomsWithStateEvents([ - alicesRoom2BeaconInfo, - ]); + it("emits a liveness change event when new beacons do not change live state", async () => { + makeRoomsWithStateEvents([alicesRoom2BeaconInfo]); const store = await makeOwnBeaconStore(); // already live expect(store.hasLiveBeacons()).toBe(true); - const emitSpy = jest.spyOn(store, 'emit'); + const emitSpy = jest.spyOn(store, "emit"); const alicesLiveBeacon = new Beacon(alicesRoom1BeaconInfo); mockClient.emit(BeaconEvent.New, alicesRoom1BeaconInfo, alicesLiveBeacon); @@ -475,23 +407,23 @@ describe('OwnBeaconStore', () => { }); }); - describe('on liveness change event', () => { + describe("on liveness change event", () => { // assume all beacons were created on this device beforeEach(() => { - localStorageGetSpy.mockReturnValue(JSON.stringify([ - alicesRoom1BeaconInfo.getId(), - alicesRoom2BeaconInfo.getId(), - alicesOldRoomIdBeaconInfo.getId(), - 'update-event-id', - ])); + localStorageGetSpy.mockReturnValue( + JSON.stringify([ + alicesRoom1BeaconInfo.getId(), + alicesRoom2BeaconInfo.getId(), + alicesOldRoomIdBeaconInfo.getId(), + "update-event-id", + ]), + ); }); - it('ignores events for irrelevant beacons', async () => { - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + it("ignores events for irrelevant beacons", async () => { + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); - const emitSpy = jest.spyOn(store, 'emit'); + const emitSpy = jest.spyOn(store, "emit"); const oldLiveBeaconIds = store.getLiveBeaconIds(); const bobsLiveBeacon = new Beacon(bobsRoom1BeaconInfo); @@ -502,15 +434,13 @@ describe('OwnBeaconStore', () => { expect(store.getLiveBeaconIds()).toBe(oldLiveBeaconIds); }); - it('updates state and emits beacon liveness changes from true to false', async () => { - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + it("updates state and emits beacon liveness changes from true to false", async () => { + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); // live before expect(store.hasLiveBeacons()).toBe(true); - const emitSpy = jest.spyOn(store, 'emit'); + const emitSpy = jest.spyOn(store, "emit"); await expireBeaconAndEmit(store, alicesRoom1BeaconInfo); @@ -519,10 +449,8 @@ describe('OwnBeaconStore', () => { expect(emitSpy).toHaveBeenCalledWith(OwnBeaconStoreEvent.LivenessChange, []); }); - it('stops beacon when liveness changes from true to false and beacon is expired', async () => { - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + it("stops beacon when liveness changes from true to false and beacon is expired", async () => { + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); const prevEventContent = alicesRoom1BeaconInfo.getContent(); @@ -534,49 +462,40 @@ describe('OwnBeaconStore', () => { ...prevEventContent, live: false, }; - expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledWith( - room1Id, - expectedUpdateContent, - ); + expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledWith(room1Id, expectedUpdateContent); }); - it('updates state and when beacon liveness changes from false to true', async () => { - makeRoomsWithStateEvents([ - alicesOldRoomIdBeaconInfo, - ]); + it("updates state and when beacon liveness changes from false to true", async () => { + makeRoomsWithStateEvents([alicesOldRoomIdBeaconInfo]); const store = await makeOwnBeaconStore(); // not live before expect(store.hasLiveBeacons()).toBe(false); - const emitSpy = jest.spyOn(store, 'emit'); + const emitSpy = jest.spyOn(store, "emit"); updateBeaconLivenessAndEmit(store, alicesOldRoomIdBeaconInfo, true); expect(store.hasLiveBeacons()).toBe(true); expect(store.hasLiveBeacons(room1Id)).toBe(true); - expect(emitSpy).toHaveBeenCalledWith( - OwnBeaconStoreEvent.LivenessChange, - [getBeaconInfoIdentifier(alicesOldRoomIdBeaconInfo)], - ); + expect(emitSpy).toHaveBeenCalledWith(OwnBeaconStoreEvent.LivenessChange, [ + getBeaconInfoIdentifier(alicesOldRoomIdBeaconInfo), + ]); }); }); - describe('on room membership changes', () => { + describe("on room membership changes", () => { // assume all beacons were created on this device beforeEach(() => { - localStorageGetSpy.mockReturnValue(JSON.stringify([ - alicesRoom1BeaconInfo.getId(), - alicesRoom2BeaconInfo.getId(), - ])); + localStorageGetSpy.mockReturnValue( + JSON.stringify([alicesRoom1BeaconInfo.getId(), alicesRoom2BeaconInfo.getId()]), + ); }); - it('ignores events for rooms without beacons', async () => { + it("ignores events for rooms without beacons", async () => { const membershipEvent = makeMembershipEvent(room2Id, aliceId); // no beacons for room2 - const [, room2] = makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + const [, room2] = makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); - const emitSpy = jest.spyOn(store, 'emit'); + const emitSpy = jest.spyOn(store, "emit"); const oldLiveBeaconIds = store.getLiveBeaconIds(); mockClient.emit( @@ -591,78 +510,55 @@ describe('OwnBeaconStore', () => { expect(store.getLiveBeaconIds()).toBe(oldLiveBeaconIds); }); - it('ignores events for membership changes that are not current user', async () => { + it("ignores events for membership changes that are not current user", async () => { // bob joins room1 const membershipEvent = makeMembershipEvent(room1Id, bobId); const member = new RoomMember(room1Id, bobId); member.setMembershipEvent(membershipEvent); - const [room1] = makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + const [room1] = makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); - const emitSpy = jest.spyOn(store, 'emit'); + const emitSpy = jest.spyOn(store, "emit"); const oldLiveBeaconIds = store.getLiveBeaconIds(); - mockClient.emit( - RoomStateEvent.Members, - membershipEvent, - room1.currentState, - member, - ); + mockClient.emit(RoomStateEvent.Members, membershipEvent, room1.currentState, member); expect(emitSpy).not.toHaveBeenCalled(); // strictly equal expect(store.getLiveBeaconIds()).toBe(oldLiveBeaconIds); }); - it('ignores events for membership changes that are not leave/ban', async () => { + it("ignores events for membership changes that are not leave/ban", async () => { // alice joins room1 const membershipEvent = makeMembershipEvent(room1Id, aliceId); const member = new RoomMember(room1Id, aliceId); member.setMembershipEvent(membershipEvent); - const [room1] = makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - alicesRoom2BeaconInfo, - ]); + const [room1] = makeRoomsWithStateEvents([alicesRoom1BeaconInfo, alicesRoom2BeaconInfo]); const store = await makeOwnBeaconStore(); - const emitSpy = jest.spyOn(store, 'emit'); + const emitSpy = jest.spyOn(store, "emit"); const oldLiveBeaconIds = store.getLiveBeaconIds(); - mockClient.emit( - RoomStateEvent.Members, - membershipEvent, - room1.currentState, - member, - ); + mockClient.emit(RoomStateEvent.Members, membershipEvent, room1.currentState, member); expect(emitSpy).not.toHaveBeenCalled(); // strictly equal expect(store.getLiveBeaconIds()).toBe(oldLiveBeaconIds); }); - it('destroys and removes beacons when current user leaves room', async () => { + it("destroys and removes beacons when current user leaves room", async () => { // alice leaves room1 - const membershipEvent = makeMembershipEvent(room1Id, aliceId, 'leave'); + const membershipEvent = makeMembershipEvent(room1Id, aliceId, "leave"); const member = new RoomMember(room1Id, aliceId); member.setMembershipEvent(membershipEvent); - const [room1] = makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - alicesRoom2BeaconInfo, - ]); + const [room1] = makeRoomsWithStateEvents([alicesRoom1BeaconInfo, alicesRoom2BeaconInfo]); const store = await makeOwnBeaconStore(); const room1BeaconInstance = store.beacons.get(getBeaconInfoIdentifier(alicesRoom1BeaconInfo)); - const beaconDestroySpy = jest.spyOn(room1BeaconInstance, 'destroy'); - const emitSpy = jest.spyOn(store, 'emit'); + const beaconDestroySpy = jest.spyOn(room1BeaconInstance, "destroy"); + const emitSpy = jest.spyOn(store, "emit"); - mockClient.emit( - RoomStateEvent.Members, - membershipEvent, - room1.currentState, - member, - ); + mockClient.emit(RoomStateEvent.Members, membershipEvent, room1.currentState, member); expect(emitSpy).toHaveBeenCalledWith( OwnBeaconStoreEvent.LivenessChange, @@ -674,23 +570,23 @@ describe('OwnBeaconStore', () => { }); }); - describe('on destroy event', () => { + describe("on destroy event", () => { // assume all beacons were created on this device beforeEach(() => { - localStorageGetSpy.mockReturnValue(JSON.stringify([ - alicesRoom1BeaconInfo.getId(), - alicesRoom2BeaconInfo.getId(), - alicesOldRoomIdBeaconInfo.getId(), - 'update-event-id', - ])); + localStorageGetSpy.mockReturnValue( + JSON.stringify([ + alicesRoom1BeaconInfo.getId(), + alicesRoom2BeaconInfo.getId(), + alicesOldRoomIdBeaconInfo.getId(), + "update-event-id", + ]), + ); }); - it('ignores events for irrelevant beacons', async () => { - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + it("ignores events for irrelevant beacons", async () => { + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); - const emitSpy = jest.spyOn(store, 'emit'); + const emitSpy = jest.spyOn(store, "emit"); const oldLiveBeaconIds = store.getLiveBeaconIds(); const bobsLiveBeacon = new Beacon(bobsRoom1BeaconInfo); @@ -701,15 +597,13 @@ describe('OwnBeaconStore', () => { expect(store.getLiveBeaconIds()).toBe(oldLiveBeaconIds); }); - it('updates state and emits beacon liveness changes from true to false', async () => { - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + it("updates state and emits beacon liveness changes from true to false", async () => { + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); // live before expect(store.hasLiveBeacons()).toBe(true); - const emitSpy = jest.spyOn(store, 'emit'); + const emitSpy = jest.spyOn(store, "emit"); const beacon = store.getBeaconById(getBeaconInfoIdentifier(alicesRoom1BeaconInfo)); @@ -722,30 +616,25 @@ describe('OwnBeaconStore', () => { }); }); - describe('stopBeacon()', () => { + describe("stopBeacon()", () => { beforeEach(() => { - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - alicesOldRoomIdBeaconInfo, - ]); + makeRoomsWithStateEvents([alicesRoom1BeaconInfo, alicesOldRoomIdBeaconInfo]); }); - it('does nothing for an unknown beacon id', async () => { + it("does nothing for an unknown beacon id", async () => { const store = await makeOwnBeaconStore(); - await store.stopBeacon('randomBeaconId'); + await store.stopBeacon("randomBeaconId"); expect(mockClient.unstable_setLiveBeacon).not.toHaveBeenCalled(); }); - it('does nothing for a beacon that is already not live', async () => { + it("does nothing for a beacon that is already not live", async () => { const store = await makeOwnBeaconStore(); await store.stopBeacon(getBeaconInfoIdentifier(alicesOldRoomIdBeaconInfo)); expect(mockClient.unstable_setLiveBeacon).not.toHaveBeenCalled(); }); - it('updates beacon to live:false when it is unexpired', async () => { - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + it("updates beacon to live:false when it is unexpired", async () => { + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); const prevEventContent = alicesRoom1BeaconInfo.getContent(); @@ -758,38 +647,33 @@ describe('OwnBeaconStore', () => { ...prevEventContent, live: false, }; - expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledWith( - room1Id, - expectedUpdateContent, - ); + expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledWith(room1Id, expectedUpdateContent); }); - it('records error when stopping beacon event fails to send', async () => { - jest.spyOn(logger, 'error').mockImplementation(() => {}); - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + it("records error when stopping beacon event fails to send", async () => { + jest.spyOn(logger, "error").mockImplementation(() => {}); + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); - const emitSpy = jest.spyOn(store, 'emit'); - const error = new Error('oups'); + const emitSpy = jest.spyOn(store, "emit"); + const error = new Error("oups"); mockClient.unstable_setLiveBeacon.mockRejectedValue(error); await expect(store.stopBeacon(getBeaconInfoIdentifier(alicesRoom1BeaconInfo))).rejects.toEqual(error); expect(store.beaconUpdateErrors.get(getBeaconInfoIdentifier(alicesRoom1BeaconInfo))).toEqual(error); expect(emitSpy).toHaveBeenCalledWith( - OwnBeaconStoreEvent.BeaconUpdateError, getBeaconInfoIdentifier(alicesRoom1BeaconInfo), true, + OwnBeaconStoreEvent.BeaconUpdateError, + getBeaconInfoIdentifier(alicesRoom1BeaconInfo), + true, ); }); - it('clears previous error and emits when stopping beacon works on retry', async () => { - jest.spyOn(logger, 'error').mockImplementation(() => {}); - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + it("clears previous error and emits when stopping beacon works on retry", async () => { + jest.spyOn(logger, "error").mockImplementation(() => {}); + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); - const emitSpy = jest.spyOn(store, 'emit'); - const error = new Error('oups'); + const emitSpy = jest.spyOn(store, "emit"); + const error = new Error("oups"); mockClient.unstable_setLiveBeacon.mockRejectedValueOnce(error); await expect(store.stopBeacon(getBeaconInfoIdentifier(alicesRoom1BeaconInfo))).rejects.toEqual(error); @@ -802,30 +686,30 @@ describe('OwnBeaconStore', () => { // emit called for error clearing expect(emitSpy).toHaveBeenCalledWith( - OwnBeaconStoreEvent.BeaconUpdateError, getBeaconInfoIdentifier(alicesRoom1BeaconInfo), false, + OwnBeaconStoreEvent.BeaconUpdateError, + getBeaconInfoIdentifier(alicesRoom1BeaconInfo), + false, ); }); - it('does not emit BeaconUpdateError when stopping succeeds and beacon did not have errors', async () => { - jest.spyOn(logger, 'error').mockImplementation(() => {}); - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + it("does not emit BeaconUpdateError when stopping succeeds and beacon did not have errors", async () => { + jest.spyOn(logger, "error").mockImplementation(() => {}); + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); - const emitSpy = jest.spyOn(store, 'emit'); + const emitSpy = jest.spyOn(store, "emit"); // error cleared expect(store.beaconUpdateErrors.get(getBeaconInfoIdentifier(alicesRoom1BeaconInfo))).toBeFalsy(); // emit called for error clearing expect(emitSpy).not.toHaveBeenCalledWith( - OwnBeaconStoreEvent.BeaconUpdateError, getBeaconInfoIdentifier(alicesRoom1BeaconInfo), false, + OwnBeaconStoreEvent.BeaconUpdateError, + getBeaconInfoIdentifier(alicesRoom1BeaconInfo), + false, ); }); - it('updates beacon to live:false when it is expired but live property is true', async () => { - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + it("updates beacon to live:false when it is expired but live property is true", async () => { + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); const prevEventContent = alicesRoom1BeaconInfo.getContent(); @@ -841,51 +725,43 @@ describe('OwnBeaconStore', () => { ...prevEventContent, live: false, }; - expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledWith( - room1Id, - expectedUpdateContent, - ); + expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledWith(room1Id, expectedUpdateContent); }); - it('removes beacon event id from local store', async () => { - localStorageGetSpy.mockReturnValue(JSON.stringify([ - alicesRoom1BeaconInfo.getId(), - alicesRoom2BeaconInfo.getId(), - ])); - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + it("removes beacon event id from local store", async () => { + localStorageGetSpy.mockReturnValue( + JSON.stringify([alicesRoom1BeaconInfo.getId(), alicesRoom2BeaconInfo.getId()]), + ); + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); await store.stopBeacon(getBeaconInfoIdentifier(alicesRoom1BeaconInfo)); expect(localStorageSetSpy).toHaveBeenCalledWith( - 'mx_live_beacon_created_id', + "mx_live_beacon_created_id", // stopped beacon's event_id was removed JSON.stringify([alicesRoom2BeaconInfo.getId()]), ); }); }); - describe('publishing positions', () => { + describe("publishing positions", () => { // assume all beacons were created on this device beforeEach(() => { - localStorageGetSpy.mockReturnValue(JSON.stringify([ - alicesRoom1BeaconInfo.getId(), - alicesRoom2BeaconInfo.getId(), - alicesOldRoomIdBeaconInfo.getId(), - 'update-event-id', - ])); + localStorageGetSpy.mockReturnValue( + JSON.stringify([ + alicesRoom1BeaconInfo.getId(), + alicesRoom2BeaconInfo.getId(), + alicesOldRoomIdBeaconInfo.getId(), + "update-event-id", + ]), + ); }); - it('stops watching position when user has no more live beacons', async () => { + it("stops watching position when user has no more live beacons", async () => { // geolocation is only going to emit 1 position - geolocation.watchPosition.mockImplementation( - watchPositionMockImplementation([0]), - ); - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + geolocation.watchPosition.mockImplementation(watchPositionMockImplementation([0])); + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); // wait for store to settle await flushPromisesWithFakeTimers(); @@ -901,11 +777,9 @@ describe('OwnBeaconStore', () => { expect(store.isMonitoringLiveLocation).toEqual(false); }); - describe('when store is initialised with live beacons', () => { - it('starts watching position', async () => { - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + describe("when store is initialised with live beacons", () => { + it("starts watching position", async () => { + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); // wait for store to settle await flushPromisesWithFakeTimers(); @@ -914,48 +788,42 @@ describe('OwnBeaconStore', () => { expect(store.isMonitoringLiveLocation).toEqual(true); }); - it('kills live beacon when geolocation is unavailable', async () => { - const errorLogSpy = jest.spyOn(logger, 'error').mockImplementation(() => { }); + it("kills live beacon when geolocation is unavailable", async () => { + const errorLogSpy = jest.spyOn(logger, "error").mockImplementation(() => {}); // remove the mock we set // @ts-ignore navigator.geolocation = undefined; - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); // wait for store to settle await flushPromisesWithFakeTimers(); expect(store.isMonitoringLiveLocation).toEqual(false); - expect(errorLogSpy).toHaveBeenCalledWith('Geolocation failed', "Unavailable"); + expect(errorLogSpy).toHaveBeenCalledWith("Geolocation failed", "Unavailable"); }); - it('kills live beacon when geolocation permissions are not granted', async () => { + it("kills live beacon when geolocation permissions are not granted", async () => { // similar case to the test above // but these errors are handled differently // above is thrown by element, this passed to error callback by geolocation // return only a permission denied error - geolocation.watchPosition.mockImplementation(watchPositionMockImplementation( - [0], [1]), - ); + geolocation.watchPosition.mockImplementation(watchPositionMockImplementation([0], [1])); - const errorLogSpy = jest.spyOn(logger, 'error').mockImplementation(() => { }); + const errorLogSpy = jest.spyOn(logger, "error").mockImplementation(() => {}); - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); // wait for store to settle await flushPromisesWithFakeTimers(); expect(store.isMonitoringLiveLocation).toEqual(false); - expect(errorLogSpy).toHaveBeenCalledWith('Geolocation failed', "PermissionDenied"); + expect(errorLogSpy).toHaveBeenCalledWith("Geolocation failed", "PermissionDenied"); }); }); - describe('adding a new beacon', () => { - it('publishes position for new beacon immediately', async () => { + describe("adding a new beacon", () => { + it("publishes position for new beacon immediately", async () => { makeRoomsWithStateEvents([]); const store = await makeOwnBeaconStore(); // wait for store to settle @@ -969,8 +837,8 @@ describe('OwnBeaconStore', () => { expect(store.isMonitoringLiveLocation).toEqual(true); }); - it('kills live beacons when geolocation is unavailable', async () => { - jest.spyOn(logger, 'error').mockImplementation(() => { }); + it("kills live beacons when geolocation is unavailable", async () => { + jest.spyOn(logger, "error").mockImplementation(() => {}); // @ts-ignore navigator.geolocation = undefined; makeRoomsWithStateEvents([]); @@ -987,7 +855,7 @@ describe('OwnBeaconStore', () => { expect(store.isMonitoringLiveLocation).toEqual(false); }); - it('publishes position for new beacon immediately when there were already live beacons', async () => { + it("publishes position for new beacon immediately when there were already live beacons", async () => { makeRoomsWithStateEvents([alicesRoom2BeaconInfo]); await makeOwnBeaconStore(); // wait for store to settle @@ -1006,14 +874,14 @@ describe('OwnBeaconStore', () => { }); }); - describe('when publishing position fails', () => { + describe("when publishing position fails", () => { beforeEach(() => { geolocation.watchPosition.mockImplementation( watchPositionMockImplementation([0, 1000, 3000, 3000, 3000]), ); // eat expected console error logs - jest.spyOn(logger, 'error').mockImplementation(() => { }); + jest.spyOn(logger, "error").mockImplementation(() => {}); }); // we need to advance time and then flush promises @@ -1030,12 +898,10 @@ describe('OwnBeaconStore', () => { } }; - it('continues publishing positions after one publish error', async () => { + it("continues publishing positions after one publish error", async () => { // fail to send first event, then succeed - mockClient.sendEvent.mockRejectedValueOnce(new Error('oups')).mockResolvedValue({ event_id: '1' }); - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + mockClient.sendEvent.mockRejectedValueOnce(new Error("oups")).mockResolvedValue({ event_id: "1" }); + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); // wait for store to settle await flushPromisesWithFakeTimers(); @@ -1049,22 +915,20 @@ describe('OwnBeaconStore', () => { expect(store.hasLocationPublishErrors()).toBe(false); }); - it('continues publishing positions when a beacon fails intermittently', async () => { + it("continues publishing positions when a beacon fails intermittently", async () => { // every second event rejects // meaning this beacon has more errors than the threshold // but they are not consecutive mockClient.sendEvent - .mockRejectedValueOnce(new Error('oups')) - .mockResolvedValueOnce({ event_id: '1' }) - .mockRejectedValueOnce(new Error('oups')) - .mockResolvedValueOnce({ event_id: '1' }) - .mockRejectedValueOnce(new Error('oups')); + .mockRejectedValueOnce(new Error("oups")) + .mockResolvedValueOnce({ event_id: "1" }) + .mockRejectedValueOnce(new Error("oups")) + .mockResolvedValueOnce({ event_id: "1" }) + .mockRejectedValueOnce(new Error("oups")); - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); - const emitSpy = jest.spyOn(store, 'emit'); + const emitSpy = jest.spyOn(store, "emit"); // wait for store to settle await flushPromisesWithFakeTimers(); @@ -1075,18 +939,17 @@ describe('OwnBeaconStore', () => { expect(store.beaconHasLocationPublishError(getBeaconInfoIdentifier(alicesRoom1BeaconInfo))).toBe(false); expect(store.hasLocationPublishErrors()).toBe(false); expect(emitSpy).not.toHaveBeenCalledWith( - OwnBeaconStoreEvent.LocationPublishError, getBeaconInfoIdentifier(alicesRoom1BeaconInfo), + OwnBeaconStoreEvent.LocationPublishError, + getBeaconInfoIdentifier(alicesRoom1BeaconInfo), ); }); - it('stops publishing positions when a beacon fails consistently', async () => { + it("stops publishing positions when a beacon fails consistently", async () => { // always fails to send events - mockClient.sendEvent.mockRejectedValue(new Error('oups')); - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + mockClient.sendEvent.mockRejectedValue(new Error("oups")); + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); - const emitSpy = jest.spyOn(store, 'emit'); + const emitSpy = jest.spyOn(store, "emit"); // wait for store to settle await flushPromisesWithFakeTimers(); @@ -1096,25 +959,24 @@ describe('OwnBeaconStore', () => { // only two allowed failures expect(mockClient.sendEvent).toHaveBeenCalledTimes(2); expect(store.beaconHasLocationPublishError(getBeaconInfoIdentifier(alicesRoom1BeaconInfo))).toBe(true); - expect(store.getLiveBeaconIdsWithLocationPublishError()).toEqual( - [getBeaconInfoIdentifier(alicesRoom1BeaconInfo)], - ); - expect(store.getLiveBeaconIdsWithLocationPublishError(room1Id)).toEqual( - [getBeaconInfoIdentifier(alicesRoom1BeaconInfo)], - ); + expect(store.getLiveBeaconIdsWithLocationPublishError()).toEqual([ + getBeaconInfoIdentifier(alicesRoom1BeaconInfo), + ]); + expect(store.getLiveBeaconIdsWithLocationPublishError(room1Id)).toEqual([ + getBeaconInfoIdentifier(alicesRoom1BeaconInfo), + ]); expect(store.hasLocationPublishErrors()).toBe(true); expect(emitSpy).toHaveBeenCalledWith( - OwnBeaconStoreEvent.LocationPublishError, getBeaconInfoIdentifier(alicesRoom1BeaconInfo), + OwnBeaconStoreEvent.LocationPublishError, + getBeaconInfoIdentifier(alicesRoom1BeaconInfo), ); }); - it('stops publishing positions when a beacon has a stopping error', async () => { + it("stops publishing positions when a beacon has a stopping error", async () => { // reject stopping beacon - const error = new Error('oups'); + const error = new Error("oups"); mockClient.unstable_setLiveBeacon.mockRejectedValue(error); - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); // wait for store to settle await flushPromisesWithFakeTimers(); @@ -1133,14 +995,12 @@ describe('OwnBeaconStore', () => { expect(mockClient.sendEvent).toHaveBeenCalledTimes(3); }); - it('restarts publishing a beacon after resetting location publish error', async () => { + it("restarts publishing a beacon after resetting location publish error", async () => { // always fails to send events - mockClient.sendEvent.mockRejectedValue(new Error('oups')); - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + mockClient.sendEvent.mockRejectedValue(new Error("oups")); + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); const store = await makeOwnBeaconStore(); - const emitSpy = jest.spyOn(store, 'emit'); + const emitSpy = jest.spyOn(store, "emit"); // wait for store to settle await flushPromisesWithFakeTimers(); @@ -1153,7 +1013,8 @@ describe('OwnBeaconStore', () => { expect(store.hasLocationPublishErrors()).toBe(true); expect(store.hasLocationPublishErrors(room1Id)).toBe(true); expect(emitSpy).toHaveBeenCalledWith( - OwnBeaconStoreEvent.LocationPublishError, getBeaconInfoIdentifier(alicesRoom1BeaconInfo), + OwnBeaconStoreEvent.LocationPublishError, + getBeaconInfoIdentifier(alicesRoom1BeaconInfo), ); // reset emitSpy mock counts to assert on locationPublishError again @@ -1168,23 +1029,20 @@ describe('OwnBeaconStore', () => { // 2 from before, 2 new ones expect(mockClient.sendEvent).toHaveBeenCalledTimes(4); expect(emitSpy).toHaveBeenCalledWith( - OwnBeaconStoreEvent.LocationPublishError, getBeaconInfoIdentifier(alicesRoom1BeaconInfo), + OwnBeaconStoreEvent.LocationPublishError, + getBeaconInfoIdentifier(alicesRoom1BeaconInfo), ); }); }); - it('publishes subsequent positions', async () => { + it("publishes subsequent positions", async () => { // modern fake timers + debounce + promises are not friends // just testing that positions are published // not that the debounce works - geolocation.watchPosition.mockImplementation( - watchPositionMockImplementation([0, 1000, 3000]), - ); + geolocation.watchPosition.mockImplementation(watchPositionMockImplementation([0, 1000, 3000])); - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); expect(mockClient.sendEvent).toHaveBeenCalledTimes(0); await makeOwnBeaconStore(); // wait for store to settle @@ -1195,16 +1053,12 @@ describe('OwnBeaconStore', () => { expect(mockClient.sendEvent).toHaveBeenCalledTimes(3); }); - it('stops live beacons when geolocation permissions are revoked', async () => { - jest.spyOn(logger, 'error').mockImplementation(() => { }); + it("stops live beacons when geolocation permissions are revoked", async () => { + jest.spyOn(logger, "error").mockImplementation(() => {}); // return two good positions, then a permission denied error - geolocation.watchPosition.mockImplementation(watchPositionMockImplementation( - [0, 1000, 3000], [0, 0, 1]), - ); + geolocation.watchPosition.mockImplementation(watchPositionMockImplementation([0, 1000, 3000], [0, 0, 1])); - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); expect(mockClient.sendEvent).toHaveBeenCalledTimes(0); const store = await makeOwnBeaconStore(); // wait for store to settle @@ -1220,16 +1074,12 @@ describe('OwnBeaconStore', () => { expect(store.isMonitoringLiveLocation).toEqual(false); }); - it('keeps sharing positions when geolocation has a non fatal error', async () => { - const errorLogSpy = jest.spyOn(logger, 'error').mockImplementation(() => { }); + it("keeps sharing positions when geolocation has a non fatal error", async () => { + const errorLogSpy = jest.spyOn(logger, "error").mockImplementation(() => {}); // return good position, timeout error, good position - geolocation.watchPosition.mockImplementation(watchPositionMockImplementation( - [0, 1000, 3000], [0, 3, 0]), - ); + geolocation.watchPosition.mockImplementation(watchPositionMockImplementation([0, 1000, 3000], [0, 3, 0])); - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); expect(mockClient.sendEvent).toHaveBeenCalledTimes(0); const store = await makeOwnBeaconStore(); // wait for store to settle @@ -1243,17 +1093,13 @@ describe('OwnBeaconStore', () => { // still sharing expect(mockClient.unstable_setLiveBeacon).not.toHaveBeenCalled(); expect(store.isMonitoringLiveLocation).toEqual(true); - expect(errorLogSpy).toHaveBeenCalledWith('Geolocation failed', 'error message'); + expect(errorLogSpy).toHaveBeenCalledWith("Geolocation failed", "error message"); }); - it('publishes last known position after 30s of inactivity', async () => { - geolocation.watchPosition.mockImplementation( - watchPositionMockImplementation([0]), - ); + it("publishes last known position after 30s of inactivity", async () => { + geolocation.watchPosition.mockImplementation(watchPositionMockImplementation([0])); - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); await makeOwnBeaconStore(); // wait for store to settle await flushPromisesWithFakeTimers(); @@ -1268,18 +1114,12 @@ describe('OwnBeaconStore', () => { expect(mockClient.sendEvent).toHaveBeenCalledTimes(2); }); - it('does not try to publish anything if there is no known position after 30s of inactivity', async () => { + it("does not try to publish anything if there is no known position after 30s of inactivity", async () => { // no position ever returned from geolocation - geolocation.watchPosition.mockImplementation( - watchPositionMockImplementation([]), - ); - geolocation.getCurrentPosition.mockImplementation( - watchPositionMockImplementation([]), - ); + geolocation.watchPosition.mockImplementation(watchPositionMockImplementation([])); + geolocation.getCurrentPosition.mockImplementation(watchPositionMockImplementation([])); - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - ]); + makeRoomsWithStateEvents([alicesRoom1BeaconInfo]); await makeOwnBeaconStore(); // wait for store to settle await flushPromisesWithFakeTimers(); @@ -1291,56 +1131,46 @@ describe('OwnBeaconStore', () => { }); }); - describe('createLiveBeacon', () => { - const newEventId = 'new-beacon-event-id'; - const loggerErrorSpy = jest.spyOn(logger, 'error').mockImplementation(() => {}); + describe("createLiveBeacon", () => { + const newEventId = "new-beacon-event-id"; + const loggerErrorSpy = jest.spyOn(logger, "error").mockImplementation(() => {}); beforeEach(() => { - localStorageGetSpy.mockReturnValue(JSON.stringify([ - alicesRoom1BeaconInfo.getId(), - ])); + localStorageGetSpy.mockReturnValue(JSON.stringify([alicesRoom1BeaconInfo.getId()])); localStorageSetSpy.mockClear(); mockClient.unstable_createLiveBeacon.mockResolvedValue({ event_id: newEventId }); }); - it('creates a live beacon', async () => { + it("creates a live beacon", async () => { const store = await makeOwnBeaconStore(); const content = makeBeaconInfoContent(100); await store.createLiveBeacon(room1Id, content); expect(mockClient.unstable_createLiveBeacon).toHaveBeenCalledWith(room1Id, content); }); - it('sets new beacon event id in local storage', async () => { + it("sets new beacon event id in local storage", async () => { const store = await makeOwnBeaconStore(); const content = makeBeaconInfoContent(100); await store.createLiveBeacon(room1Id, content); expect(localStorageSetSpy).toHaveBeenCalledWith( - 'mx_live_beacon_created_id', - JSON.stringify([ - alicesRoom1BeaconInfo.getId(), - newEventId, - ]), + "mx_live_beacon_created_id", + JSON.stringify([alicesRoom1BeaconInfo.getId(), newEventId]), ); }); - it('handles saving beacon event id when local storage has bad value', async () => { - localStorageGetSpy.mockReturnValue(JSON.stringify({ id: '1' })); + it("handles saving beacon event id when local storage has bad value", async () => { + localStorageGetSpy.mockReturnValue(JSON.stringify({ id: "1" })); const store = await makeOwnBeaconStore(); const content = makeBeaconInfoContent(100); await store.createLiveBeacon(room1Id, content); // stored successfully - expect(localStorageSetSpy).toHaveBeenCalledWith( - 'mx_live_beacon_created_id', - JSON.stringify([ - newEventId, - ]), - ); + expect(localStorageSetSpy).toHaveBeenCalledWith("mx_live_beacon_created_id", JSON.stringify([newEventId])); }); - it('creates a live beacon without error when no beacons exist for room', async () => { + it("creates a live beacon without error when no beacons exist for room", async () => { const store = await makeOwnBeaconStore(); const content = makeBeaconInfoContent(100); await store.createLiveBeacon(room1Id, content); @@ -1349,12 +1179,9 @@ describe('OwnBeaconStore', () => { expect(loggerErrorSpy).not.toHaveBeenCalled(); }); - it('stops existing live beacon for room before creates new beacon', async () => { + it("stops existing live beacon for room before creates new beacon", async () => { // room1 already has a live beacon for alice - makeRoomsWithStateEvents([ - alicesRoom1BeaconInfo, - alicesRoom2BeaconInfo, - ]); + makeRoomsWithStateEvents([alicesRoom1BeaconInfo, alicesRoom2BeaconInfo]); const store = await makeOwnBeaconStore(); const content = makeBeaconInfoContent(100); @@ -1362,15 +1189,14 @@ describe('OwnBeaconStore', () => { // stop alicesRoom1BeaconInfo expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledWith( - room1Id, expect.objectContaining({ live: false }), + room1Id, + expect.objectContaining({ live: false }), ); // only called for beacons in room1, room2 beacon is not stopped expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledTimes(1); // new beacon created - expect(mockClient.unstable_createLiveBeacon).toHaveBeenCalledWith( - room1Id, content, - ); + expect(mockClient.unstable_createLiveBeacon).toHaveBeenCalledWith(room1Id, content); }); }); }); diff --git a/test/stores/RoomViewStore-test.ts b/test/stores/RoomViewStore-test.ts index 5f1bb98d3d..6103c6bb46 100644 --- a/test/stores/RoomViewStore-test.ts +++ b/test/stores/RoomViewStore-test.ts @@ -14,30 +14,30 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { Room } from 'matrix-js-sdk/src/matrix'; +import { Room } from "matrix-js-sdk/src/matrix"; -import { RoomViewStore } from '../../src/stores/RoomViewStore'; -import { Action } from '../../src/dispatcher/actions'; -import { getMockClientWithEventEmitter, untilDispatch, untilEmission } from '../test-utils'; -import SettingsStore from '../../src/settings/SettingsStore'; -import { SlidingSyncManager } from '../../src/SlidingSyncManager'; -import { PosthogAnalytics } from '../../src/PosthogAnalytics'; -import { TimelineRenderingType } from '../../src/contexts/RoomContext'; -import { MatrixDispatcher } from '../../src/dispatcher/dispatcher'; -import { UPDATE_EVENT } from '../../src/stores/AsyncStore'; -import { ActiveRoomChangedPayload } from '../../src/dispatcher/payloads/ActiveRoomChangedPayload'; -import { SpaceStoreClass } from '../../src/stores/spaces/SpaceStore'; -import { TestSdkContext } from '../TestSdkContext'; +import { RoomViewStore } from "../../src/stores/RoomViewStore"; +import { Action } from "../../src/dispatcher/actions"; +import { getMockClientWithEventEmitter, untilDispatch, untilEmission } from "../test-utils"; +import SettingsStore from "../../src/settings/SettingsStore"; +import { SlidingSyncManager } from "../../src/SlidingSyncManager"; +import { PosthogAnalytics } from "../../src/PosthogAnalytics"; +import { TimelineRenderingType } from "../../src/contexts/RoomContext"; +import { MatrixDispatcher } from "../../src/dispatcher/dispatcher"; +import { UPDATE_EVENT } from "../../src/stores/AsyncStore"; +import { ActiveRoomChangedPayload } from "../../src/dispatcher/payloads/ActiveRoomChangedPayload"; +import { SpaceStoreClass } from "../../src/stores/spaces/SpaceStore"; +import { TestSdkContext } from "../TestSdkContext"; // mock out the injected classes -jest.mock('../../src/PosthogAnalytics'); -const MockPosthogAnalytics = >PosthogAnalytics; -jest.mock('../../src/SlidingSyncManager'); -const MockSlidingSyncManager = >SlidingSyncManager; -jest.mock('../../src/stores/spaces/SpaceStore'); -const MockSpaceStore = >SpaceStoreClass; +jest.mock("../../src/PosthogAnalytics"); +const MockPosthogAnalytics = >(PosthogAnalytics); +jest.mock("../../src/SlidingSyncManager"); +const MockSlidingSyncManager = >(SlidingSyncManager); +jest.mock("../../src/stores/spaces/SpaceStore"); +const MockSpaceStore = >(SpaceStoreClass); -jest.mock('../../src/utils/DMRoomMap', () => { +jest.mock("../../src/utils/DMRoomMap", () => { const mock = { getUserIdForRoomId: jest.fn(), getDMRoomsForUserId: jest.fn(), @@ -49,8 +49,8 @@ jest.mock('../../src/utils/DMRoomMap', () => { }; }); -describe('RoomViewStore', function() { - const userId = '@alice:server'; +describe("RoomViewStore", function () { + const userId = "@alice:server"; const roomId = "!randomcharacters:aser.ver"; // we need to change the alias to ensure cache misses as the cache exists // through all tests. @@ -67,7 +67,7 @@ describe('RoomViewStore', function() { let slidingSyncManager: SlidingSyncManager; let dis: MatrixDispatcher; - beforeEach(function() { + beforeEach(function () { jest.clearAllMocks(); mockClient.credentials = { userId: userId }; mockClient.joinRoom.mockResolvedValue(room); @@ -81,13 +81,11 @@ describe('RoomViewStore', function() { stores._SlidingSyncManager = slidingSyncManager; stores._PosthogAnalytics = new MockPosthogAnalytics(); stores._SpaceStore = new MockSpaceStore(); - roomViewStore = new RoomViewStore( - dis, stores, - ); + roomViewStore = new RoomViewStore(dis, stores); stores._RoomViewStore = roomViewStore; }); - it('can be used to view a room by ID and join', async () => { + it("can be used to view a room by ID and join", async () => { dis.dispatch({ action: Action.ViewRoom, room_id: roomId }); dis.dispatch({ action: Action.JoinRoom }); await untilDispatch(Action.JoinRoomReady, dis); @@ -95,44 +93,45 @@ describe('RoomViewStore', function() { expect(roomViewStore.isJoining()).toBe(true); }); - it('can auto-join a room', async () => { + it("can auto-join a room", async () => { dis.dispatch({ action: Action.ViewRoom, room_id: roomId, auto_join: true }); await untilDispatch(Action.JoinRoomReady, dis); expect(mockClient.joinRoom).toHaveBeenCalledWith(roomId, { viaServers: [] }); expect(roomViewStore.isJoining()).toBe(true); }); - it('emits ActiveRoomChanged when the viewed room changes', async () => { + it("emits ActiveRoomChanged when the viewed room changes", async () => { const roomId2 = "!roomid:2"; dis.dispatch({ action: Action.ViewRoom, room_id: roomId }); - let payload = await untilDispatch(Action.ActiveRoomChanged, dis) as ActiveRoomChangedPayload; + let payload = (await untilDispatch(Action.ActiveRoomChanged, dis)) as ActiveRoomChangedPayload; expect(payload.newRoomId).toEqual(roomId); expect(payload.oldRoomId).toEqual(null); dis.dispatch({ action: Action.ViewRoom, room_id: roomId2 }); - payload = await untilDispatch(Action.ActiveRoomChanged, dis) as ActiveRoomChangedPayload; + payload = (await untilDispatch(Action.ActiveRoomChanged, dis)) as ActiveRoomChangedPayload; expect(payload.newRoomId).toEqual(roomId2); expect(payload.oldRoomId).toEqual(roomId); }); - it('invokes room activity listeners when the viewed room changes', async () => { + it("invokes room activity listeners when the viewed room changes", async () => { const roomId2 = "!roomid:2"; const callback = jest.fn(); roomViewStore.addRoomListener(roomId, callback); dis.dispatch({ action: Action.ViewRoom, room_id: roomId }); - await untilDispatch(Action.ActiveRoomChanged, dis) as ActiveRoomChangedPayload; + (await untilDispatch(Action.ActiveRoomChanged, dis)) as ActiveRoomChangedPayload; expect(callback).toHaveBeenCalledWith(true); expect(callback).not.toHaveBeenCalledWith(false); dis.dispatch({ action: Action.ViewRoom, room_id: roomId2 }); - await untilDispatch(Action.ActiveRoomChanged, dis) as ActiveRoomChangedPayload; + (await untilDispatch(Action.ActiveRoomChanged, dis)) as ActiveRoomChangedPayload; expect(callback).toHaveBeenCalledWith(false); }); - it('can be used to view a room by alias and join', async () => { + it("can be used to view a room by alias and join", async () => { mockClient.getRoomIdForAlias.mockResolvedValue({ room_id: roomId, servers: [] }); dis.dispatch({ action: Action.ViewRoom, room_alias: alias }); - await untilDispatch((p) => { // wait for the re-dispatch with the room ID + await untilDispatch((p) => { + // wait for the re-dispatch with the room ID return p.action === Action.ViewRoom && p.room_id === roomId; }, dis); @@ -148,7 +147,7 @@ describe('RoomViewStore', function() { expect(mockClient.joinRoom).toHaveBeenCalledWith(alias, { viaServers: [] }); }); - it('emits ViewRoomError if the alias lookup fails', async () => { + it("emits ViewRoomError if the alias lookup fails", async () => { alias = "#something-different:to-ensure-cache-miss"; mockClient.getRoomIdForAlias.mockRejectedValue(new Error("network error or something")); dis.dispatch({ action: Action.ViewRoom, room_alias: alias }); @@ -158,7 +157,7 @@ describe('RoomViewStore', function() { expect(roomViewStore.getRoomAlias()).toEqual(alias); }); - it('emits JoinRoomError if joining the room fails', async () => { + it("emits JoinRoomError if joining the room fails", async () => { const joinErr = new Error("network error or something"); mockClient.joinRoom.mockRejectedValue(joinErr); dis.dispatch({ action: Action.ViewRoom, room_id: roomId }); @@ -168,13 +167,13 @@ describe('RoomViewStore', function() { expect(roomViewStore.getJoinError()).toEqual(joinErr); }); - it('remembers the event being replied to when swapping rooms', async () => { + it("remembers the event being replied to when swapping rooms", async () => { dis.dispatch({ action: Action.ViewRoom, room_id: roomId }); await untilDispatch(Action.ActiveRoomChanged, dis); const replyToEvent = { getRoomId: () => roomId, }; - dis.dispatch({ action: 'reply_to_event', event: replyToEvent, context: TimelineRenderingType.Room }); + dis.dispatch({ action: "reply_to_event", event: replyToEvent, context: TimelineRenderingType.Room }); await untilEmission(roomViewStore, UPDATE_EVENT); expect(roomViewStore.getQuotingEvent()).toEqual(replyToEvent); // view the same room, should remember the event. @@ -184,20 +183,20 @@ describe('RoomViewStore', function() { expect(roomViewStore.getQuotingEvent()).toEqual(replyToEvent); }); - it('swaps to the replied event room if it is not the current room', async () => { + it("swaps to the replied event room if it is not the current room", async () => { const roomId2 = "!room2:bar"; dis.dispatch({ action: Action.ViewRoom, room_id: roomId }); await untilDispatch(Action.ActiveRoomChanged, dis); const replyToEvent = { getRoomId: () => roomId2, }; - dis.dispatch({ action: 'reply_to_event', event: replyToEvent, context: TimelineRenderingType.Room }); + dis.dispatch({ action: "reply_to_event", event: replyToEvent, context: TimelineRenderingType.Room }); await untilDispatch(Action.ViewRoom, dis); expect(roomViewStore.getQuotingEvent()).toEqual(replyToEvent); expect(roomViewStore.getRoomId()).toEqual(roomId2); }); - it('removes the roomId on ViewHomePage', async () => { + it("removes the roomId on ViewHomePage", async () => { dis.dispatch({ action: Action.ViewRoom, room_id: roomId }); await untilDispatch(Action.ActiveRoomChanged, dis); expect(roomViewStore.getRoomId()).toEqual(roomId); @@ -207,17 +206,17 @@ describe('RoomViewStore', function() { expect(roomViewStore.getRoomId()).toBeNull(); }); - describe('Sliding Sync', function() { + describe("Sliding Sync", function () { beforeEach(() => { - jest.spyOn(SettingsStore, 'getValue').mockImplementation((settingName, roomId, value) => { + jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName, roomId, value) => { return settingName === "feature_sliding_sync"; // this is enabled, everything else is disabled. }); }); it("subscribes to the room", async () => { - const setRoomVisible = jest.spyOn(slidingSyncManager, "setRoomVisible").mockReturnValue( - Promise.resolve(""), - ); + const setRoomVisible = jest + .spyOn(slidingSyncManager, "setRoomVisible") + .mockReturnValue(Promise.resolve("")); const subscribedRoomId = "!sub1:localhost"; dis.dispatch({ action: Action.ViewRoom, room_id: subscribedRoomId }); await untilDispatch(Action.ActiveRoomChanged, dis); @@ -227,9 +226,9 @@ describe('RoomViewStore', function() { // Regression test for an in-the-wild bug where rooms would rapidly switch forever in sliding sync mode it("doesn't get stuck in a loop if you view rooms quickly", async () => { - const setRoomVisible = jest.spyOn(slidingSyncManager, "setRoomVisible").mockReturnValue( - Promise.resolve(""), - ); + const setRoomVisible = jest + .spyOn(slidingSyncManager, "setRoomVisible") + .mockReturnValue(Promise.resolve("")); const subscribedRoomId = "!sub1:localhost"; const subscribedRoomId2 = "!sub2:localhost"; dis.dispatch({ action: Action.ViewRoom, room_id: subscribedRoomId }, true); diff --git a/test/stores/SpaceStore-test.ts b/test/stores/SpaceStore-test.ts index d83bc21f22..ab6db4b767 100644 --- a/test/stores/SpaceStore-test.ts +++ b/test/stores/SpaceStore-test.ts @@ -15,12 +15,12 @@ limitations under the License. */ import { EventEmitter } from "events"; -import { mocked } from 'jest-mock'; +import { mocked } from "jest-mock"; import { EventType } from "matrix-js-sdk/src/@types/event"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state"; import { defer } from "matrix-js-sdk/src/utils"; -import { ClientEvent, RoomEvent, MatrixEvent } from 'matrix-js-sdk/src/matrix'; +import { ClientEvent, RoomEvent, MatrixEvent } from "matrix-js-sdk/src/matrix"; import SpaceStore from "../../src/stores/spaces/SpaceStore"; import { @@ -68,14 +68,14 @@ const space2 = "!space2:server"; const space3 = "!space3:server"; const space4 = "!space4:server"; -const getUserIdForRoomId = jest.fn(roomId => { +const getUserIdForRoomId = jest.fn((roomId) => { return { [dm1]: dm1Partner.userId, [dm2]: dm2Partner.userId, [dm3]: dm3Partner.userId, }[roomId]; }); -const getDMRoomsForUserId = jest.fn(userId => { +const getDMRoomsForUserId = jest.fn((userId) => { switch (userId) { case dm1Partner.userId: return [dm1]; @@ -100,11 +100,13 @@ describe("SpaceStore", () => { let rooms = []; const mkRoom = (roomId: string) => testUtils.mkRoom(client, roomId, rooms); const mkSpace = (spaceId: string, children: string[] = []) => testUtils.mkSpace(client, spaceId, rooms, children); - const viewRoom = roomId => defaultDispatcher.dispatch({ action: Action.ViewRoom, room_id: roomId }, true); + const viewRoom = (roomId) => defaultDispatcher.dispatch({ action: Action.ViewRoom, room_id: roomId }, true); const run = async () => { - mocked(client).getRoom.mockImplementation(roomId => rooms.find(room => room.roomId === roomId)); - mocked(client).getRoomUpgradeHistory.mockImplementation(roomId => [rooms.find(room => room.roomId === roomId)]); + mocked(client).getRoom.mockImplementation((roomId) => rooms.find((room) => room.roomId === roomId)); + mocked(client).getRoomUpgradeHistory.mockImplementation((roomId) => [ + rooms.find((room) => room.roomId === roomId), + ]); await testUtils.setupAsyncStoreWithClient(store, client); jest.runOnlyPendingTimers(); }; @@ -119,7 +121,7 @@ describe("SpaceStore", () => { beforeEach(async () => { jest.runOnlyPendingTimers(); // run async dispatch - mocked(client).getVisibleRooms.mockReturnValue(rooms = []); + mocked(client).getVisibleRooms.mockReturnValue((rooms = [])); await SettingsStore.setValue("Spaces.enabledMetaSpaces", null, SettingLevel.DEVICE, { [MetaSpace.Home]: true, @@ -157,18 +159,14 @@ describe("SpaceStore", () => { mkSpace("!space1:server"); mkSpace("!space2:server"); mkSpace("!company:server", [ - mkSpace("!company_dept1:server", [ - mkSpace("!company_dept1_group1:server").roomId, - ]).roomId, + mkSpace("!company_dept1:server", [mkSpace("!company_dept1_group1:server").roomId]).roomId, mkSpace("!company_dept2:server").roomId, ]); await run(); - expect(store.spacePanelSpaces.map(r => r.roomId).sort()).toStrictEqual([ - "!space1:server", - "!space2:server", - "!company:server", - ].sort()); + expect(store.spacePanelSpaces.map((r) => r.roomId).sort()).toStrictEqual( + ["!space1:server", "!space2:server", "!company:server"].sort(), + ); expect(store.invitedSpaces).toStrictEqual([]); expect(store.getChildRooms("!space1:server")).toStrictEqual([]); @@ -195,19 +193,16 @@ describe("SpaceStore", () => { mkSpace("!space1:server"); mkSpace("!space2:server"); mkSpace("!company:server", [ - mkSpace("!company_dept1:server", [ - mkSpace("!company_dept1_group1:server", [subspace.roomId]).roomId, - ]).roomId, + mkSpace("!company_dept1:server", [mkSpace("!company_dept1_group1:server", [subspace.roomId]).roomId]) + .roomId, mkSpace("!company_dept2:server", [subspace.roomId]).roomId, subspace.roomId, ]); await run(); - expect(store.spacePanelSpaces.map(r => r.roomId).sort()).toStrictEqual([ - "!space1:server", - "!space2:server", - "!company:server", - ].sort()); + expect(store.spacePanelSpaces.map((r) => r.roomId).sort()).toStrictEqual( + ["!space1:server", "!space2:server", "!company:server"].sort(), + ); expect(store.invitedSpaces).toStrictEqual([]); expect(store.getChildRooms("!space1:server")).toStrictEqual([]); @@ -231,16 +226,10 @@ describe("SpaceStore", () => { }); it("handles full cycles", async () => { - mkSpace("!a:server", [ - mkSpace("!b:server", [ - mkSpace("!c:server", [ - "!a:server", - ]).roomId, - ]).roomId, - ]); + mkSpace("!a:server", [mkSpace("!b:server", [mkSpace("!c:server", ["!a:server"]).roomId]).roomId]); await run(); - expect(store.spacePanelSpaces.map(r => r.roomId)).toStrictEqual(["!a:server"]); + expect(store.spacePanelSpaces.map((r) => r.roomId)).toStrictEqual(["!a:server"]); expect(store.invitedSpaces).toStrictEqual([]); expect(store.getChildRooms("!a:server")).toStrictEqual([]); @@ -252,16 +241,10 @@ describe("SpaceStore", () => { }); it("handles partial cycles", async () => { - mkSpace("!b:server", [ - mkSpace("!a:server", [ - mkSpace("!c:server", [ - "!a:server", - ]).roomId, - ]).roomId, - ]); + mkSpace("!b:server", [mkSpace("!a:server", [mkSpace("!c:server", ["!a:server"]).roomId]).roomId]); await run(); - expect(store.spacePanelSpaces.map(r => r.roomId)).toStrictEqual(["!b:server"]); + expect(store.spacePanelSpaces.map((r) => r.roomId)).toStrictEqual(["!b:server"]); expect(store.invitedSpaces).toStrictEqual([]); expect(store.getChildRooms("!b:server")).toStrictEqual([]); @@ -275,16 +258,11 @@ describe("SpaceStore", () => { it("handles partial cycles with additional spaces coming off them", async () => { // TODO this test should be failing right now mkSpace("!a:server", [ - mkSpace("!b:server", [ - mkSpace("!c:server", [ - "!a:server", - mkSpace("!d:server").roomId, - ]).roomId, - ]).roomId, + mkSpace("!b:server", [mkSpace("!c:server", ["!a:server", mkSpace("!d:server").roomId]).roomId]).roomId, ]); await run(); - expect(store.spacePanelSpaces.map(r => r.roomId)).toStrictEqual(["!a:server"]); + expect(store.spacePanelSpaces.map((r) => r.roomId)).toStrictEqual(["!a:server"]); expect(store.invitedSpaces).toStrictEqual([]); expect(store.getChildRooms("!a:server")).toStrictEqual([]); @@ -313,16 +291,30 @@ describe("SpaceStore", () => { describe("test fixture 1", () => { beforeEach(async () => { - [fav1, fav2, fav3, dm1, dm2, dm3, orphan1, orphan2, invite1, invite2, room1, room2, room3, room4] - .forEach(mkRoom); + [ + fav1, + fav2, + fav3, + dm1, + dm2, + dm3, + orphan1, + orphan2, + invite1, + invite2, + room1, + room2, + room3, + room4, + ].forEach(mkRoom); mkSpace(space1, [fav1, room1]); mkSpace(space2, [fav1, fav2, fav3, room1]); mkSpace(space3, [invite2]); mkSpace(space4, [room4, fav2, space2, space3]); - mocked(client).getRoom.mockImplementation(roomId => rooms.find(room => room.roomId === roomId)); + mocked(client).getRoom.mockImplementation((roomId) => rooms.find((room) => room.roomId === roomId)); - [fav1, fav2, fav3].forEach(roomId => { + [fav1, fav2, fav3].forEach((roomId) => { client.getRoom(roomId).tags = { "m.favourite": { order: 0.5, @@ -330,21 +322,21 @@ describe("SpaceStore", () => { }; }); - [invite1, invite2].forEach(roomId => { + [invite1, invite2].forEach((roomId) => { mocked(client.getRoom(roomId)).getMyMembership.mockReturnValue("invite"); }); // have dmPartner1 be in space1 with you const mySpace1Member = new RoomMember(space1, testUserId); mySpace1Member.membership = "join"; - (rooms.find(r => r.roomId === space1).getMembers as jest.Mock).mockReturnValue([ + (rooms.find((r) => r.roomId === space1).getMembers as jest.Mock).mockReturnValue([ mySpace1Member, dm1Partner, ]); // have dmPartner2 be in space2 with you const mySpace2Member = new RoomMember(space2, testUserId); mySpace2Member.membership = "join"; - (rooms.find(r => r.roomId === space2).getMembers as jest.Mock).mockReturnValue([ + (rooms.find((r) => r.roomId === space2).getMembers as jest.Mock).mockReturnValue([ mySpace2Member, dm2Partner, ]); @@ -371,7 +363,8 @@ describe("SpaceStore", () => { return userId === client.getUserId(); } return true; - }); + }, + ); // room 3 claims to be a child of space3 but is not due to invalid m.space.parent (permissions) const cliRoom3 = client.getRoom(room3); @@ -386,7 +379,8 @@ describe("SpaceStore", () => { content: { via: [], canonical: true }, ts: Date.now(), }), - ])); + ]), + ); const cliSpace3 = client.getRoom(space3); mocked(cliSpace3.currentState).maySendStateEvent.mockImplementation( (evType: string, userId: string) => { @@ -394,12 +388,13 @@ describe("SpaceStore", () => { return false; } return true; - }); + }, + ); await run(); }); - describe('isRoomInSpace()', () => { + describe("isRoomInSpace()", () => { it("home space contains orphaned rooms", () => { expect(store.isRoomInSpace(MetaSpace.Home, orphan1)).toBeTruthy(); expect(store.isRoomInSpace(MetaSpace.Home, orphan2)).toBeTruthy(); @@ -425,12 +420,10 @@ describe("SpaceStore", () => { expect(store.isRoomInSpace(MetaSpace.Home, invite2)).toBeTruthy(); }); - it( - "all rooms space does contain rooms/low priority even if they are also shown in a space", - async () => { - await setShowAllRooms(true); - expect(store.isRoomInSpace(MetaSpace.Home, room1)).toBeTruthy(); - }); + it("all rooms space does contain rooms/low priority even if they are also shown in a space", async () => { + await setShowAllRooms(true); + expect(store.isRoomInSpace(MetaSpace.Home, room1)).toBeTruthy(); + }); it("favourites space does contain favourites even if they are also shown in a space", async () => { expect(store.isRoomInSpace(MetaSpace.Favourites, fav1)).toBeTruthy(); @@ -528,7 +521,7 @@ describe("SpaceStore", () => { expect(store.isRoomInSpace(space3, dm3)).toBeFalsy(); }); - it('uses cached aggregated rooms', () => { + it("uses cached aggregated rooms", () => { const rooms = store.getSpaceFilteredRoomIds(space4, true); expect(store.isRoomInSpace(space4, fav1)).toBeTruthy(); expect(store.isRoomInSpace(space4, fav3)).toBeTruthy(); @@ -540,26 +533,44 @@ describe("SpaceStore", () => { }); it("dms are only added to Notification States for only the People Space", async () => { - [dm1, dm2, dm3].forEach(d => { - expect(store.getNotificationState(MetaSpace.People) - .rooms.map(r => r.roomId).includes(d)).toBeTruthy(); + [dm1, dm2, dm3].forEach((d) => { + expect( + store + .getNotificationState(MetaSpace.People) + .rooms.map((r) => r.roomId) + .includes(d), + ).toBeTruthy(); }); - [space1, space2, space3, MetaSpace.Home, MetaSpace.Orphans, MetaSpace.Favourites].forEach(s => { - [dm1, dm2, dm3].forEach(d => { - expect(store.getNotificationState(s).rooms.map(r => r.roomId).includes(d)).toBeFalsy(); + [space1, space2, space3, MetaSpace.Home, MetaSpace.Orphans, MetaSpace.Favourites].forEach((s) => { + [dm1, dm2, dm3].forEach((d) => { + expect( + store + .getNotificationState(s) + .rooms.map((r) => r.roomId) + .includes(d), + ).toBeFalsy(); }); }); }); it("orphan rooms are added to Notification States for only the Home Space", async () => { await setShowAllRooms(false); - [orphan1, orphan2].forEach(d => { - expect(store.getNotificationState(MetaSpace.Home) - .rooms.map(r => r.roomId).includes(d)).toBeTruthy(); + [orphan1, orphan2].forEach((d) => { + expect( + store + .getNotificationState(MetaSpace.Home) + .rooms.map((r) => r.roomId) + .includes(d), + ).toBeTruthy(); }); - [space1, space2, space3].forEach(s => { - [orphan1, orphan2].forEach(d => { - expect(store.getNotificationState(s).rooms.map(r => r.roomId).includes(d)).toBeFalsy(); + [space1, space2, space3].forEach((s) => { + [orphan1, orphan2].forEach((d) => { + expect( + store + .getNotificationState(s) + .rooms.map((r) => r.roomId) + .includes(d), + ).toBeFalsy(); }); }); }); @@ -569,23 +580,83 @@ describe("SpaceStore", () => { // [fav1, fav2, fav3].forEach(d => { // expect(store.getNotificationState(HOME_SPACE).rooms.map(r => r.roomId).includes(d)).toBeTruthy(); // }); - expect(store.getNotificationState(space1).rooms.map(r => r.roomId).includes(fav1)).toBeTruthy(); - expect(store.getNotificationState(space1).rooms.map(r => r.roomId).includes(fav2)).toBeFalsy(); - expect(store.getNotificationState(space1).rooms.map(r => r.roomId).includes(fav3)).toBeFalsy(); - expect(store.getNotificationState(space2).rooms.map(r => r.roomId).includes(fav1)).toBeTruthy(); - expect(store.getNotificationState(space2).rooms.map(r => r.roomId).includes(fav2)).toBeTruthy(); - expect(store.getNotificationState(space2).rooms.map(r => r.roomId).includes(fav3)).toBeTruthy(); - expect(store.getNotificationState(space3).rooms.map(r => r.roomId).includes(fav1)).toBeFalsy(); - expect(store.getNotificationState(space3).rooms.map(r => r.roomId).includes(fav2)).toBeFalsy(); - expect(store.getNotificationState(space3).rooms.map(r => r.roomId).includes(fav3)).toBeFalsy(); + expect( + store + .getNotificationState(space1) + .rooms.map((r) => r.roomId) + .includes(fav1), + ).toBeTruthy(); + expect( + store + .getNotificationState(space1) + .rooms.map((r) => r.roomId) + .includes(fav2), + ).toBeFalsy(); + expect( + store + .getNotificationState(space1) + .rooms.map((r) => r.roomId) + .includes(fav3), + ).toBeFalsy(); + expect( + store + .getNotificationState(space2) + .rooms.map((r) => r.roomId) + .includes(fav1), + ).toBeTruthy(); + expect( + store + .getNotificationState(space2) + .rooms.map((r) => r.roomId) + .includes(fav2), + ).toBeTruthy(); + expect( + store + .getNotificationState(space2) + .rooms.map((r) => r.roomId) + .includes(fav3), + ).toBeTruthy(); + expect( + store + .getNotificationState(space3) + .rooms.map((r) => r.roomId) + .includes(fav1), + ).toBeFalsy(); + expect( + store + .getNotificationState(space3) + .rooms.map((r) => r.roomId) + .includes(fav2), + ).toBeFalsy(); + expect( + store + .getNotificationState(space3) + .rooms.map((r) => r.roomId) + .includes(fav3), + ).toBeFalsy(); }); it("other rooms are added to Notification States for all spaces containing the room exc Home", () => { // XXX: All rooms space is forcibly enabled, as part of a future PR test Home space better // expect(store.getNotificationState(HOME_SPACE).rooms.map(r => r.roomId).includes(room1)).toBeFalsy(); - expect(store.getNotificationState(space1).rooms.map(r => r.roomId).includes(room1)).toBeTruthy(); - expect(store.getNotificationState(space2).rooms.map(r => r.roomId).includes(room1)).toBeTruthy(); - expect(store.getNotificationState(space3).rooms.map(r => r.roomId).includes(room1)).toBeFalsy(); + expect( + store + .getNotificationState(space1) + .rooms.map((r) => r.roomId) + .includes(room1), + ).toBeTruthy(); + expect( + store + .getNotificationState(space2) + .rooms.map((r) => r.roomId) + .includes(room1), + ).toBeTruthy(); + expect( + store + .getNotificationState(space3) + .rooms.map((r) => r.roomId) + .includes(room1), + ).toBeFalsy(); }); it("honours m.space.parent if sender has permission in parent space", () => { @@ -690,10 +761,24 @@ describe("SpaceStore", () => { expect(store.isRoomInSpace(MetaSpace.Home, invite1)).toBeTruthy(); }); - describe('onRoomsUpdate()', () => { + describe("onRoomsUpdate()", () => { beforeEach(() => { - [fav1, fav2, fav3, dm1, dm2, dm3, orphan1, orphan2, invite1, invite2, room1, room2, room3, room4] - .forEach(mkRoom); + [ + fav1, + fav2, + fav3, + dm1, + dm2, + dm3, + orphan1, + orphan2, + invite1, + invite2, + room1, + room2, + room3, + room4, + ].forEach(mkRoom); mkSpace(space2, [fav1, fav2, fav3, room1]); mkSpace(space3, [invite2]); mkSpace(space4, [room4, fav2, space2, space3]); @@ -725,7 +810,7 @@ describe("SpaceStore", () => { room: spaceId, user: client.getUserId(), skey: user.userId, - content: { membership: 'join' }, + content: { membership: "join" }, ts: Date.now(), }); const spaceRoom = client.getRoom(spaceId); @@ -737,11 +822,11 @@ describe("SpaceStore", () => { client.emit(RoomStateEvent.Members, memberEvent, spaceRoom.currentState, user); }; - it('emits events for parent spaces when child room is added', async () => { + it("emits events for parent spaces when child room is added", async () => { await run(); - const room5 = mkRoom('!room5:server'); - const emitSpy = jest.spyOn(store, 'emit').mockClear(); + const room5 = mkRoom("!room5:server"); + const emitSpy = jest.spyOn(store, "emit").mockClear(); // add room5 into space2 addChildRoom(space2, room5.roomId); @@ -753,9 +838,9 @@ describe("SpaceStore", () => { expect(emitSpy).not.toHaveBeenCalledWith(space3); }); - it('updates rooms state when a child room is added', async () => { + it("updates rooms state when a child room is added", async () => { await run(); - const room5 = mkRoom('!room5:server'); + const room5 = mkRoom("!room5:server"); expect(store.isRoomInSpace(space2, room5.roomId)).toBeFalsy(); expect(store.isRoomInSpace(space4, room5.roomId)).toBeFalsy(); @@ -770,10 +855,10 @@ describe("SpaceStore", () => { expect(store.isRoomInSpace(space1, room5.roomId)).toBeTruthy(); }); - it('emits events for parent spaces when a member is added', async () => { + it("emits events for parent spaces when a member is added", async () => { await run(); - const emitSpy = jest.spyOn(store, 'emit').mockClear(); + const emitSpy = jest.spyOn(store, "emit").mockClear(); // add into space2 addMember(space2, dm1Partner); @@ -785,7 +870,7 @@ describe("SpaceStore", () => { expect(emitSpy).not.toHaveBeenCalledWith(space3); }); - it('updates users state when a member is added', async () => { + it("updates users state when a member is added", async () => { await run(); expect(store.getSpaceFilteredUserIds(space2)).toEqual(new Set([])); @@ -805,9 +890,7 @@ describe("SpaceStore", () => { beforeEach(async () => { mkRoom(room1); // not a space - mkSpace(space1, [ - mkSpace(space2).roomId, - ]); + mkSpace(space1, [mkSpace(space2).roomId]); mkSpace(space3).getMyMembership.mockReturnValue("invite"); await run(); store.setActiveSpace(MetaSpace.Home); @@ -873,7 +956,7 @@ describe("SpaceStore", () => { user: dm1Partner.userId, room: space1, }); - space.getMember.mockImplementation(userId => { + space.getMember.mockImplementation((userId) => { if (userId === dm1Partner.userId) { const member = new RoomMember(space1, dm1Partner.userId); member.membership = "join"; @@ -924,7 +1007,7 @@ describe("SpaceStore", () => { mkSpace(space2, [room2]); await run(); - dispatcherRef = defaultDispatcher.register(payload => { + dispatcherRef = defaultDispatcher.register((payload) => { if (payload.action === Action.ViewRoom || payload.action === Action.ViewHomePage) { currentRoom = payload.room_id || null; } @@ -1006,17 +1089,19 @@ describe("SpaceStore", () => { mkSpace(space2, [room1, room2]); const cliRoom2 = client.getRoom(room2); - mocked(cliRoom2.currentState).getStateEvents.mockImplementation(testUtils.mockStateEventImplementation([ - mkEvent({ - event: true, - type: EventType.SpaceParent, - room: room2, - user: testUserId, - skey: space2, - content: { via: [], canonical: true }, - ts: Date.now(), - }), - ])); + mocked(cliRoom2.currentState).getStateEvents.mockImplementation( + testUtils.mockStateEventImplementation([ + mkEvent({ + event: true, + type: EventType.SpaceParent, + room: room2, + user: testUserId, + skey: space2, + content: { via: [], canonical: true }, + ts: Date.now(), + }), + ]), + ); await run(); }); @@ -1174,11 +1259,8 @@ describe("SpaceStore", () => { myRootSpaceMember.membership = "join"; const rootSpaceFriend = new RoomMember(space1, dm1Partner.userId); rootSpaceFriend.membership = "join"; - rootSpace.getMembers.mockReturnValue([ - myRootSpaceMember, - rootSpaceFriend, - ]); - rootSpace.getMember.mockImplementation(userId => { + rootSpace.getMembers.mockReturnValue([myRootSpaceMember, rootSpaceFriend]); + rootSpace.getMember.mockImplementation((userId) => { switch (userId) { case testUserId: return myRootSpaceMember; @@ -1219,7 +1301,7 @@ describe("SpaceStore", () => { client.emit(ClientEvent.Room, subspace); jest.runOnlyPendingTimers(); expect(SpaceStore.instance.invitedSpaces).toStrictEqual([]); - expect(SpaceStore.instance.spacePanelSpaces.map(r => r.roomId)).toStrictEqual([rootSpace.roomId]); + expect(SpaceStore.instance.spacePanelSpaces.map((r) => r.roomId)).toStrictEqual([rootSpace.roomId]); await prom; }); diff --git a/test/stores/TypingStore-test.ts b/test/stores/TypingStore-test.ts index b6b5c388f8..436ea14a4b 100644 --- a/test/stores/TypingStore-test.ts +++ b/test/stores/TypingStore-test.ts @@ -32,8 +32,8 @@ describe("TypingStore", () => { let typingStore: TypingStore; let mockClient: MatrixClient; const settings = { - "sendTypingNotifications": true, - "feature_thread": false, + sendTypingNotifications: true, + feature_thread: false, }; const roomId = "!test:example.com"; const localRoomId = LOCAL_ROOM_ID_PREFIX + "test"; diff --git a/test/stores/VoiceRecordingStore-test.ts b/test/stores/VoiceRecordingStore-test.ts index c675d8cc1a..0733804fb4 100644 --- a/test/stores/VoiceRecordingStore-test.ts +++ b/test/stores/VoiceRecordingStore-test.ts @@ -1,4 +1,3 @@ - /* Copyright 2022 The Matrix.org Foundation C.I.C. @@ -17,18 +16,18 @@ limitations under the License. import { MatrixClient } from "matrix-js-sdk/src/matrix"; -import { VoiceRecordingStore } from '../../src/stores/VoiceRecordingStore'; +import { VoiceRecordingStore } from "../../src/stores/VoiceRecordingStore"; import { MatrixClientPeg } from "../../src/MatrixClientPeg"; import { flushPromises } from "../test-utils"; import { VoiceMessageRecording } from "../../src/audio/VoiceMessageRecording"; const stubClient = {} as undefined as MatrixClient; -jest.spyOn(MatrixClientPeg, 'get').mockReturnValue(stubClient); +jest.spyOn(MatrixClientPeg, "get").mockReturnValue(stubClient); -describe('VoiceRecordingStore', () => { - const room1Id = '!room1:server.org'; - const room2Id = '!room2:server.org'; - const room3Id = '!room3:server.org'; +describe("VoiceRecordingStore", () => { + const room1Id = "!room1:server.org"; + const room2Id = "!room2:server.org"; + const room3Id = "!room3:server.org"; const room1Recording = { destroy: jest.fn() } as unknown as VoiceMessageRecording; const room2Recording = { destroy: jest.fn() } as unknown as VoiceMessageRecording; @@ -44,20 +43,20 @@ describe('VoiceRecordingStore', () => { return store; }; - describe('startRecording()', () => { - it('throws when roomId is falsy', () => { + describe("startRecording()", () => { + it("throws when roomId is falsy", () => { const store = mkStore(); expect(() => store.startRecording(undefined)).toThrow("Recording must be associated with a room"); }); - it('throws when room already has a recording', () => { + it("throws when room already has a recording", () => { const store = mkStore(); // @ts-ignore store.storeState = state; expect(() => store.startRecording(room2Id)).toThrow("A recording is already in progress"); }); - it('creates and adds recording to state', async () => { + it("creates and adds recording to state", async () => { const store = mkStore(); const result = store.startRecording(room2Id); @@ -68,8 +67,8 @@ describe('VoiceRecordingStore', () => { }); }); - describe('disposeRecording()', () => { - it('destroys recording for a room if it exists in state', async () => { + describe("disposeRecording()", () => { + it("destroys recording for a room if it exists in state", async () => { const store = mkStore(); // @ts-ignore store.storeState = state; @@ -79,7 +78,7 @@ describe('VoiceRecordingStore', () => { expect(room1Recording.destroy).toHaveBeenCalled(); }); - it('removes room from state when it has a recording', async () => { + it("removes room from state when it has a recording", async () => { const store = mkStore(); // @ts-ignore store.storeState = state; @@ -89,7 +88,7 @@ describe('VoiceRecordingStore', () => { expect(store.getActiveRecording(room2Id)).toBeFalsy(); }); - it('removes room from state when it has a falsy recording', async () => { + it("removes room from state when it has a falsy recording", async () => { const store = mkStore(); // @ts-ignore store.storeState = state; diff --git a/test/stores/WidgetLayoutStore-test.ts b/test/stores/WidgetLayoutStore-test.ts index eb41d1231a..54d40c52b7 100644 --- a/test/stores/WidgetLayoutStore-test.ts +++ b/test/stores/WidgetLayoutStore-test.ts @@ -31,17 +31,18 @@ const mockRoom = { getContent: () => null, }; }, - } }; + }, +}; const mockApps = [ - { roomId: roomId, id: "1" }, - { roomId: roomId, id: "2" }, - { roomId: roomId, id: "3" }, - { roomId: roomId, id: "4" }, + { roomId: roomId, id: "1" }, + { roomId: roomId, id: "2" }, + { roomId: roomId, id: "3" }, + { roomId: roomId, id: "4" }, ]; // fake the WidgetStore.instance to just return an object with `getApps` -jest.spyOn(WidgetStore, 'instance', 'get').mockReturnValue({ getApps: (_room) => mockApps }); +jest.spyOn(WidgetStore, "instance", "get").mockReturnValue({ getApps: (_room) => mockApps }); describe("WidgetLayoutStore", () => { // we need to init a client so it does not error, when asking for DeviceStorage handlers (SettingsStore.setValue("Widgets.layout")) @@ -63,16 +64,16 @@ describe("WidgetLayoutStore", () => { store.moveToContainer(mockRoom, mockApps[0], Container.Top); store.moveToContainer(mockRoom, mockApps[1], Container.Top); store.moveToContainer(mockRoom, mockApps[2], Container.Top); - expect(new Set(store.getContainerWidgets(mockRoom, Container.Top))) - .toEqual(new Set([mockApps[0], mockApps[1], mockApps[2]])); + expect(new Set(store.getContainerWidgets(mockRoom, Container.Top))).toEqual( + new Set([mockApps[0], mockApps[1], mockApps[2]]), + ); }); it("cannot add more than three widgets to top container", async () => { store.recalculateRoom(mockRoom); store.moveToContainer(mockRoom, mockApps[0], Container.Top); store.moveToContainer(mockRoom, mockApps[1], Container.Top); store.moveToContainer(mockRoom, mockApps[2], Container.Top); - expect(store.canAddToContainer(mockRoom, Container.Top)) - .toEqual(false); + expect(store.canAddToContainer(mockRoom, Container.Top)).toEqual(false); }); it("remove pins when maximising (other widget)", async () => { store.recalculateRoom(mockRoom); @@ -80,12 +81,11 @@ describe("WidgetLayoutStore", () => { store.moveToContainer(mockRoom, mockApps[1], Container.Top); store.moveToContainer(mockRoom, mockApps[2], Container.Top); store.moveToContainer(mockRoom, mockApps[3], Container.Center); - expect(store.getContainerWidgets(mockRoom, Container.Top)) - .toEqual([]); - expect(new Set(store.getContainerWidgets(mockRoom, Container.Right))) - .toEqual(new Set([mockApps[0], mockApps[1], mockApps[2]])); - expect(store.getContainerWidgets(mockRoom, Container.Center)) - .toEqual([mockApps[3]]); + expect(store.getContainerWidgets(mockRoom, Container.Top)).toEqual([]); + expect(new Set(store.getContainerWidgets(mockRoom, Container.Right))).toEqual( + new Set([mockApps[0], mockApps[1], mockApps[2]]), + ); + expect(store.getContainerWidgets(mockRoom, Container.Center)).toEqual([mockApps[3]]); }); it("remove pins when maximising (one of the pinned widgets)", async () => { store.recalculateRoom(mockRoom); @@ -93,33 +93,30 @@ describe("WidgetLayoutStore", () => { store.moveToContainer(mockRoom, mockApps[1], Container.Top); store.moveToContainer(mockRoom, mockApps[2], Container.Top); store.moveToContainer(mockRoom, mockApps[0], Container.Center); - expect(store.getContainerWidgets(mockRoom, Container.Top)) - .toEqual([]); - expect(store.getContainerWidgets(mockRoom, Container.Center)) - .toEqual([mockApps[0]]); - expect(new Set(store.getContainerWidgets(mockRoom, Container.Right))) - .toEqual(new Set([mockApps[1], mockApps[2], mockApps[3]])); + expect(store.getContainerWidgets(mockRoom, Container.Top)).toEqual([]); + expect(store.getContainerWidgets(mockRoom, Container.Center)).toEqual([mockApps[0]]); + expect(new Set(store.getContainerWidgets(mockRoom, Container.Right))).toEqual( + new Set([mockApps[1], mockApps[2], mockApps[3]]), + ); }); it("remove maximised when pinning (other widget)", async () => { store.recalculateRoom(mockRoom); store.moveToContainer(mockRoom, mockApps[0], Container.Center); store.moveToContainer(mockRoom, mockApps[1], Container.Top); - expect(store.getContainerWidgets(mockRoom, Container.Top)) - .toEqual([mockApps[1]]); - expect(store.getContainerWidgets(mockRoom, Container.Center)) - .toEqual([]); - expect(new Set(store.getContainerWidgets(mockRoom, Container.Right))) - .toEqual(new Set([mockApps[2], mockApps[3], mockApps[0]])); + expect(store.getContainerWidgets(mockRoom, Container.Top)).toEqual([mockApps[1]]); + expect(store.getContainerWidgets(mockRoom, Container.Center)).toEqual([]); + expect(new Set(store.getContainerWidgets(mockRoom, Container.Right))).toEqual( + new Set([mockApps[2], mockApps[3], mockApps[0]]), + ); }); it("remove maximised when pinning (same widget)", async () => { store.recalculateRoom(mockRoom); store.moveToContainer(mockRoom, mockApps[0], Container.Center); store.moveToContainer(mockRoom, mockApps[0], Container.Top); - expect(store.getContainerWidgets(mockRoom, Container.Top)) - .toEqual([mockApps[0]]); - expect(store.getContainerWidgets(mockRoom, Container.Center)) - .toEqual([]); - expect(new Set(store.getContainerWidgets(mockRoom, Container.Right))) - .toEqual(new Set([mockApps[2], mockApps[3], mockApps[1]])); + expect(store.getContainerWidgets(mockRoom, Container.Top)).toEqual([mockApps[0]]); + expect(store.getContainerWidgets(mockRoom, Container.Center)).toEqual([]); + expect(new Set(store.getContainerWidgets(mockRoom, Container.Right))).toEqual( + new Set([mockApps[2], mockApps[3], mockApps[1]]), + ); }); }); diff --git a/test/stores/right-panel/RightPanelStore-test.ts b/test/stores/right-panel/RightPanelStore-test.ts index e7168dd010..e05e6e39a0 100644 --- a/test/stores/right-panel/RightPanelStore-test.ts +++ b/test/stores/right-panel/RightPanelStore-test.ts @@ -45,8 +45,8 @@ describe("RightPanelStore", () => { }); const viewRoom = async (roomId: string) => { - const roomChanged = new Promise(resolve => { - const ref = defaultDispatcher.register(payload => { + const roomChanged = new Promise((resolve) => { + const ref = defaultDispatcher.register((payload) => { if (payload.action === Action.ActiveRoomChanged && payload.newRoomId === roomId) { defaultDispatcher.unregister(ref); resolve(); @@ -113,9 +113,7 @@ describe("RightPanelStore", () => { await viewRoom("!1:example.org"); store.setCard({ phase: RightPanelPhases.RoomSummary }, true, "!1:example.org"); store.setCard({ phase: RightPanelPhases.RoomSummary }, true, "!1:example.org"); - expect(store.roomPhaseHistory).toEqual([ - { phase: RightPanelPhases.RoomSummary, state: {} }, - ]); + expect(store.roomPhaseHistory).toEqual([{ phase: RightPanelPhases.RoomSummary, state: {} }]); }); it("opens the panel in the given room with the correct phase", () => { store.setCard({ phase: RightPanelPhases.RoomSummary }, true, "!1:example.org"); @@ -126,9 +124,7 @@ describe("RightPanelStore", () => { await viewRoom("!1:example.org"); store.setCard({ phase: RightPanelPhases.RoomSummary }, true, "!1:example.org"); store.setCard({ phase: RightPanelPhases.RoomMemberList }, true, "!1:example.org"); - expect(store.roomPhaseHistory).toEqual([ - { phase: RightPanelPhases.RoomMemberList, state: {} }, - ]); + expect(store.roomPhaseHistory).toEqual([{ phase: RightPanelPhases.RoomMemberList, state: {} }]); }); }); @@ -136,10 +132,11 @@ describe("RightPanelStore", () => { it("overwrites history", async () => { await viewRoom("!1:example.org"); store.setCard({ phase: RightPanelPhases.RoomMemberList }, true, "!1:example.org"); - store.setCards([ - { phase: RightPanelPhases.RoomSummary }, - { phase: RightPanelPhases.PinnedMessages }, - ], true, "!1:example.org"); + store.setCards( + [{ phase: RightPanelPhases.RoomSummary }, { phase: RightPanelPhases.PinnedMessages }], + true, + "!1:example.org", + ); expect(store.roomPhaseHistory).toEqual([ { phase: RightPanelPhases.RoomSummary, state: {} }, { phase: RightPanelPhases.PinnedMessages, state: {} }, @@ -171,10 +168,11 @@ describe("RightPanelStore", () => { describe("popCard", () => { it("removes the most recent card", () => { - store.setCards([ - { phase: RightPanelPhases.RoomSummary }, - { phase: RightPanelPhases.PinnedMessages }, - ], true, "!1:example.org"); + store.setCards( + [{ phase: RightPanelPhases.RoomSummary }, { phase: RightPanelPhases.PinnedMessages }], + true, + "!1:example.org", + ); expect(store.currentCardForRoom("!1:example.org").phase).toEqual(RightPanelPhases.PinnedMessages); store.popCard("!1:example.org"); expect(store.currentCardForRoom("!1:example.org").phase).toEqual(RightPanelPhases.RoomSummary); @@ -208,15 +206,19 @@ describe("RightPanelStore", () => { it("doesn't restore member info cards when switching back to a room", async () => { await viewRoom("!1:example.org"); - store.setCards([ - { - phase: RightPanelPhases.RoomMemberList, - }, - { - phase: RightPanelPhases.RoomMemberInfo, - state: { member: new RoomMember("!1:example.org", "@alice:example.org") }, - }, - ], true, "!1:example.org"); + store.setCards( + [ + { + phase: RightPanelPhases.RoomMemberList, + }, + { + phase: RightPanelPhases.RoomMemberInfo, + state: { member: new RoomMember("!1:example.org", "@alice:example.org") }, + }, + ], + true, + "!1:example.org", + ); expect(store.currentCardForRoom("!1:example.org").phase).toEqual(RightPanelPhases.RoomMemberInfo); // Switch away and back diff --git a/test/stores/room-list/SlidingRoomListStore-test.ts b/test/stores/room-list/SlidingRoomListStore-test.ts index 488c92396a..0c882ce35b 100644 --- a/test/stores/room-list/SlidingRoomListStore-test.ts +++ b/test/stores/room-list/SlidingRoomListStore-test.ts @@ -13,9 +13,9 @@ 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 { mocked } from 'jest-mock'; -import { SlidingSync, SlidingSyncEvent } from 'matrix-js-sdk/src/sliding-sync'; -import { Room } from 'matrix-js-sdk/src/matrix'; +import { mocked } from "jest-mock"; +import { SlidingSync, SlidingSyncEvent } from "matrix-js-sdk/src/sliding-sync"; +import { Room } from "matrix-js-sdk/src/matrix"; import { LISTS_UPDATE_EVENT, @@ -24,18 +24,18 @@ import { } from "../../../src/stores/room-list/SlidingRoomListStore"; import { SpaceStoreClass } from "../../../src/stores/spaces/SpaceStore"; import { MockEventEmitter, stubClient, untilEmission } from "../../test-utils"; -import { TestSdkContext } from '../../TestSdkContext'; -import { SlidingSyncManager } from '../../../src/SlidingSyncManager'; -import { RoomViewStore } from '../../../src/stores/RoomViewStore'; -import { MatrixDispatcher } from '../../../src/dispatcher/dispatcher'; -import { SortAlgorithm } from '../../../src/stores/room-list/algorithms/models'; -import { DefaultTagID, TagID } from '../../../src/stores/room-list/models'; -import { UPDATE_SELECTED_SPACE } from '../../../src/stores/spaces'; -import { LISTS_LOADING_EVENT } from '../../../src/stores/room-list/RoomListStore'; -import { UPDATE_EVENT } from '../../../src/stores/AsyncStore'; +import { TestSdkContext } from "../../TestSdkContext"; +import { SlidingSyncManager } from "../../../src/SlidingSyncManager"; +import { RoomViewStore } from "../../../src/stores/RoomViewStore"; +import { MatrixDispatcher } from "../../../src/dispatcher/dispatcher"; +import { SortAlgorithm } from "../../../src/stores/room-list/algorithms/models"; +import { DefaultTagID, TagID } from "../../../src/stores/room-list/models"; +import { UPDATE_SELECTED_SPACE } from "../../../src/stores/spaces"; +import { LISTS_LOADING_EVENT } from "../../../src/stores/room-list/RoomListStore"; +import { UPDATE_EVENT } from "../../../src/stores/AsyncStore"; -jest.mock('../../../src/SlidingSyncManager'); -const MockSlidingSyncManager = >SlidingSyncManager; +jest.mock("../../../src/SlidingSyncManager"); +const MockSlidingSyncManager = >(SlidingSyncManager); describe("SlidingRoomListStore", () => { let store: SlidingRoomListStoreClass; @@ -54,12 +54,16 @@ describe("SlidingRoomListStore", () => { }, }) as SpaceStoreClass; context._SlidingSyncManager = new MockSlidingSyncManager(); - context._SlidingSyncManager.slidingSync = mocked(new MockEventEmitter({ - getListData: jest.fn(), - }) as unknown as SlidingSync); - context._RoomViewStore = mocked(new MockEventEmitter({ - getRoomId: jest.fn(), - }) as unknown as RoomViewStore); + context._SlidingSyncManager.slidingSync = mocked( + new MockEventEmitter({ + getListData: jest.fn(), + }) as unknown as SlidingSync, + ); + context._RoomViewStore = mocked( + new MockEventEmitter({ + getRoomId: jest.fn(), + }) as unknown as RoomViewStore, + ); // mock implementations to allow the store to map tag IDs to sliding sync list indexes and vice versa let index = 0; @@ -208,12 +212,13 @@ describe("SlidingRoomListStore", () => { return indexToListData[i] || null; }); - expect(store.getTagsForRoom(new Room(roomA, context.client, context.client.getUserId()))).toEqual( - [DefaultTagID.Untagged], - ); - expect(store.getTagsForRoom(new Room(roomB, context.client, context.client.getUserId()))).toEqual( - [DefaultTagID.Favourite, DefaultTagID.Untagged], - ); + expect(store.getTagsForRoom(new Room(roomA, context.client, context.client.getUserId()))).toEqual([ + DefaultTagID.Untagged, + ]); + expect(store.getTagsForRoom(new Room(roomB, context.client, context.client.getUserId()))).toEqual([ + DefaultTagID.Favourite, + DefaultTagID.Untagged, + ]); }); it("emits LISTS_UPDATE_EVENT when slidingSync lists update", async () => { @@ -224,7 +229,8 @@ describe("SlidingRoomListStore", () => { const tagId = DefaultTagID.Favourite; const listIndex = context.slidingSyncManager.getOrAllocateListIndex(tagId); const joinCount = 10; - const roomIndexToRoomId = { // mixed to ensure we sort + const roomIndexToRoomId = { + // mixed to ensure we sort 1: roomB, 2: roomC, 0: roomA, @@ -261,7 +267,8 @@ describe("SlidingRoomListStore", () => { const tagId = DefaultTagID.Favourite; const listIndex = context.slidingSyncManager.getOrAllocateListIndex(tagId); const joinCount = 10; - const roomIndexToRoomId = { // mixed to ensure we sort + const roomIndexToRoomId = { + // mixed to ensure we sort 1: roomIdB, 2: roomIdC, 0: roomIdA, diff --git a/test/stores/room-list/SpaceWatcher-test.ts b/test/stores/room-list/SpaceWatcher-test.ts index 9664c9dd8c..2e570ef75d 100644 --- a/test/stores/room-list/SpaceWatcher-test.ts +++ b/test/stores/room-list/SpaceWatcher-test.ts @@ -13,7 +13,7 @@ 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 { mocked } from 'jest-mock'; +import { mocked } from "jest-mock"; import { SpaceWatcher } from "../../../src/stores/room-list/SpaceWatcher"; import type { RoomListStoreClass } from "../../../src/stores/room-list/RoomListStore"; @@ -22,11 +22,7 @@ import SpaceStore from "../../../src/stores/spaces/SpaceStore"; import { MetaSpace, UPDATE_HOME_BEHAVIOUR } from "../../../src/stores/spaces"; import { stubClient } from "../../test-utils"; import { SettingLevel } from "../../../src/settings/SettingLevel"; -import { - mkSpace, - emitPromise, - setupAsyncStoreWithClient, -} from "../../test-utils"; +import { mkSpace, emitPromise, setupAsyncStoreWithClient } from "../../test-utils"; import { MatrixClientPeg } from "../../../src/MatrixClientPeg"; import { SpaceFilterCondition } from "../../../src/stores/room-list/filters/SpaceFilterCondition"; import DMRoomMap from "../../../src/utils/DMRoomMap"; @@ -34,8 +30,8 @@ import DMRoomMap from "../../../src/utils/DMRoomMap"; let filter: SpaceFilterCondition = null; const mockRoomListStore = { - addFilter: f => filter = f, - removeFilter: () => filter = null, + addFilter: (f) => (filter = f), + removeFilter: () => (filter = null), } as unknown as RoomListStoreClass; const getUserIdForRoomId = jest.fn(); @@ -64,7 +60,7 @@ describe("SpaceWatcher", () => { filter = null; store.removeAllListeners(); store.setActiveSpace(MetaSpace.Home); - client.getVisibleRooms.mockReturnValue(rooms = []); + client.getVisibleRooms.mockReturnValue((rooms = [])); mkSpaceForRooms(space1); mkSpaceForRooms(space2); @@ -76,7 +72,7 @@ describe("SpaceWatcher", () => { [MetaSpace.Orphans]: true, }); - client.getRoom.mockImplementation(roomId => rooms.find(room => room.roomId === roomId)); + client.getRoom.mockImplementation((roomId) => rooms.find((room) => room.roomId === roomId)); await setupAsyncStoreWithClient(store, client); }); diff --git a/test/stores/room-list/algorithms/Algorithm-test.ts b/test/stores/room-list/algorithms/Algorithm-test.ts index ec45bed553..efd33cad0f 100644 --- a/test/stores/room-list/algorithms/Algorithm-test.ts +++ b/test/stores/room-list/algorithms/Algorithm-test.ts @@ -63,11 +63,14 @@ describe("Algorithm", () => { pendingEventOrdering: PendingEventOrdering.Detached, }); - client.getRoom.mockImplementation(roomId => { + client.getRoom.mockImplementation((roomId) => { switch (roomId) { - case room.roomId: return room; - case roomWithCall.roomId: return roomWithCall; - default: return null; + case room.roomId: + return room; + case roomWithCall.roomId: + return roomWithCall; + default: + return null; } }); client.getRooms.mockReturnValue([room, roomWithCall]); diff --git a/test/stores/room-list/filters/SpaceFilterCondition-test.ts b/test/stores/room-list/filters/SpaceFilterCondition-test.ts index ae9aab135c..e8429f30b3 100644 --- a/test/stores/room-list/filters/SpaceFilterCondition-test.ts +++ b/test/stores/room-list/filters/SpaceFilterCondition-test.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { mocked } from 'jest-mock'; +import { mocked } from "jest-mock"; import { Room } from "matrix-js-sdk/src/matrix"; import SettingsStore from "../../../../src/settings/SettingsStore"; @@ -26,7 +26,7 @@ import SpaceStore from "../../../../src/stores/spaces/SpaceStore"; jest.mock("../../../../src/settings/SettingsStore"); jest.mock("../../../../src/stores/spaces/SpaceStore", () => { // eslint-disable-next-line @typescript-eslint/no-var-requires - const EventEmitter = require('events'); + const EventEmitter = require("events"); class MockSpaceStore extends EventEmitter { isRoomInSpace = jest.fn(); getSpaceFilteredUserIds = jest.fn().mockReturnValue(new Set([])); @@ -40,16 +40,19 @@ const SpaceStoreInstanceMock = mocked(SpaceStore.instance); jest.useFakeTimers(); -describe('SpaceFilterCondition', () => { - const space1 = '!space1:server'; - const space2 = '!space2:server'; - const room1Id = '!r1:server'; - const room2Id = '!r2:server'; - const room3Id = '!r3:server'; - const user1Id = '@u1:server'; - const user2Id = '@u2:server'; - const user3Id = '@u3:server'; - const makeMockGetValue = (settings = {}) => (settingName, space) => settings[settingName]?.[space] || false; +describe("SpaceFilterCondition", () => { + const space1 = "!space1:server"; + const space2 = "!space2:server"; + const room1Id = "!r1:server"; + const room2Id = "!r2:server"; + const room3Id = "!r3:server"; + const user1Id = "@u1:server"; + const user2Id = "@u2:server"; + const user3Id = "@u3:server"; + const makeMockGetValue = + (settings = {}) => + (settingName, space) => + settings[settingName]?.[space] || false; beforeEach(() => { jest.resetAllMocks(); @@ -65,9 +68,9 @@ describe('SpaceFilterCondition', () => { return filter; }; - describe('isVisible', () => { + describe("isVisible", () => { const room1 = { roomId: room1Id } as unknown as Room; - it('calls isRoomInSpace correctly', () => { + it("calls isRoomInSpace correctly", () => { const filter = initFilter(space1); expect(filter.isVisible(room1)).toEqual(true); @@ -75,40 +78,46 @@ describe('SpaceFilterCondition', () => { }); }); - describe('onStoreUpdate', () => { - it('emits filter changed event when updateSpace is called even without changes', async () => { + describe("onStoreUpdate", () => { + it("emits filter changed event when updateSpace is called even without changes", async () => { const filter = new SpaceFilterCondition(); - const emitSpy = jest.spyOn(filter, 'emit'); + const emitSpy = jest.spyOn(filter, "emit"); filter.updateSpace(space1); jest.runOnlyPendingTimers(); expect(emitSpy).toHaveBeenCalledWith(FILTER_CHANGED); }); - describe('showPeopleInSpace setting', () => { - it('emits filter changed event when setting changes', async () => { + describe("showPeopleInSpace setting", () => { + it("emits filter changed event when setting changes", async () => { // init filter with setting true for space1 - SettingsStoreMock.getValue.mockImplementation(makeMockGetValue({ - ["Spaces.showPeopleInSpace"]: { [space1]: true }, - })); + SettingsStoreMock.getValue.mockImplementation( + makeMockGetValue({ + ["Spaces.showPeopleInSpace"]: { [space1]: true }, + }), + ); const filter = initFilter(space1); - const emitSpy = jest.spyOn(filter, 'emit'); + const emitSpy = jest.spyOn(filter, "emit"); - SettingsStoreMock.getValue.mockClear().mockImplementation(makeMockGetValue({ - ["Spaces.showPeopleInSpace"]: { [space1]: false }, - })); + SettingsStoreMock.getValue.mockClear().mockImplementation( + makeMockGetValue({ + ["Spaces.showPeopleInSpace"]: { [space1]: false }, + }), + ); SpaceStoreInstanceMock.emit(space1); jest.runOnlyPendingTimers(); expect(emitSpy).toHaveBeenCalledWith(FILTER_CHANGED); }); - it('emits filter changed event when setting is false and space changes to a meta space', async () => { + it("emits filter changed event when setting is false and space changes to a meta space", async () => { // init filter with setting true for space1 - SettingsStoreMock.getValue.mockImplementation(makeMockGetValue({ - ["Spaces.showPeopleInSpace"]: { [space1]: false }, - })); + SettingsStoreMock.getValue.mockImplementation( + makeMockGetValue({ + ["Spaces.showPeopleInSpace"]: { [space1]: false }, + }), + ); const filter = initFilter(space1); - const emitSpy = jest.spyOn(filter, 'emit'); + const emitSpy = jest.spyOn(filter, "emit"); filter.updateSpace(MetaSpace.Home); jest.runOnlyPendingTimers(); @@ -116,19 +125,19 @@ describe('SpaceFilterCondition', () => { }); }); - it('does not emit filter changed event on store update when nothing changed', async () => { + it("does not emit filter changed event on store update when nothing changed", async () => { const filter = initFilter(space1); - const emitSpy = jest.spyOn(filter, 'emit'); + const emitSpy = jest.spyOn(filter, "emit"); SpaceStoreInstanceMock.emit(space1); jest.runOnlyPendingTimers(); expect(emitSpy).not.toHaveBeenCalledWith(FILTER_CHANGED); }); - it('removes listener when updateSpace is called', async () => { + it("removes listener when updateSpace is called", async () => { const filter = initFilter(space1); filter.updateSpace(space2); jest.runOnlyPendingTimers(); - const emitSpy = jest.spyOn(filter, 'emit'); + const emitSpy = jest.spyOn(filter, "emit"); // update mock so filter would emit change if it was listening to space1 SpaceStoreInstanceMock.getSpaceFilteredRoomIds.mockReturnValue(new Set([room1Id])); @@ -138,11 +147,11 @@ describe('SpaceFilterCondition', () => { expect(emitSpy).not.toHaveBeenCalledWith(FILTER_CHANGED); }); - it('removes listener when destroy is called', async () => { + it("removes listener when destroy is called", async () => { const filter = initFilter(space1); filter.destroy(); jest.runOnlyPendingTimers(); - const emitSpy = jest.spyOn(filter, 'emit'); + const emitSpy = jest.spyOn(filter, "emit"); // update mock so filter would emit change if it was listening to space1 SpaceStoreInstanceMock.getSpaceFilteredRoomIds.mockReturnValue(new Set([room1Id])); @@ -152,19 +161,19 @@ describe('SpaceFilterCondition', () => { expect(emitSpy).not.toHaveBeenCalledWith(FILTER_CHANGED); }); - describe('when directChildRoomIds change', () => { + describe("when directChildRoomIds change", () => { beforeEach(() => { SpaceStoreInstanceMock.getSpaceFilteredRoomIds.mockReturnValue(new Set([room1Id, room2Id])); }); const filterChangedCases = [ - ['room added', [room1Id, room2Id, room3Id]], - ['room removed', [room1Id]], - ['room swapped', [room1Id, room3Id]], // same number of rooms with changes + ["room added", [room1Id, room2Id, room3Id]], + ["room removed", [room1Id]], + ["room swapped", [room1Id, room3Id]], // same number of rooms with changes ]; - it.each(filterChangedCases)('%s', (_d, rooms) => { + it.each(filterChangedCases)("%s", (_d, rooms) => { const filter = initFilter(space1); - const emitSpy = jest.spyOn(filter, 'emit'); + const emitSpy = jest.spyOn(filter, "emit"); SpaceStoreInstanceMock.getSpaceFilteredRoomIds.mockReturnValue(new Set(rooms)); SpaceStoreInstanceMock.emit(space1); @@ -173,19 +182,19 @@ describe('SpaceFilterCondition', () => { }); }); - describe('when user ids change', () => { + describe("when user ids change", () => { beforeEach(() => { SpaceStoreInstanceMock.getSpaceFilteredUserIds.mockReturnValue(new Set([user1Id, user2Id])); }); const filterChangedCases = [ - ['user added', [user1Id, user2Id, user3Id]], - ['user removed', [user1Id]], - ['user swapped', [user1Id, user3Id]], // same number of rooms with changes + ["user added", [user1Id, user2Id, user3Id]], + ["user removed", [user1Id]], + ["user swapped", [user1Id, user3Id]], // same number of rooms with changes ]; - it.each(filterChangedCases)('%s', (_d, rooms) => { + it.each(filterChangedCases)("%s", (_d, rooms) => { const filter = initFilter(space1); - const emitSpy = jest.spyOn(filter, 'emit'); + const emitSpy = jest.spyOn(filter, "emit"); SpaceStoreInstanceMock.getSpaceFilteredUserIds.mockReturnValue(new Set(rooms)); SpaceStoreInstanceMock.emit(space1); diff --git a/test/stores/room-list/filters/VisibilityProvider-test.ts b/test/stores/room-list/filters/VisibilityProvider-test.ts index ca6c67dfb1..e8f2781fec 100644 --- a/test/stores/room-list/filters/VisibilityProvider-test.ts +++ b/test/stores/room-list/filters/VisibilityProvider-test.ts @@ -43,7 +43,7 @@ jest.mock("../../../../src/customisations/RoomList", () => ({ const createRoom = (isSpaceRoom = false): Room => { return { isSpaceRoom: () => isSpaceRoom, - getType: () => isSpaceRoom ? RoomType.Space : undefined, + getType: () => (isSpaceRoom ? RoomType.Space : undefined), } as unknown as Room; }; diff --git a/test/stores/room-list/previews/PollStartEventPreview-test.ts b/test/stores/room-list/previews/PollStartEventPreview-test.ts index b69e7da976..324f42d7b6 100644 --- a/test/stores/room-list/previews/PollStartEventPreview-test.ts +++ b/test/stores/room-list/previews/PollStartEventPreview-test.ts @@ -20,7 +20,7 @@ import { PollStartEventPreview } from "../../../../src/stores/room-list/previews import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; import { makePollStartEvent } from "../../../test-utils"; -jest.spyOn(MatrixClientPeg, 'get').mockReturnValue({ +jest.spyOn(MatrixClientPeg, "get").mockReturnValue({ getUserId: () => "@me:example.com", } as unknown as MatrixClient); @@ -37,4 +37,3 @@ describe("PollStartEventPreview", () => { expect(preview.getTextFor(pollStartEvent)).toBe("@yo:example.com: Your Question"); }); }); - diff --git a/test/stores/widgets/StopGapWidget-test.ts b/test/stores/widgets/StopGapWidget-test.ts index 717fcc77f1..1040b92f66 100644 --- a/test/stores/widgets/StopGapWidget-test.ts +++ b/test/stores/widgets/StopGapWidget-test.ts @@ -95,14 +95,12 @@ describe("StopGapWidget", () => { describe(`and receiving a action:${ElementWidgetActions.JoinCall} message`, () => { beforeEach(async () => { - messaging.on.mock.calls.find( - ([event, listener]) => { - if (event === `action:${ElementWidgetActions.JoinCall}`) { - listener(); - return true; - } - }, - ); + messaging.on.mock.calls.find(([event, listener]) => { + if (event === `action:${ElementWidgetActions.JoinCall}`) { + listener(); + return true; + } + }); }); it("should pause the current voice broadcast recording", () => { diff --git a/test/stores/widgets/StopGapWidgetDriver-test.ts b/test/stores/widgets/StopGapWidgetDriver-test.ts index 90214ec406..4c245f24b9 100644 --- a/test/stores/widgets/StopGapWidgetDriver-test.ts +++ b/test/stores/widgets/StopGapWidgetDriver-test.ts @@ -28,18 +28,19 @@ import { stubClient } from "../../test-utils"; describe("StopGapWidgetDriver", () => { let client: MockedObject; - const mkDefaultDriver = (): WidgetDriver => new StopGapWidgetDriver( - [], - new Widget({ - id: "test", - creatorUserId: "@alice:example.org", - type: "example", - url: "https://example.org", - }), - WidgetKind.Room, - false, - "!1:example.org", - ); + const mkDefaultDriver = (): WidgetDriver => + new StopGapWidgetDriver( + [], + new Widget({ + id: "test", + creatorUserId: "@alice:example.org", + type: "example", + url: "https://example.org", + }), + WidgetKind.Room, + false, + "!1:example.org", + ); beforeEach(() => { stubClient(); @@ -107,7 +108,7 @@ describe("StopGapWidgetDriver", () => { }, }, "@bob:example.org": { - "bobDesktop": { + bobDesktop: { hello: "bob", }, }, @@ -115,7 +116,9 @@ describe("StopGapWidgetDriver", () => { let driver: WidgetDriver; - beforeEach(() => { driver = mkDefaultDriver(); }); + beforeEach(() => { + driver = mkDefaultDriver(); + }); it("sends unencrypted messages", async () => { await driver.sendToDevice("org.example.foo", false, contentMap); @@ -140,7 +143,9 @@ describe("StopGapWidgetDriver", () => { describe("getTurnServers", () => { let driver: WidgetDriver; - beforeEach(() => { driver = mkDefaultDriver(); }); + beforeEach(() => { + driver = mkDefaultDriver(); + }); it("stops if VoIP isn't supported", async () => { jest.spyOn(client, "pollingTurnServers", "get").mockReturnValue(false); @@ -199,77 +204,72 @@ describe("StopGapWidgetDriver", () => { describe("readEventRelations", () => { let driver: WidgetDriver; - beforeEach(() => { driver = mkDefaultDriver(); }); + beforeEach(() => { + driver = mkDefaultDriver(); + }); - it('reads related events from the current room', async () => { - jest.spyOn(SdkContextClass.instance.roomViewStore, 'getRoomId').mockReturnValue('!this-room-id'); + it("reads related events from the current room", async () => { + jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId").mockReturnValue("!this-room-id"); client.relations.mockResolvedValue({ originalEvent: new MatrixEvent(), events: [], }); - await expect(driver.readEventRelations('$event')).resolves.toEqual({ + await expect(driver.readEventRelations("$event")).resolves.toEqual({ chunk: [], nextBatch: undefined, prevBatch: undefined, }); - expect(client.relations).toBeCalledWith('!this-room-id', '$event', null, null, {}); + expect(client.relations).toBeCalledWith("!this-room-id", "$event", null, null, {}); }); - it('reads related events from a selected room', async () => { + it("reads related events from a selected room", async () => { client.relations.mockResolvedValue({ originalEvent: new MatrixEvent(), events: [new MatrixEvent(), new MatrixEvent()], - nextBatch: 'next-batch-token', + nextBatch: "next-batch-token", }); - await expect(driver.readEventRelations('$event', '!room-id')).resolves.toEqual({ - chunk: [ - expect.objectContaining({ content: {} }), - expect.objectContaining({ content: {} }), - ], - nextBatch: 'next-batch-token', + await expect(driver.readEventRelations("$event", "!room-id")).resolves.toEqual({ + chunk: [expect.objectContaining({ content: {} }), expect.objectContaining({ content: {} })], + nextBatch: "next-batch-token", prevBatch: undefined, }); - expect(client.relations).toBeCalledWith('!room-id', '$event', null, null, {}); + expect(client.relations).toBeCalledWith("!room-id", "$event", null, null, {}); }); - it('reads related events with custom parameters', async () => { + it("reads related events with custom parameters", async () => { client.relations.mockResolvedValue({ originalEvent: new MatrixEvent(), events: [], }); - await expect(driver.readEventRelations( - '$event', - '!room-id', - 'm.reference', - 'm.room.message', - 'from-token', - 'to-token', - 25, - 'f', - )).resolves.toEqual({ + await expect( + driver.readEventRelations( + "$event", + "!room-id", + "m.reference", + "m.room.message", + "from-token", + "to-token", + 25, + "f", + ), + ).resolves.toEqual({ chunk: [], nextBatch: undefined, prevBatch: undefined, }); - expect(client.relations).toBeCalledWith( - '!room-id', - '$event', - 'm.reference', - 'm.room.message', - { - limit: 25, - from: 'from-token', - to: 'to-token', - dir: Direction.Forward, - }, - ); + expect(client.relations).toBeCalledWith("!room-id", "$event", "m.reference", "m.room.message", { + limit: 25, + from: "from-token", + to: "to-token", + dir: Direction.Forward, + }); }); }); }); diff --git a/test/stores/widgets/WidgetPermissionStore-test.ts b/test/stores/widgets/WidgetPermissionStore-test.ts index 3ebb7fc9f5..6ddc72a6d8 100644 --- a/test/stores/widgets/WidgetPermissionStore-test.ts +++ b/test/stores/widgets/WidgetPermissionStore-test.ts @@ -45,15 +45,13 @@ describe("WidgetPermissionStore", () => { mocked(SettingsStore.getValue).mockImplementation((setting: string) => { return settings[setting]; }); - mocked(SettingsStore.setValue).mockImplementation((settingName: string, - roomId: string | null, - level: SettingLevel, - value: any, - ): Promise => { - // the store doesn't use any specific level or room ID (room IDs are packed into keys in `value`) - settings[settingName] = value; - return Promise.resolve(); - }); + mocked(SettingsStore.setValue).mockImplementation( + (settingName: string, roomId: string | null, level: SettingLevel, value: any): Promise => { + // the store doesn't use any specific level or room ID (room IDs are packed into keys in `value`) + settings[settingName] = value; + return Promise.resolve(); + }, + ); mockClient = stubClient(); const context = new TestSdkContext(); context.client = mockClient; @@ -63,39 +61,29 @@ describe("WidgetPermissionStore", () => { it("should persist OIDCState.Allowed for a widget", () => { widgetPermissionStore.setOIDCState(w, WidgetKind.Account, null, OIDCState.Allowed); // check it remembered the value - expect( - widgetPermissionStore.getOIDCState(w, WidgetKind.Account, null), - ).toEqual(OIDCState.Allowed); + expect(widgetPermissionStore.getOIDCState(w, WidgetKind.Account, null)).toEqual(OIDCState.Allowed); }); it("should persist OIDCState.Denied for a widget", () => { widgetPermissionStore.setOIDCState(w, WidgetKind.Account, null, OIDCState.Denied); // check it remembered the value - expect( - widgetPermissionStore.getOIDCState(w, WidgetKind.Account, null), - ).toEqual(OIDCState.Denied); + expect(widgetPermissionStore.getOIDCState(w, WidgetKind.Account, null)).toEqual(OIDCState.Denied); }); it("should update OIDCState for a widget", () => { widgetPermissionStore.setOIDCState(w, WidgetKind.Account, null, OIDCState.Allowed); widgetPermissionStore.setOIDCState(w, WidgetKind.Account, null, OIDCState.Denied); // check it remembered the latest value - expect( - widgetPermissionStore.getOIDCState(w, WidgetKind.Account, null), - ).toEqual(OIDCState.Denied); + expect(widgetPermissionStore.getOIDCState(w, WidgetKind.Account, null)).toEqual(OIDCState.Denied); }); it("should scope the location for a widget when setting OIDC state", () => { // allow this widget for this room widgetPermissionStore.setOIDCState(w, WidgetKind.Room, roomId, OIDCState.Allowed); // check it remembered the value - expect( - widgetPermissionStore.getOIDCState(w, WidgetKind.Room, roomId), - ).toEqual(OIDCState.Allowed); + expect(widgetPermissionStore.getOIDCState(w, WidgetKind.Room, roomId)).toEqual(OIDCState.Allowed); // check this is not the case for the entire account - expect( - widgetPermissionStore.getOIDCState(w, WidgetKind.Account, roomId), - ).toEqual(OIDCState.Unknown); + expect(widgetPermissionStore.getOIDCState(w, WidgetKind.Account, roomId)).toEqual(OIDCState.Unknown); }); it("is created once in SdkContextClass", () => { const context = new SdkContextClass(); diff --git a/test/test-utils/beacon.ts b/test/test-utils/beacon.ts index 7ca5741bd5..12813c727d 100644 --- a/test/test-utils/beacon.ts +++ b/test/test-utils/beacon.ts @@ -16,12 +16,7 @@ limitations under the License. import { MockedObject } from "jest-mock"; import { makeBeaconInfoContent, makeBeaconContent } from "matrix-js-sdk/src/content-helpers"; -import { - MatrixClient, - MatrixEvent, - Beacon, - getBeaconInfoIdentifier, -} from "matrix-js-sdk/src/matrix"; +import { MatrixClient, MatrixEvent, Beacon, getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix"; import { M_BEACON, M_BEACON_INFO } from "matrix-js-sdk/src/@types/beacon"; import { LocationAssetType } from "matrix-js-sdk/src/@types/location"; @@ -50,13 +45,7 @@ export const makeBeaconInfoEvent = ( contentProps: Partial = {}, eventId?: string, ): MatrixEvent => { - const { - timeout, - isLive, - description, - assetType, - timestamp, - } = { + const { timeout, isLive, description, assetType, timestamp } = { ...DEFAULT_INFO_CONTENT_PROPS, ...contentProps, }; @@ -84,9 +73,9 @@ type ContentProps = { description?: string; }; const DEFAULT_CONTENT_PROPS: ContentProps = { - geoUri: 'geo:-36.24484561954707,175.46884959563613;u=10', + geoUri: "geo:-36.24484561954707,175.46884959563613;u=10", timestamp: 123, - beaconInfoId: '$123', + beaconInfoId: "$123", }; /** @@ -116,10 +105,13 @@ export const makeBeaconEvent = ( * Create a mock geolocation position * defaults all required properties */ -export const makeGeolocationPosition = ( - { timestamp, coords }: - { timestamp?: number, coords?: Partial }, -): GeolocationPosition => ({ +export const makeGeolocationPosition = ({ + timestamp, + coords, +}: { + timestamp?: number; + coords?: Partial; +}): GeolocationPosition => ({ timestamp: timestamp ?? 1647256791840, coords: { accuracy: 1, @@ -141,8 +133,8 @@ export const makeGeolocationPosition = ( export const mockGeolocation = (): MockedObject => { const mockGeolocation = { clearWatch: jest.fn(), - getCurrentPosition: jest.fn().mockImplementation(callback => callback(makeGeolocationPosition({}))), - watchPosition: jest.fn().mockImplementation(callback => callback(makeGeolocationPosition({}))), + getCurrentPosition: jest.fn().mockImplementation((callback) => callback(makeGeolocationPosition({}))), + watchPosition: jest.fn().mockImplementation((callback) => callback(makeGeolocationPosition({}))), } as unknown as MockedObject; // jest jsdom does not provide geolocation @@ -181,7 +173,7 @@ export const watchPositionMockImplementation = (delays: number[], errorCodes: nu totalDelay += delayMs; const timeout = window.setTimeout(() => { if (errorCodes[index]) { - error(getMockGeolocationPositionError(errorCodes[index], 'error message')); + error(getMockGeolocationPositionError(errorCodes[index], "error message")); } else { callback({ ...position, timestamp: position.timestamp + totalDelay }); } @@ -203,11 +195,11 @@ export const makeRoomWithBeacons = ( locationEvents?: MatrixEvent[], ): Beacon[] => { const room = makeRoomWithStateEvents(beaconInfoEvents, { roomId, mockClient }); - const beacons = beaconInfoEvents.map(event => room.currentState.beacons.get(getBeaconInfoIdentifier(event))); + const beacons = beaconInfoEvents.map((event) => room.currentState.beacons.get(getBeaconInfoIdentifier(event))); if (locationEvents) { - beacons.forEach(beacon => { + beacons.forEach((beacon) => { // this filtering happens in roomState, which is bypassed here - const validLocationEvents = locationEvents?.filter(event => event.getSender() === beacon.beaconInfoOwner); + const validLocationEvents = locationEvents?.filter((event) => event.getSender() === beacon.beaconInfoOwner); beacon.addLocations(validLocationEvents); }); } diff --git a/test/test-utils/call.ts b/test/test-utils/call.ts index 0ddedbb04a..04d9175b5e 100644 --- a/test/test-utils/call.ts +++ b/test/test-utils/call.ts @@ -45,21 +45,21 @@ export class MockedCall extends Call { public static get(room: Room): MockedCall | null { const [event] = room.currentState.getStateEvents(this.EVENT_TYPE); - return (event === undefined || "m.terminated" in event.getContent()) - ? null - : new MockedCall(room, event); + return event === undefined || "m.terminated" in event.getContent() ? null : new MockedCall(room, event); } public static create(room: Room, id: string) { - room.addLiveEvents([mkEvent({ - event: true, - type: this.EVENT_TYPE, - room: room.roomId, - user: "@alice:example.org", - content: { "m.type": "m.video", "m.intent": "m.prompt" }, - skey: id, - ts: Date.now(), - })]); + room.addLiveEvents([ + mkEvent({ + event: true, + type: this.EVENT_TYPE, + room: room.roomId, + user: "@alice:example.org", + content: { "m.type": "m.video", "m.intent": "m.prompt" }, + skey: id, + ts: Date.now(), + }), + ]); // @ts-ignore deliberately calling a private method // Let CallStore know that a call might now exist CallStore.instance.updateRoom(room); @@ -86,15 +86,17 @@ export class MockedCall extends Call { public destroy() { // Terminate the call for good measure - this.room.addLiveEvents([mkEvent({ - event: true, - type: MockedCall.EVENT_TYPE, - room: this.room.roomId, - user: "@alice:example.org", - content: { ...this.event.getContent(), "m.terminated": "Call ended" }, - skey: this.widget.id, - ts: Date.now(), - })]); + this.room.addLiveEvents([ + mkEvent({ + event: true, + type: MockedCall.EVENT_TYPE, + room: this.room.roomId, + user: "@alice:example.org", + content: { ...this.event.getContent(), "m.terminated": "Call ended" }, + skey: this.widget.id, + ts: Date.now(), + }), + ]); super.destroy(); } @@ -104,7 +106,7 @@ export class MockedCall extends Call { * Sets up the call store to use mocked calls. */ export const useMockedCalls = () => { - Call.get = room => MockedCall.get(room); - JitsiCall.create = async room => MockedCall.create(room, "1"); - ElementCall.create = async room => MockedCall.create(room, "1"); + Call.get = (room) => MockedCall.get(room); + JitsiCall.create = async (room) => MockedCall.create(room, "1"); + ElementCall.create = async (room) => MockedCall.create(room, "1"); }; diff --git a/test/test-utils/client.ts b/test/test-utils/client.ts index 1018690421..47e037b8fd 100644 --- a/test/test-utils/client.ts +++ b/test/test-utils/client.ts @@ -32,7 +32,7 @@ export class MockEventEmitter extends EventEmitter { * @param mockProperties An object with the mock property or function implementations. 'getters' * are correctly cloned to this event emitter. */ - constructor(mockProperties: Partial|PropertyLikeKeys, unknown>> = {}) { + constructor(mockProperties: Partial | PropertyLikeKeys, unknown>> = {}) { super(); // We must use defineProperties and not assign as the former clones getters correctly, // whereas the latter invokes the getter and sets the return value permanently on the @@ -70,17 +70,17 @@ export const getMockClientWithEventEmitter = ( ): MockedObject => { const mock = mocked(new MockClientWithEventEmitter(mockProperties) as unknown as MatrixClient); - jest.spyOn(MatrixClientPeg, 'get').mockReturnValue(mock); + jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mock); // @ts-ignore simplified test stub mock.canSupport = new Map(); - Object.keys(Feature).forEach(feature => { + Object.keys(Feature).forEach((feature) => { mock.canSupport.set(feature as Feature, ServerSupport.Stable); }); return mock; }; -export const unmockClientPeg = () => jest.spyOn(MatrixClientPeg, 'get').mockRestore(); +export const unmockClientPeg = () => jest.spyOn(MatrixClientPeg, "get").mockRestore(); /** * Returns basic mocked client methods related to the current user @@ -90,11 +90,11 @@ export const unmockClientPeg = () => jest.spyOn(MatrixClientPeg, 'get').mockRest }); * ``` */ -export const mockClientMethodsUser = (userId = '@alice:domain') => ({ +export const mockClientMethodsUser = (userId = "@alice:domain") => ({ getUserId: jest.fn().mockReturnValue(userId), getUser: jest.fn().mockReturnValue(new User(userId)), isGuest: jest.fn().mockReturnValue(false), - mxcUrlToHttp: jest.fn().mockReturnValue('mock-mxcUrlToHttp'), + mxcUrlToHttp: jest.fn().mockReturnValue("mock-mxcUrlToHttp"), credentials: { userId }, getThreePids: jest.fn().mockResolvedValue({ threepids: [] }), getAccessToken: jest.fn(), @@ -130,15 +130,15 @@ export const mockClientMethodsServer = (): Partial, unknown>> => ({ getDeviceId: jest.fn().mockReturnValue(deviceId), getDeviceEd25519Key: jest.fn(), getDevices: jest.fn().mockResolvedValue({ devices: [] }), }); -export const mockClientMethodsCrypto = (): Partial & PropertyLikeKeys, unknown> +export const mockClientMethodsCrypto = (): Partial< + Record & PropertyLikeKeys, unknown> > => ({ isCryptoEnabled: jest.fn(), isSecretStorageReady: jest.fn(), @@ -156,4 +156,3 @@ export const mockClientMethodsCrypto = (): Partial act(() => { - // couldn't get input event on contenteditable to work - // paste works without illegal private method access - const pasteEvent = { - clipboardData: { - types: [], - files: [], - getData: type => type === "text/plain" ? text : undefined, - }, - }; - fireEvent.paste(container.querySelector('[role="textbox"]'), pasteEvent); -}); +export const addTextToComposer = (container: HTMLElement, text: string) => + act(() => { + // couldn't get input event on contenteditable to work + // paste works without illegal private method access + const pasteEvent = { + clipboardData: { + types: [], + files: [], + getData: (type) => (type === "text/plain" ? text : undefined), + }, + }; + fireEvent.paste(container.querySelector('[role="textbox"]'), pasteEvent); + }); -export const addTextToComposerEnzyme = (wrapper: ReactWrapper, text: string) => act(() => { - // couldn't get input event on contenteditable to work - // paste works without illegal private method access - const pasteEvent = { - clipboardData: { - types: [], - files: [], - getData: type => type === "text/plain" ? text : undefined, - }, - }; - wrapper.find('[role="textbox"]').simulate('paste', pasteEvent); - wrapper.update(); -}); +export const addTextToComposerEnzyme = (wrapper: ReactWrapper, text: string) => + act(() => { + // couldn't get input event on contenteditable to work + // paste works without illegal private method access + const pasteEvent = { + clipboardData: { + types: [], + files: [], + getData: (type) => (type === "text/plain" ? text : undefined), + }, + }; + wrapper.find('[role="textbox"]').simulate("paste", pasteEvent); + wrapper.update(); + }); diff --git a/test/test-utils/console.ts b/test/test-utils/console.ts index f73c42568a..b4c4b98f91 100644 --- a/test/test-utils/console.ts +++ b/test/test-utils/console.ts @@ -30,12 +30,12 @@ const originalFunctions: FilteredConsole = { * @param ignoreList Messages to be filtered * @returns function to restore the console */ -export const filterConsole = (...ignoreList: string[]): () => void => { +export const filterConsole = (...ignoreList: string[]): (() => void) => { for (const [key, originalFunction] of Object.entries(originalFunctions)) { window.console[key as keyof FilteredConsole] = (...data: any[]) => { const message = data?.[0]?.message || data?.[0]; - if (typeof message === "string" && ignoreList.some(i => message.includes(i))) { + if (typeof message === "string" && ignoreList.some((i) => message.includes(i))) { return; } diff --git a/test/test-utils/index.ts b/test/test-utils/index.ts index 55b4779dc4..d9d4ff3e11 100644 --- a/test/test-utils/index.ts +++ b/test/test-utils/index.ts @@ -14,16 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. */ -export * from './beacon'; -export * from './client'; -export * from './location'; -export * from './platform'; -export * from './poll'; -export * from './room'; -export * from './test-utils'; -export * from './call'; -export * from './wrappers'; -export * from './utilities'; -export * from './date'; -export * from './relations'; -export * from './console'; +export * from "./beacon"; +export * from "./client"; +export * from "./location"; +export * from "./platform"; +export * from "./poll"; +export * from "./room"; +export * from "./test-utils"; +export * from "./call"; +export * from "./wrappers"; +export * from "./utilities"; +export * from "./date"; +export * from "./relations"; +export * from "./console"; diff --git a/test/test-utils/location.ts b/test/test-utils/location.ts index 39d84ef3d6..044259d52f 100644 --- a/test/test-utils/location.ts +++ b/test/test-utils/location.ts @@ -20,38 +20,35 @@ import { MatrixEvent, EventType } from "matrix-js-sdk/src/matrix"; let id = 1; export const makeLegacyLocationEvent = (geoUri: string): MatrixEvent => { - return new MatrixEvent( - { - "event_id": `$${++id}`, - "type": EventType.RoomMessage, - "content": { - "body": "Something about where I am", - "msgtype": "m.location", - "geo_uri": geoUri, - }, + return new MatrixEvent({ + event_id: `$${++id}`, + type: EventType.RoomMessage, + content: { + body: "Something about where I am", + msgtype: "m.location", + geo_uri: geoUri, }, - ); + }); }; export const makeLocationEvent = (geoUri: string, assetType?: LocationAssetType): MatrixEvent => { - return new MatrixEvent( - { - "event_id": `$${++id}`, - "type": M_LOCATION.name, - "content": makeLocationContent( - `Found at ${geoUri} at 2021-12-21T12:22+0000`, - geoUri, - 252523, - "Human-readable label", - assetType, - ), - }, - ); + return new MatrixEvent({ + event_id: `$${++id}`, + type: M_LOCATION.name, + content: makeLocationContent( + `Found at ${geoUri} at 2021-12-21T12:22+0000`, + geoUri, + 252523, + "Human-readable label", + assetType, + ), + }); }; // https://developer.mozilla.org/en-US/docs/Web/API/GeolocationPositionError export const getMockGeolocationPositionError = (code: number, message: string): GeolocationPositionError => ({ - code, message, + code, + message, PERMISSION_DENIED: 1, POSITION_UNAVAILABLE: 2, TIMEOUT: 3, diff --git a/test/test-utils/platform.ts b/test/test-utils/platform.ts index 1d61c1ef12..513e301dcb 100644 --- a/test/test-utils/platform.ts +++ b/test/test-utils/platform.ts @@ -37,10 +37,10 @@ export const mockPlatformPeg = ( platformMocks: Partial, unknown>> = {}, ): MockedObject => { const mockPlatform = new MockPlatform(platformMocks); - jest.spyOn(PlatformPeg, 'get').mockReturnValue(mockPlatform); + jest.spyOn(PlatformPeg, "get").mockReturnValue(mockPlatform); return mocked(mockPlatform); }; export const unmockPlatformPeg = () => { - jest.spyOn(PlatformPeg, 'get').mockRestore(); + jest.spyOn(PlatformPeg, "get").mockRestore(); }; diff --git a/test/test-utils/poll.ts b/test/test-utils/poll.ts index ca25b9eaa0..88b7c3035a 100644 --- a/test/test-utils/poll.ts +++ b/test/test-utils/poll.ts @@ -17,34 +17,28 @@ limitations under the License. import { MatrixEvent } from "matrix-js-sdk/src/matrix"; import { M_TEXT, M_POLL_START, POLL_ANSWER, M_POLL_KIND_DISCLOSED } from "matrix-events-sdk"; -export const makePollStartEvent = ( - question: string, - sender: string, - answers?: POLL_ANSWER[], -): MatrixEvent => { +export const makePollStartEvent = (question: string, sender: string, answers?: POLL_ANSWER[]): MatrixEvent => { if (!answers) { answers = [ - { "id": "socks", [M_TEXT.name]: "Socks" }, - { "id": "shoes", [M_TEXT.name]: "Shoes" }, + { id: "socks", [M_TEXT.name]: "Socks" }, + { id: "shoes", [M_TEXT.name]: "Shoes" }, ]; } - return new MatrixEvent( - { - "event_id": "$mypoll", - "room_id": "#myroom:example.com", - "sender": sender, - "type": M_POLL_START.name, - "content": { - [M_POLL_START.name]: { - "question": { - [M_TEXT.name]: question, - }, - "kind": M_POLL_KIND_DISCLOSED.name, - "answers": answers, + return new MatrixEvent({ + event_id: "$mypoll", + room_id: "#myroom:example.com", + sender: sender, + type: M_POLL_START.name, + content: { + [M_POLL_START.name]: { + question: { + [M_TEXT.name]: question, }, - [M_TEXT.name]: `${question}: answers`, + kind: M_POLL_KIND_DISCLOSED.name, + answers: answers, }, + [M_TEXT.name]: `${question}: answers`, }, - ); + }); }; diff --git a/test/test-utils/relations.ts b/test/test-utils/relations.ts index 5918750c2f..1190528623 100644 --- a/test/test-utils/relations.ts +++ b/test/test-utils/relations.ts @@ -20,9 +20,7 @@ import { RelationsContainer } from "matrix-js-sdk/src/models/relations-container import { PublicInterface } from "../@types/common"; export const mkRelations = (): Relations => { - return { - - } as PublicInterface as Relations; + return {} as PublicInterface as Relations; }; export const mkRelationsContainer = (): RelationsContainer => { diff --git a/test/test-utils/room.ts b/test/test-utils/room.ts index 44762500f1..d20200bef1 100644 --- a/test/test-utils/room.ts +++ b/test/test-utils/room.ts @@ -15,29 +15,23 @@ limitations under the License. */ import { MockedObject } from "jest-mock"; -import { - MatrixClient, - MatrixEvent, - EventType, - Room, -} from "matrix-js-sdk/src/matrix"; +import { MatrixClient, MatrixEvent, EventType, Room } from "matrix-js-sdk/src/matrix"; import { IRoomState } from "../../src/components/structures/RoomView"; import { TimelineRenderingType } from "../../src/contexts/RoomContext"; import { Layout } from "../../src/settings/enums/Layout"; import { mkEvent } from "./test-utils"; -export const makeMembershipEvent = ( - roomId: string, userId: string, membership = 'join', -) => mkEvent({ - event: true, - type: EventType.RoomMember, - room: roomId, - user: userId, - skey: userId, - content: { membership }, - ts: Date.now(), -}); +export const makeMembershipEvent = (roomId: string, userId: string, membership = "join") => + mkEvent({ + event: true, + type: EventType.RoomMember, + room: roomId, + user: userId, + skey: userId, + content: { membership }, + ts: Date.now(), + }); /** * Creates a room @@ -47,8 +41,9 @@ export const makeMembershipEvent = ( */ export const makeRoomWithStateEvents = ( stateEvents: MatrixEvent[] = [], - { roomId, mockClient }: { roomId: string, mockClient: MockedObject}): Room => { - const room1 = new Room(roomId, mockClient, '@user:server.org'); + { roomId, mockClient }: { roomId: string; mockClient: MockedObject }, +): Room => { + const room1 = new Room(roomId, mockClient, "@user:server.org"); room1.currentState.setStateEvents(stateEvents); mockClient.getRoom.mockReturnValue(room1); return room1; diff --git a/test/test-utils/test-utils.ts b/test/test-utils/test-utils.ts index 69b626dfd5..d0c7d34b45 100644 --- a/test/test-utils/test-utils.ts +++ b/test/test-utils/test-utils.ts @@ -15,9 +15,9 @@ limitations under the License. */ import EventEmitter from "events"; -import { mocked, MockedObject } from 'jest-mock'; +import { mocked, MockedObject } from "jest-mock"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; -import { JoinRule } from 'matrix-js-sdk/src/@types/partials'; +import { JoinRule } from "matrix-js-sdk/src/@types/partials"; import { Room, User, @@ -33,14 +33,14 @@ import { IPusher, RoomType, KNOWN_SAFE_ROOM_VERSION, -} from 'matrix-js-sdk/src/matrix'; +} from "matrix-js-sdk/src/matrix"; import { normalize } from "matrix-js-sdk/src/utils"; import { ReEmitter } from "matrix-js-sdk/src/ReEmitter"; import { MediaHandler } from "matrix-js-sdk/src/webrtc/mediaHandler"; import { Feature, ServerSupport } from "matrix-js-sdk/src/feature"; import type { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall"; -import { MatrixClientPeg as peg } from '../../src/MatrixClientPeg'; +import { MatrixClientPeg as peg } from "../../src/MatrixClientPeg"; import { makeType } from "../../src/utils/TypeUtils"; import { ValidatedServerConfig } from "../../src/utils/ValidatedServerConfig"; import { EnhancedMap } from "../../src/utils/maps"; @@ -62,12 +62,14 @@ export function stubClient(): MatrixClient { // // 'sandbox.restore()' doesn't work correctly on inherited methods, // so we do this for each method - jest.spyOn(peg, 'get'); - jest.spyOn(peg, 'unset'); - jest.spyOn(peg, 'replaceUsingCreds'); + jest.spyOn(peg, "get"); + jest.spyOn(peg, "unset"); + jest.spyOn(peg, "replaceUsingCreds"); // MatrixClientPeg.get() is called a /lot/, so implement it with our own // fast stub function rather than a sinon stub - peg.get = function() { return client; }; + peg.get = function () { + return client; + }; MatrixClientBackedSettingsHandler.matrixClient = client; return client; } @@ -108,7 +110,7 @@ export function createTestClient(): MatrixClient { }, getPushActionsForEvent: jest.fn(), - getRoom: jest.fn().mockImplementation(roomId => mkStubRoom(roomId, "My room", client)), + getRoom: jest.fn().mockImplementation((roomId) => mkStubRoom(roomId, "My room", client)), getRooms: jest.fn().mockReturnValue([]), getVisibleRooms: jest.fn().mockReturnValue([]), loginFlows: jest.fn(), @@ -196,7 +198,7 @@ export function createTestClient(): MatrixClient { } as unknown as MediaHandler), uploadContent: jest.fn(), getEventMapper: () => (opts) => new MatrixEvent(opts), - leaveRoomChain: jest.fn(roomId => ({ [roomId]: null })), + leaveRoomChain: jest.fn((roomId) => ({ [roomId]: null })), doesServerSupportLogoutDevices: jest.fn().mockReturnValue(true), requestPasswordEmailToken: jest.fn().mockRejectedValue({}), setPassword: jest.fn().mockRejectedValue({}), @@ -206,7 +208,7 @@ export function createTestClient(): MatrixClient { client.reEmitter = new ReEmitter(client); client.canSupport = new Map(); - Object.keys(Feature).forEach(feature => { + Object.keys(Feature).forEach((feature) => { client.canSupport.set(feature as Feature, ServerSupport.Stable); }); @@ -278,16 +280,26 @@ export function mkEvent(opts: MakeEventProps): MatrixEvent { }; if (opts.skey !== undefined) { event.state_key = opts.skey; - } else if ([ - "m.room.name", "m.room.topic", "m.room.create", "m.room.join_rules", - "m.room.power_levels", "m.room.topic", "m.room.history_visibility", - "m.room.encryption", "m.room.member", "com.example.state", - "m.room.guest_access", "m.room.tombstone", - ].indexOf(opts.type) !== -1) { + } else if ( + [ + "m.room.name", + "m.room.topic", + "m.room.create", + "m.room.join_rules", + "m.room.power_levels", + "m.room.topic", + "m.room.history_visibility", + "m.room.encryption", + "m.room.member", + "com.example.state", + "m.room.guest_access", + "m.room.tombstone", + ].indexOf(opts.type) !== -1 + ) { event.state_key = ""; } - const mxEvent = opts.event ? new MatrixEvent(event) : event as unknown as MatrixEvent; + const mxEvent = opts.event ? new MatrixEvent(event) : (event as unknown as MatrixEvent); if (!mxEvent.sender && opts.user && opts.room) { mxEvent.sender = { userId: opts.user, @@ -341,15 +353,17 @@ export function mkPresence(opts) { * @param {boolean} opts.event True to make a MatrixEvent. * @return {Object|MatrixEvent} The event */ -export function mkMembership(opts: MakeEventPassThruProps & { - room: Room["roomId"]; - mship: string; - prevMship?: string; - name?: string; - url?: string; - skey?: string; - target?: RoomMember; -}): MatrixEvent { +export function mkMembership( + opts: MakeEventPassThruProps & { + room: Room["roomId"]; + mship: string; + prevMship?: string; + name?: string; + url?: string; + skey?: string; + target?: RoomMember; + }, +): MatrixEvent { const event: MakeEventProps = { ...opts, type: "m.room.member", @@ -367,8 +381,12 @@ export function mkMembership(opts: MakeEventPassThruProps & { if (opts.prevMship) { event.prev_content = { membership: opts.prevMship }; } - if (opts.name) { event.content.displayname = opts.name; } - if (opts.url) { event.content.avatar_url = opts.url; } + if (opts.name) { + event.content.displayname = opts.name; + } + if (opts.url) { + event.content.avatar_url = opts.url; + } const e = mkEvent(event); if (opts.target) { e.target = opts.target; @@ -406,7 +424,11 @@ export type MessageEventProps = MakeEventPassThruProps & { * @param {string=} opts.msg Optional. The content.body for the event. * @return {Object|MatrixEvent} The event */ -export function mkMessage({ msg, relatesTo, ...opts }: MakeEventPassThruProps & { +export function mkMessage({ + msg, + relatesTo, + ...opts +}: MakeEventPassThruProps & { room: Room["roomId"]; msg?: string; }): MatrixEvent { @@ -420,7 +442,7 @@ export function mkMessage({ msg, relatesTo, ...opts }: MakeEventPassThruProps & content: { msgtype: "m.text", body: message, - ['m.relates_to']: relatesTo, + ["m.relates_to"]: relatesTo, }, }; @@ -433,12 +455,12 @@ export function mkStubRoom(roomId: string = null, name: string, client: MatrixCl roomId, getReceiptsForEvent: jest.fn().mockReturnValue([]), getMember: jest.fn().mockReturnValue({ - userId: '@member:domain.bla', - name: 'Member', - rawDisplayName: 'Member', + userId: "@member:domain.bla", + name: "Member", + rawDisplayName: "Member", roomId: roomId, - getAvatarUrl: () => 'mxc://avatar.url/image.png', - getMxcAvatarUrl: () => 'mxc://avatar.url/image.png', + getAvatarUrl: () => "mxc://avatar.url/image.png", + getMxcAvatarUrl: () => "mxc://avatar.url/image.png", }), getMembersWithMembership: jest.fn().mockReturnValue([]), getJoinedMembers: jest.fn().mockReturnValue([]), @@ -452,12 +474,12 @@ export function mkStubRoom(roomId: string = null, name: string, client: MatrixCl findEventById: () => null, getAccountData: () => null, hasMembershipState: () => null, - getVersion: () => '1', + getVersion: () => "1", shouldUpgradeToVersion: () => null, getMyMembership: jest.fn().mockReturnValue("join"), maySendMessage: jest.fn().mockReturnValue(true), currentState: { - getStateEvents: jest.fn((_type, key) => key === undefined ? [] : null), + getStateEvents: jest.fn((_type, key) => (key === undefined ? [] : null)), getMember: jest.fn(), mayClientSendStateEvent: jest.fn().mockReturnValue(true), maySendStateEvent: jest.fn().mockReturnValue(true), @@ -476,8 +498,8 @@ export function mkStubRoom(roomId: string = null, name: string, client: MatrixCl getDMInviter: jest.fn(), name, normalizedName: normalize(name || ""), - getAvatarUrl: () => 'mxc://avatar.url/room.png', - getMxcAvatarUrl: () => 'mxc://avatar.url/room.png', + getAvatarUrl: () => "mxc://avatar.url/room.png", + getMxcAvatarUrl: () => "mxc://avatar.url/room.png", isSpaceRoom: jest.fn().mockReturnValue(false), getType: jest.fn().mockReturnValue(undefined), isElementVideoRoom: jest.fn().mockReturnValue(false), @@ -524,7 +546,7 @@ export const resetAsyncStoreWithClient = async (store: AsyncStoreWi export const mockStateEventImplementation = (events: MatrixEvent[]) => { const stateMap = new EnhancedMap>(); - events.forEach(event => { + events.forEach((event) => { stateMap.getOrCreate(event.getType(), new Map()).set(event.getStateKey(), event); }); @@ -578,17 +600,21 @@ export const mkSpace = ( const space = mocked(mkRoom(client, spaceId, rooms)); space.isSpaceRoom.mockReturnValue(true); space.getType.mockReturnValue(RoomType.Space); - mocked(space.currentState).getStateEvents.mockImplementation(mockStateEventImplementation(children.map(roomId => - mkEvent({ - event: true, - type: EventType.SpaceChild, - room: spaceId, - user: "@user:server", - skey: roomId, - content: { via: [] }, - ts: Date.now(), - }), - ))); + mocked(space.currentState).getStateEvents.mockImplementation( + mockStateEventImplementation( + children.map((roomId) => + mkEvent({ + event: true, + type: EventType.SpaceChild, + room: spaceId, + user: "@user:server", + skey: roomId, + content: { via: [] }, + ts: Date.now(), + }), + ), + ), + ); return space; }; diff --git a/test/test-utils/threads.ts b/test/test-utils/threads.ts index 3b07c45051..43ea61db32 100644 --- a/test/test-utils/threads.ts +++ b/test/test-utils/threads.ts @@ -19,18 +19,24 @@ import { Thread } from "matrix-js-sdk/src/models/thread"; import { mkMessage, MessageEventProps } from "./test-utils"; -export const makeThreadEvent = ({ rootEventId, replyToEventId, ...props }: MessageEventProps & { - rootEventId: string; replyToEventId: string; -}): MatrixEvent => mkMessage({ - ...props, - relatesTo: { - event_id: rootEventId, - rel_type: "m.thread", - ['m.in_reply_to']: { - event_id: replyToEventId, +export const makeThreadEvent = ({ + rootEventId, + replyToEventId, + ...props +}: MessageEventProps & { + rootEventId: string; + replyToEventId: string; +}): MatrixEvent => + mkMessage({ + ...props, + relatesTo: { + event_id: rootEventId, + rel_type: "m.thread", + ["m.in_reply_to"]: { + event_id: replyToEventId, + }, }, - }, -}); + }); type MakeThreadEventsProps = { roomId: Room["roomId"]; @@ -48,13 +54,18 @@ type MakeThreadEventsProps = { }; export const makeThreadEvents = ({ - roomId, authorId, participantUserIds, length = 2, ts = 1, currentUserId, -}: MakeThreadEventsProps): { rootEvent: MatrixEvent, events: MatrixEvent[] } => { + roomId, + authorId, + participantUserIds, + length = 2, + ts = 1, + currentUserId, +}: MakeThreadEventsProps): { rootEvent: MatrixEvent; events: MatrixEvent[] } => { const rootEvent = mkMessage({ user: authorId, event: true, room: roomId, - msg: 'root event message ' + Math.random(), + msg: "root event message " + Math.random(), ts, }); @@ -65,16 +76,18 @@ export const makeThreadEvents = ({ const prevEvent = events[i - 1]; const replyToEventId = prevEvent.getId(); const user = participantUserIds[i % participantUserIds.length]; - events.push(makeThreadEvent({ - user, - room: roomId, - event: true, - msg: `reply ${i} by ${user}`, - rootEventId, - replyToEventId, - // replies are 1ms after each other - ts: ts + i, - })); + events.push( + makeThreadEvent({ + user, + room: roomId, + event: true, + msg: `reply ${i} by ${user}`, + rootEventId, + replyToEventId, + // replies are 1ms after each other + ts: ts + i, + }), + ); } rootEvent.setUnsigned({ @@ -106,7 +119,7 @@ export const mkThread = ({ participantUserIds, length = 2, ts = 1, -}: MakeThreadProps): { thread: Thread, rootEvent: MatrixEvent, events: MatrixEvent[] } => { +}: MakeThreadProps): { thread: Thread; rootEvent: MatrixEvent; events: MatrixEvent[] } => { const { rootEvent, events } = makeThreadEvents({ roomId: room.roomId, authorId, @@ -118,9 +131,7 @@ export const mkThread = ({ expect(rootEvent).toBeTruthy(); for (const evt of events) { - room?.reEmitter.reEmit(evt, [ - MatrixEventEvent.BeforeRedaction, - ]); + room?.reEmitter.reEmit(evt, [MatrixEventEvent.BeforeRedaction]); } const thread = room.createThread(rootEvent.getId(), rootEvent, events, true); diff --git a/test/test-utils/utilities.ts b/test/test-utils/utilities.ts index 0f22ed8467..58e0d34435 100644 --- a/test/test-utils/utilities.ts +++ b/test/test-utils/utilities.ts @@ -22,7 +22,7 @@ import { ActionPayload } from "../../src/dispatcher/payloads"; import defaultDispatcher from "../../src/dispatcher/dispatcher"; import { DispatcherAction } from "../../src/dispatcher/actions"; -export const emitPromise = (e: EventEmitter, k: string | symbol) => new Promise(r => e.once(k, r)); +export const emitPromise = (e: EventEmitter, k: string | symbol) => new Promise((r) => e.once(k, r)); /** * Waits for a certain payload to be dispatched. @@ -33,7 +33,9 @@ export const emitPromise = (e: EventEmitter, k: string | symbol) => new Promise( * Rejects when the timeout is reached. */ export function untilDispatch( - waitForAction: DispatcherAction | ((payload: ActionPayload) => boolean), dispatcher=defaultDispatcher, timeout=1000, + waitForAction: DispatcherAction | ((payload: ActionPayload) => boolean), + dispatcher = defaultDispatcher, + timeout = 1000, ): Promise { const callerLine = new Error().stack.toString().split("\n")[2]; if (typeof waitForAction === "string") { @@ -42,7 +44,7 @@ export function untilDispatch( return payload.action === action; }; } - const callback = waitForAction as ((payload: ActionPayload) => boolean); + const callback = waitForAction as (payload: ActionPayload) => boolean; return new Promise((resolve, reject) => { let fulfilled = false; let timeoutId; @@ -58,7 +60,8 @@ export function untilDispatch( // listen for dispatches const token = dispatcher.register((p: ActionPayload) => { const finishWaiting = callback(p); - if (finishWaiting || fulfilled) { // wait until we're told or we timeout + if (finishWaiting || fulfilled) { + // wait until we're told or we timeout // if we haven't timed out, resolve now with the payload. if (!fulfilled) { resolve(p); @@ -84,7 +87,10 @@ export function untilDispatch( * no callback is provided. Rejects when the timeout is reached. */ export function untilEmission( - emitter: EventEmitter, eventName: string, check: ((...args: any[]) => boolean)=undefined, timeout=1000, + emitter: EventEmitter, + eventName: string, + check: (...args: any[]) => boolean = undefined, + timeout = 1000, ): Promise { const callerLine = new Error().stack.toString().split("\n")[2]; return new Promise((resolve, reject) => { @@ -124,24 +130,23 @@ export function untilEmission( export const findByAttr = (attr: string) => (component: ReactWrapper, value: string) => component.find(`[${attr}="${value}"]`); -export const findByTestId = findByAttr('data-test-id'); -export const findById = findByAttr('id'); -export const findByAriaLabel = findByAttr('aria-label'); +export const findByTestId = findByAttr("data-test-id"); +export const findById = findByAttr("id"); +export const findByAriaLabel = findByAttr("aria-label"); -const findByTagAndAttr = (attr: string) => - (component: ReactWrapper, value: string, tag: string) => - component.find(`${tag}[${attr}="${value}"]`); +const findByTagAndAttr = (attr: string) => (component: ReactWrapper, value: string, tag: string) => + component.find(`${tag}[${attr}="${value}"]`); -export const findByTagAndTestId = findByTagAndAttr('data-test-id'); +export const findByTagAndTestId = findByTagAndAttr("data-test-id"); -export const flushPromises = async () => await new Promise(resolve => window.setTimeout(resolve)); +export const flushPromises = async () => await new Promise((resolve) => window.setTimeout(resolve)); // with jest's modern fake timers process.nextTick is also mocked, // flushing promises in the normal way then waits for some advancement // of the fake timers // https://gist.github.com/apieceofbart/e6dea8d884d29cf88cdb54ef14ddbcc4?permalink_comment_id=4018174#gistcomment-4018174 export const flushPromisesWithFakeTimers = async (): Promise => { - const promise = new Promise(resolve => process.nextTick(resolve)); + const promise = new Promise((resolve) => process.nextTick(resolve)); jest.advanceTimersByTime(1); await promise; }; @@ -179,6 +184,6 @@ export function waitForUpdate(inst: React.Component, updates = 1): Promise * that also checks timestamps */ export const advanceDateAndTime = (ms: number) => { - jest.spyOn(global.Date, 'now').mockReturnValue(Date.now() + ms); + jest.spyOn(global.Date, "now").mockReturnValue(Date.now() + ms); jest.advanceTimersByTime(ms); }; diff --git a/test/test-utils/wrappers.tsx b/test/test-utils/wrappers.tsx index 62c11ff1a6..8bcc33b137 100644 --- a/test/test-utils/wrappers.tsx +++ b/test/test-utils/wrappers.tsx @@ -17,7 +17,7 @@ limitations under the License. import React, { RefCallback, ComponentType } from "react"; import { MatrixClient } from "matrix-js-sdk/src/matrix"; -import { MatrixClientPeg as peg } from '../../src/MatrixClientPeg'; +import { MatrixClientPeg as peg } from "../../src/MatrixClientPeg"; import MatrixClientContext from "../../src/contexts/MatrixClientContext"; import { SDKContext, SdkContextClass } from "../../src/contexts/SDKContext"; @@ -33,9 +33,11 @@ export function wrapInMatrixClientContext(WrappedComponent: ComponentType) } render() { - return - - ; + return ( + + + + ); } } return Wrapper; @@ -47,9 +49,11 @@ export function wrapInSdkContext( ): ComponentType> { return class extends React.Component> { render() { - return - - ; + return ( + + + + ); } }; } diff --git a/test/theme-test.ts b/test/theme-test.ts index 8e0e6c94e1..01a23499b6 100644 --- a/test/theme-test.ts +++ b/test/theme-test.ts @@ -16,8 +16,8 @@ limitations under the License. import { setTheme } from "../src/theme"; -describe('theme', () => { - describe('setTheme', () => { +describe("theme", () => { + describe("setTheme", () => { let lightTheme; let darkTheme; @@ -27,30 +27,30 @@ describe('theme', () => { const styles = [ { attributes: { - 'data-mx-theme': { - value: 'light', + "data-mx-theme": { + value: "light", }, }, disabled: true, - href: 'urlLight', + href: "urlLight", onload: () => void 0, }, { attributes: { - 'data-mx-theme': { - value: 'dark', + "data-mx-theme": { + value: "dark", }, }, disabled: true, - href: 'urlDark', + href: "urlDark", onload: () => void 0, }, ]; lightTheme = styles[0]; darkTheme = styles[1]; - jest.spyOn(document.body, 'style', 'get').mockReturnValue([] as any); - spyQuerySelectorAll = jest.spyOn(document, 'querySelectorAll').mockReturnValue(styles as any); + jest.spyOn(document.body, "style", "get").mockReturnValue([] as any); + spyQuerySelectorAll = jest.spyOn(document, "querySelectorAll").mockReturnValue(styles as any); }); afterEach(() => { @@ -58,44 +58,46 @@ describe('theme', () => { jest.useRealTimers(); }); - it('should switch theme on onload call', async () => { + it("should switch theme on onload call", async () => { // When - await new Promise(resolve => { - setTheme('light').then(resolve); + await new Promise((resolve) => { + setTheme("light").then(resolve); lightTheme.onload(); }); // Then - expect(spyQuerySelectorAll).toHaveBeenCalledWith('[data-mx-theme]'); + expect(spyQuerySelectorAll).toHaveBeenCalledWith("[data-mx-theme]"); expect(spyQuerySelectorAll).toBeCalledTimes(1); expect(lightTheme.disabled).toBe(false); expect(darkTheme.disabled).toBe(true); }); - it('should reject promise on onerror call', () => { - return expect(new Promise(resolve => { - setTheme('light').catch(e => resolve(e)); - lightTheme.onerror('call onerror'); - })).resolves.toBe('call onerror'); + it("should reject promise on onerror call", () => { + return expect( + new Promise((resolve) => { + setTheme("light").catch((e) => resolve(e)); + lightTheme.onerror("call onerror"); + }), + ).resolves.toBe("call onerror"); }); - it('should switch theme if CSS are preloaded', async () => { + it("should switch theme if CSS are preloaded", async () => { // When - jest.spyOn(document, 'styleSheets', 'get').mockReturnValue([lightTheme] as any); + jest.spyOn(document, "styleSheets", "get").mockReturnValue([lightTheme] as any); - await setTheme('light'); + await setTheme("light"); // Then expect(lightTheme.disabled).toBe(false); expect(darkTheme.disabled).toBe(true); }); - it('should switch theme if CSS is loaded during pooling', async () => { + it("should switch theme if CSS is loaded during pooling", async () => { // When jest.useFakeTimers(); - await new Promise(resolve => { - setTheme('light').then(resolve); - jest.spyOn(document, 'styleSheets', 'get').mockReturnValue([lightTheme] as any); + await new Promise((resolve) => { + setTheme("light").then(resolve); + jest.spyOn(document, "styleSheets", "get").mockReturnValue([lightTheme] as any); jest.advanceTimersByTime(200); }); @@ -104,10 +106,10 @@ describe('theme', () => { expect(darkTheme.disabled).toBe(true); }); - it('should reject promise if pooling maximum value is reached', () => { + it("should reject promise if pooling maximum value is reached", () => { jest.useFakeTimers(); - return new Promise(resolve => { - setTheme('light').catch(resolve); + return new Promise((resolve) => { + setTheme("light").catch(resolve); jest.advanceTimersByTime(200 * 10); }); }); diff --git a/test/toasts/IncomingCallToast-test.tsx b/test/toasts/IncomingCallToast-test.tsx index c1fecea767..212042949e 100644 --- a/test/toasts/IncomingCallToast-test.tsx +++ b/test/toasts/IncomingCallToast-test.tsx @@ -42,7 +42,7 @@ import { getIncomingCallToastKey, IncomingCallToast } from "../../src/toasts/Inc describe("IncomingCallEvent", () => { useMockedCalls(); - jest.spyOn(HTMLMediaElement.prototype, "play").mockImplementation(async () => { }); + jest.spyOn(HTMLMediaElement.prototype, "play").mockImplementation(async () => {}); let client: Mocked; let room: Room; @@ -66,13 +66,15 @@ describe("IncomingCallEvent", () => { alice = mkRoomMember(room.roomId, "@alice:example.org"); bob = mkRoomMember(room.roomId, "@bob:example.org"); - client.getRoom.mockImplementation(roomId => roomId === room.roomId ? room : null); + client.getRoom.mockImplementation((roomId) => (roomId === room.roomId ? room : null)); client.getRooms.mockReturnValue([room]); client.reEmitter.reEmit(room, [RoomStateEvent.Events]); - await Promise.all([CallStore.instance, WidgetMessagingStore.instance].map( - store => setupAsyncStoreWithClient(store, client), - )); + await Promise.all( + [CallStore.instance, WidgetMessagingStore.instance].map((store) => + setupAsyncStoreWithClient(store, client), + ), + ); MockedCall.create(room, "1"); const maybeCall = CallStore.instance.getCall(room.roomId); @@ -81,7 +83,7 @@ describe("IncomingCallEvent", () => { widget = new Widget(call.widget); WidgetMessagingStore.instance.storeMessaging(widget, room.roomId, { - stop: () => { }, + stop: () => {}, } as unknown as ClientWidgetApi); jest.spyOn(DMRoomMap, "shared").mockReturnValue(dmRoomMap); @@ -96,7 +98,9 @@ describe("IncomingCallEvent", () => { jest.restoreAllMocks(); }); - const renderToast = () => { render(); }; + const renderToast = () => { + render(); + }; it("correctly shows all the information", () => { call.participants = new Map([ @@ -131,14 +135,16 @@ describe("IncomingCallEvent", () => { const dispatcherRef = defaultDispatcher.register(dispatcherSpy); fireEvent.click(screen.getByRole("button", { name: "Join" })); - await waitFor(() => expect(dispatcherSpy).toHaveBeenCalledWith({ - action: Action.ViewRoom, - room_id: room.roomId, - view_call: true, - })); - await waitFor(() => expect(toastStore.dismissToast).toHaveBeenCalledWith( - getIncomingCallToastKey(call.event.getStateKey()!), - )); + await waitFor(() => + expect(dispatcherSpy).toHaveBeenCalledWith({ + action: Action.ViewRoom, + room_id: room.roomId, + view_call: true, + }), + ); + await waitFor(() => + expect(toastStore.dismissToast).toHaveBeenCalledWith(getIncomingCallToastKey(call.event.getStateKey()!)), + ); defaultDispatcher.unregister(dispatcherRef); }); @@ -150,9 +156,9 @@ describe("IncomingCallEvent", () => { const dispatcherRef = defaultDispatcher.register(dispatcherSpy); fireEvent.click(screen.getByRole("button", { name: "Close" })); - await waitFor(() => expect(toastStore.dismissToast).toHaveBeenCalledWith( - getIncomingCallToastKey(call.event.getStateKey()!), - )); + await waitFor(() => + expect(toastStore.dismissToast).toHaveBeenCalledWith(getIncomingCallToastKey(call.event.getStateKey()!)), + ); defaultDispatcher.unregister(dispatcherRef); }); @@ -166,8 +172,8 @@ describe("IncomingCallEvent", () => { view_call: true, }); - await waitFor(() => expect(toastStore.dismissToast).toHaveBeenCalledWith( - getIncomingCallToastKey(call.event.getStateKey()!), - )); + await waitFor(() => + expect(toastStore.dismissToast).toHaveBeenCalledWith(getIncomingCallToastKey(call.event.getStateKey()!)), + ); }); }); diff --git a/test/toasts/IncomingLegacyCallToast-test.tsx b/test/toasts/IncomingLegacyCallToast-test.tsx index bdd2636094..4d90d432c5 100644 --- a/test/toasts/IncomingLegacyCallToast-test.tsx +++ b/test/toasts/IncomingLegacyCallToast-test.tsx @@ -13,21 +13,21 @@ 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 { render } from '@testing-library/react'; -import { LOCAL_NOTIFICATION_SETTINGS_PREFIX, MatrixEvent, Room } from 'matrix-js-sdk/src/matrix'; -import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call'; -import React from 'react'; +import { render } from "@testing-library/react"; +import { LOCAL_NOTIFICATION_SETTINGS_PREFIX, MatrixEvent, Room } from "matrix-js-sdk/src/matrix"; +import { MatrixCall } from "matrix-js-sdk/src/webrtc/call"; +import React from "react"; -import LegacyCallHandler from '../../src/LegacyCallHandler'; +import LegacyCallHandler from "../../src/LegacyCallHandler"; import IncomingLegacyCallToast from "../../src/toasts/IncomingLegacyCallToast"; -import DMRoomMap from '../../src/utils/DMRoomMap'; -import { getMockClientWithEventEmitter, mockClientMethodsServer, mockClientMethodsUser } from '../test-utils'; +import DMRoomMap from "../../src/utils/DMRoomMap"; +import { getMockClientWithEventEmitter, mockClientMethodsServer, mockClientMethodsUser } from "../test-utils"; -describe('', () => { - const userId = '@alice:server.org'; - const deviceId = 'my-device'; +describe("", () => { + const userId = "@alice:server.org"; + const deviceId = "my-device"; - jest.spyOn(DMRoomMap, 'shared').mockReturnValue({ + jest.spyOn(DMRoomMap, "shared").mockReturnValue({ getUserIdForRoomId: jest.fn(), } as unknown as DMRoomMap); @@ -36,7 +36,7 @@ describe('', () => { ...mockClientMethodsServer(), getRoom: jest.fn(), }); - const mockRoom = new Room('!room:server.org', mockClient, userId); + const mockRoom = new Room("!room:server.org", mockClient, userId); mockClient.deviceId = deviceId; const call = new MatrixCall({ client: mockClient, roomId: mockRoom.roomId }); @@ -51,18 +51,18 @@ describe('', () => { mockClient.getRoom.mockReturnValue(mockRoom); }); - it('renders when silence button when call is not silenced', () => { + it("renders when silence button when call is not silenced", () => { const { getByLabelText } = render(getComponent()); - expect(getByLabelText('Silence call')).toMatchSnapshot(); + expect(getByLabelText("Silence call")).toMatchSnapshot(); }); - it('renders sound on button when call is silenced', () => { + it("renders sound on button when call is silenced", () => { LegacyCallHandler.instance.silenceCall(call.callId); const { getByLabelText } = render(getComponent()); - expect(getByLabelText('Sound on')).toMatchSnapshot(); + expect(getByLabelText("Sound on")).toMatchSnapshot(); }); - it('renders disabled silenced button when call is forced to silent', () => { + it("renders disabled silenced button when call is forced to silent", () => { // silence local notifications -> force call ringer to silent mockClient.getAccountData.mockImplementation((eventType) => { if (eventType.includes(LOCAL_NOTIFICATION_SETTINGS_PREFIX.name)) { @@ -75,6 +75,6 @@ describe('', () => { } }); const { getByLabelText } = render(getComponent()); - expect(getByLabelText('Notifications silenced')).toMatchSnapshot(); + expect(getByLabelText("Notifications silenced")).toMatchSnapshot(); }); }); diff --git a/test/useTopic-test.tsx b/test/useTopic-test.tsx index 6ce8a1fcb3..5cab29e9b6 100644 --- a/test/useTopic-test.tsx +++ b/test/useTopic-test.tsx @@ -28,11 +28,11 @@ describe("useTopic", () => { stubClient(); const room = new Room("!TESTROOM", MatrixClientPeg.get(), "@alice:example.org"); const topic = mkEvent({ - type: 'm.room.topic', - room: '!TESTROOM', - user: '@alice:example.org', + type: "m.room.topic", + room: "!TESTROOM", + user: "@alice:example.org", content: { - topic: 'Test topic', + topic: "Test topic", }, ts: 123, event: true, @@ -42,7 +42,7 @@ describe("useTopic", () => { function RoomTopic() { const topic = useTopic(room); - return

{ topic.text }

; + return

{topic.text}

; } render(); @@ -50,11 +50,11 @@ describe("useTopic", () => { expect(screen.queryByText("Test topic")).toBeInTheDocument(); const updatedTopic = mkEvent({ - type: 'm.room.topic', - room: '!TESTROOM', - user: '@alice:example.org', + type: "m.room.topic", + room: "!TESTROOM", + user: "@alice:example.org", content: { - topic: 'New topic', + topic: "New topic", }, ts: 666, event: true, diff --git a/test/utils/DateUtils-test.ts b/test/utils/DateUtils-test.ts index 9cb020571e..2c72b26177 100644 --- a/test/utils/DateUtils-test.ts +++ b/test/utils/DateUtils-test.ts @@ -26,17 +26,17 @@ import { REPEATABLE_DATE } from "../test-utils"; describe("formatSeconds", () => { it("correctly formats time with hours", () => { - expect(formatSeconds((60 * 60 * 3) + (60 * 31) + (55))).toBe("03:31:55"); - expect(formatSeconds((60 * 60 * 3) + (60 * 0) + (55))).toBe("03:00:55"); - expect(formatSeconds((60 * 60 * 3) + (60 * 31) + (0))).toBe("03:31:00"); - expect(formatSeconds(-((60 * 60 * 3) + (60 * 31) + (0)))).toBe("-03:31:00"); + expect(formatSeconds(60 * 60 * 3 + 60 * 31 + 55)).toBe("03:31:55"); + expect(formatSeconds(60 * 60 * 3 + 60 * 0 + 55)).toBe("03:00:55"); + expect(formatSeconds(60 * 60 * 3 + 60 * 31 + 0)).toBe("03:31:00"); + expect(formatSeconds(-(60 * 60 * 3 + 60 * 31 + 0))).toBe("-03:31:00"); }); it("correctly formats time without hours", () => { - expect(formatSeconds((60 * 60 * 0) + (60 * 31) + (55))).toBe("31:55"); - expect(formatSeconds((60 * 60 * 0) + (60 * 0) + (55))).toBe("00:55"); - expect(formatSeconds((60 * 60 * 0) + (60 * 31) + (0))).toBe("31:00"); - expect(formatSeconds(-((60 * 60 * 0) + (60 * 31) + (0)))).toBe("-31:00"); + expect(formatSeconds(60 * 60 * 0 + 60 * 31 + 55)).toBe("31:55"); + expect(formatSeconds(60 * 60 * 0 + 60 * 0 + 55)).toBe("00:55"); + expect(formatSeconds(60 * 60 * 0 + 60 * 31 + 0)).toBe("31:00"); + expect(formatSeconds(-(60 * 60 * 0 + 60 * 31 + 0))).toBe("-31:00"); }); }); @@ -44,7 +44,7 @@ describe("formatRelativeTime", () => { let dateSpy; beforeAll(() => { dateSpy = jest - .spyOn(global.Date, 'now') + .spyOn(global.Date, "now") // Tuesday, 2 November 2021 11:18:03 UTC .mockImplementation(() => 1635851883000); }); @@ -81,24 +81,24 @@ describe("formatRelativeTime", () => { }); }); -describe('formatDuration()', () => { +describe("formatDuration()", () => { type TestCase = [string, string, number]; const MINUTE_MS = 60000; const HOUR_MS = MINUTE_MS * 60; it.each([ - ['rounds up to nearest day when more than 24h - 40 hours', '2d', 40 * HOUR_MS], - ['rounds down to nearest day when more than 24h - 26 hours', '1d', 26 * HOUR_MS], - ['24 hours', '1d', 24 * HOUR_MS], - ['rounds to nearest hour when less than 24h - 23h', '23h', 23 * HOUR_MS], - ['rounds to nearest hour when less than 24h - 6h and 10min', '6h', 6 * HOUR_MS + 10 * MINUTE_MS], - ['rounds to nearest hours when less than 24h', '2h', 2 * HOUR_MS + 124234], - ['rounds to nearest minute when less than 1h - 59 minutes', '59m', 59 * MINUTE_MS], - ['rounds to nearest minute when less than 1h - 1 minute', '1m', MINUTE_MS], - ['rounds to nearest second when less than 1min - 59 seconds', '59s', 59000], - ['rounds to 0 seconds when less than a second - 123ms', '0s', 123], - ])('%s formats to %s', (_description, expectedResult, input) => { + ["rounds up to nearest day when more than 24h - 40 hours", "2d", 40 * HOUR_MS], + ["rounds down to nearest day when more than 24h - 26 hours", "1d", 26 * HOUR_MS], + ["24 hours", "1d", 24 * HOUR_MS], + ["rounds to nearest hour when less than 24h - 23h", "23h", 23 * HOUR_MS], + ["rounds to nearest hour when less than 24h - 6h and 10min", "6h", 6 * HOUR_MS + 10 * MINUTE_MS], + ["rounds to nearest hours when less than 24h", "2h", 2 * HOUR_MS + 124234], + ["rounds to nearest minute when less than 1h - 59 minutes", "59m", 59 * MINUTE_MS], + ["rounds to nearest minute when less than 1h - 1 minute", "1m", MINUTE_MS], + ["rounds to nearest second when less than 1min - 59 seconds", "59s", 59000], + ["rounds to 0 seconds when less than a second - 123ms", "0s", 123], + ])("%s formats to %s", (_description, expectedResult, input) => { expect(formatDuration(input)).toEqual(expectedResult); }); }); @@ -109,12 +109,12 @@ describe("formatPreciseDuration", () => { const DAY_MS = HOUR_MS * 24; it.each<[string, string, number]>([ - ['3 days, 6 hours, 48 minutes, 59 seconds', '3d 6h 48m 59s', 3 * DAY_MS + 6 * HOUR_MS + 48 * MINUTE_MS + 59000], - ['6 hours, 48 minutes, 59 seconds', '6h 48m 59s', 6 * HOUR_MS + 48 * MINUTE_MS + 59000], - ['48 minutes, 59 seconds', '48m 59s', 48 * MINUTE_MS + 59000], - ['59 seconds', '59s', 59000], - ['0 seconds', '0s', 0], - ])('%s formats to %s', (_description, expectedResult, input) => { + ["3 days, 6 hours, 48 minutes, 59 seconds", "3d 6h 48m 59s", 3 * DAY_MS + 6 * HOUR_MS + 48 * MINUTE_MS + 59000], + ["6 hours, 48 minutes, 59 seconds", "6h 48m 59s", 6 * HOUR_MS + 48 * MINUTE_MS + 59000], + ["48 minutes, 59 seconds", "48m 59s", 48 * MINUTE_MS + 59000], + ["59 seconds", "59s", 59000], + ["0 seconds", "0s", 0], + ])("%s formats to %s", (_description, expectedResult, input) => { expect(formatPreciseDuration(input)).toEqual(expectedResult); }); }); diff --git a/test/utils/EventUtils-test.ts b/test/utils/EventUtils-test.ts index bf72dcd9fa..9f8105a6f7 100644 --- a/test/utils/EventUtils-test.ts +++ b/test/utils/EventUtils-test.ts @@ -41,9 +41,9 @@ import { } from "../../src/utils/EventUtils"; import { getMockClientWithEventEmitter, makeBeaconInfoEvent, makePollStartEvent, stubClient } from "../test-utils"; -describe('EventUtils', () => { - const userId = '@user:server'; - const roomId = '!room:server'; +describe("EventUtils", () => { + const userId = "@user:server"; + const roomId = "!room:server"; const mockClient = getMockClientWithEventEmitter({ getUserId: jest.fn().mockReturnValue(userId), }); @@ -52,7 +52,7 @@ describe('EventUtils', () => { mockClient.getUserId.mockClear().mockReturnValue(userId); }); afterAll(() => { - jest.spyOn(MatrixClientPeg, 'get').mockRestore(); + jest.spyOn(MatrixClientPeg, "get").mockRestore(); }); // setup events @@ -70,7 +70,7 @@ describe('EventUtils', () => { const stateEvent = new MatrixEvent({ type: EventType.RoomTopic, - state_key: '', + state_key: "", }); const beaconInfoEvent = makeBeaconInfoEvent(userId, roomId); @@ -84,13 +84,13 @@ describe('EventUtils', () => { sender: userId, }); - const pollStartEvent = makePollStartEvent('What?', userId); + const pollStartEvent = makePollStartEvent("What?", userId); const notDecryptedEvent = new MatrixEvent({ type: EventType.RoomMessage, sender: userId, content: { - msgtype: 'm.bad.encrypted', + msgtype: "m.bad.encrypted", }, }); @@ -115,7 +115,7 @@ describe('EventUtils', () => { sender: userId, content: { msgtype: MsgType.Text, - body: '', + body: "", }, }); @@ -133,54 +133,54 @@ describe('EventUtils', () => { sender: userId, content: { msgtype: MsgType.Text, - body: 'Hello', + body: "Hello", }, }); const bobsTextMessage = new MatrixEvent({ type: EventType.RoomMessage, - sender: '@bob:server', + sender: "@bob:server", content: { msgtype: MsgType.Text, - body: 'Hello from Bob', + body: "Hello from Bob", }, }); - describe('isContentActionable()', () => { + describe("isContentActionable()", () => { type TestCase = [string, MatrixEvent]; it.each([ - ['unsent event', unsentEvent], - ['redacted event', redactedEvent], - ['state event', stateEvent], - ['undecrypted event', notDecryptedEvent], - ['room member event', roomMemberEvent], - ['event without msgtype', noMsgType], - ['event without content body property', noContentBody], - ])('returns false for %s', (_description, event) => { + ["unsent event", unsentEvent], + ["redacted event", redactedEvent], + ["state event", stateEvent], + ["undecrypted event", notDecryptedEvent], + ["room member event", roomMemberEvent], + ["event without msgtype", noMsgType], + ["event without content body property", noContentBody], + ])("returns false for %s", (_description, event) => { expect(isContentActionable(event)).toBe(false); }); it.each([ - ['sticker event', stickerEvent], - ['poll start event', pollStartEvent], - ['event with empty content body', emptyContentBody], - ['event with a content body', niceTextMessage], - ['beacon_info event', beaconInfoEvent], - ])('returns true for %s', (_description, event) => { + ["sticker event", stickerEvent], + ["poll start event", pollStartEvent], + ["event with empty content body", emptyContentBody], + ["event with a content body", niceTextMessage], + ["beacon_info event", beaconInfoEvent], + ])("returns true for %s", (_description, event) => { expect(isContentActionable(event)).toBe(true); }); }); - describe('editable content helpers', () => { + describe("editable content helpers", () => { const replaceRelationEvent = new MatrixEvent({ type: EventType.RoomMessage, sender: userId, content: { msgtype: MsgType.Text, - body: 'Hello', - ['m.relates_to']: { + body: "Hello", + ["m.relates_to"]: { rel_type: RelationType.Replace, - event_id: '1', + event_id: "1", }, }, }); @@ -190,10 +190,10 @@ describe('EventUtils', () => { sender: userId, content: { msgtype: MsgType.Text, - body: 'Hello', - ['m.relates_to']: { + body: "Hello", + ["m.relates_to"]: { rel_type: RelationType.Reference, - event_id: '1', + event_id: "1", }, }, }); @@ -203,79 +203,79 @@ describe('EventUtils', () => { sender: userId, content: { msgtype: MsgType.Emote, - body: '🧪', + body: "🧪", }, }); type TestCase = [string, MatrixEvent]; const uneditableCases: TestCase[] = [ - ['redacted event', redactedEvent], - ['state event', stateEvent], - ['event that is not room message', roomMemberEvent], - ['event without msgtype', noMsgType], - ['event without content body property', noContentBody], - ['event with empty content body property', emptyContentBody], - ['event with non-string body', objectContentBody], - ['event not sent by current user', bobsTextMessage], - ['event with a replace relation', replaceRelationEvent], + ["redacted event", redactedEvent], + ["state event", stateEvent], + ["event that is not room message", roomMemberEvent], + ["event without msgtype", noMsgType], + ["event without content body property", noContentBody], + ["event with empty content body property", emptyContentBody], + ["event with non-string body", objectContentBody], + ["event not sent by current user", bobsTextMessage], + ["event with a replace relation", replaceRelationEvent], ]; const editableCases: TestCase[] = [ - ['event with reference relation', referenceRelationEvent], - ['emote event', emoteEvent], - ['poll start event', pollStartEvent], - ['event with a content body', niceTextMessage], + ["event with reference relation", referenceRelationEvent], + ["emote event", emoteEvent], + ["poll start event", pollStartEvent], + ["event with a content body", niceTextMessage], ]; - describe('canEditContent()', () => { - it.each(uneditableCases)('returns false for %s', (_description, event) => { + describe("canEditContent()", () => { + it.each(uneditableCases)("returns false for %s", (_description, event) => { expect(canEditContent(event)).toBe(false); }); - it.each(editableCases)('returns true for %s', (_description, event) => { + it.each(editableCases)("returns true for %s", (_description, event) => { expect(canEditContent(event)).toBe(true); }); }); - describe('canEditOwnContent()', () => { - it.each(uneditableCases)('returns false for %s', (_description, event) => { + describe("canEditOwnContent()", () => { + it.each(uneditableCases)("returns false for %s", (_description, event) => { expect(canEditOwnEvent(event)).toBe(false); }); - it.each(editableCases)('returns true for %s', (_description, event) => { + it.each(editableCases)("returns true for %s", (_description, event) => { expect(canEditOwnEvent(event)).toBe(true); }); }); }); - describe('isVoiceMessage()', () => { - it('returns true for an event with msc2516.voice content', () => { + describe("isVoiceMessage()", () => { + it("returns true for an event with msc2516.voice content", () => { const event = new MatrixEvent({ type: EventType.RoomMessage, content: { - ['org.matrix.msc2516.voice']: {}, + ["org.matrix.msc2516.voice"]: {}, }, }); expect(isVoiceMessage(event)).toBe(true); }); - it('returns true for an event with msc3245.voice content', () => { + it("returns true for an event with msc3245.voice content", () => { const event = new MatrixEvent({ type: EventType.RoomMessage, content: { - ['org.matrix.msc3245.voice']: {}, + ["org.matrix.msc3245.voice"]: {}, }, }); expect(isVoiceMessage(event)).toBe(true); }); - it('returns false for an event with voice content', () => { + it("returns false for an event with voice content", () => { const event = new MatrixEvent({ type: EventType.RoomMessage, content: { - body: 'hello', + body: "hello", }, }); @@ -283,20 +283,20 @@ describe('EventUtils', () => { }); }); - describe('isLocationEvent()', () => { - it('returns true for an event with m.location stable type', () => { + describe("isLocationEvent()", () => { + it("returns true for an event with m.location stable type", () => { const event = new MatrixEvent({ type: M_LOCATION.altName, }); expect(isLocationEvent(event)).toBe(true); }); - it('returns true for an event with m.location unstable prefixed type', () => { + it("returns true for an event with m.location unstable prefixed type", () => { const event = new MatrixEvent({ type: M_LOCATION.name, }); expect(isLocationEvent(event)).toBe(true); }); - it('returns true for a room message with stable m.location msgtype', () => { + it("returns true for a room message with stable m.location msgtype", () => { const event = new MatrixEvent({ type: EventType.RoomMessage, content: { @@ -305,7 +305,7 @@ describe('EventUtils', () => { }); expect(isLocationEvent(event)).toBe(true); }); - it('returns true for a room message with unstable m.location msgtype', () => { + it("returns true for a room message with unstable m.location msgtype", () => { const event = new MatrixEvent({ type: EventType.RoomMessage, content: { @@ -314,32 +314,31 @@ describe('EventUtils', () => { }); expect(isLocationEvent(event)).toBe(true); }); - it('returns false for a non location event', () => { + it("returns false for a non location event", () => { const event = new MatrixEvent({ type: EventType.RoomMessage, content: { - body: 'Hello', + body: "Hello", }, }); expect(isLocationEvent(event)).toBe(false); }); }); - describe('canCancel()', () => { - it.each([ - [EventStatus.QUEUED], - [EventStatus.NOT_SENT], - [EventStatus.ENCRYPTING], - ])('return true for status %s', (status) => { - expect(canCancel(status)).toBe(true); - }); + describe("canCancel()", () => { + it.each([[EventStatus.QUEUED], [EventStatus.NOT_SENT], [EventStatus.ENCRYPTING]])( + "return true for status %s", + (status) => { + expect(canCancel(status)).toBe(true); + }, + ); it.each([ [EventStatus.SENDING], [EventStatus.CANCELLED], [EventStatus.SENT], - ['invalid-status' as unknown as EventStatus], - ])('return false for status %s', (status) => { + ["invalid-status" as unknown as EventStatus], + ])("return false for status %s", (status) => { expect(canCancel(status)).toBe(false); }); }); @@ -358,16 +357,16 @@ describe('EventUtils', () => { event_id: NORMAL_EVENT, type: EventType.RoomMessage, content: { - "body": "Classic event", - "msgtype": MsgType.Text, + body: "Classic event", + msgtype: MsgType.Text, }, }, [THREAD_ROOT]: { event_id: THREAD_ROOT, type: EventType.RoomMessage, content: { - "body": "Thread root", - "msgtype": "m.text", + body: "Thread root", + msgtype: "m.text", }, unsigned: { "m.relations": { @@ -434,10 +433,12 @@ describe('EventUtils', () => { describe("findEditableEvent", () => { it("should not explode when given empty events array", () => { - expect(findEditableEvent({ - events: [], - isForward: true, - })).toBeUndefined(); + expect( + findEditableEvent({ + events: [], + isForward: true, + }), + ).toBeUndefined(); }); }); }); diff --git a/test/utils/FixedRollingArray-test.ts b/test/utils/FixedRollingArray-test.ts index 732a4f175e..f810d46d22 100644 --- a/test/utils/FixedRollingArray-test.ts +++ b/test/utils/FixedRollingArray-test.ts @@ -16,17 +16,17 @@ limitations under the License. import { FixedRollingArray } from "../../src/utils/FixedRollingArray"; -describe('FixedRollingArray', () => { - it('should seed the array with the given value', () => { +describe("FixedRollingArray", () => { + it("should seed the array with the given value", () => { const seed = "test"; const width = 24; const array = new FixedRollingArray(width, seed); expect(array.value.length).toBe(width); - expect(array.value.every(v => v === seed)).toBe(true); + expect(array.value.every((v) => v === seed)).toBe(true); }); - it('should insert at the correct end', () => { + it("should insert at the correct end", () => { const seed = "test"; const value = "changed"; const width = 24; @@ -37,7 +37,7 @@ describe('FixedRollingArray', () => { expect(array.value[0]).toBe(value); }); - it('should roll over', () => { + it("should roll over", () => { const seed = -1; const width = 24; const array = new FixedRollingArray(width, seed); diff --git a/test/utils/MegolmExportEncryption-test.ts b/test/utils/MegolmExportEncryption-test.ts index 65f132de53..8d5b4a2bdb 100644 --- a/test/utils/MegolmExportEncryption-test.ts +++ b/test/utils/MegolmExportEncryption-test.ts @@ -25,43 +25,43 @@ function getRandomValues(buf: T): T { return nodeCrypto.randomFillSync(buf); } -const TEST_VECTORS=[ +const TEST_VECTORS = [ [ "plain", "password", "-----BEGIN MEGOLM SESSION DATA-----\n" + - "AXNhbHRzYWx0c2FsdHNhbHSIiIiIiIiIiIiIiIiIiIiIAAAACmIRUW2OjZ3L2l6j9h0lHlV3M2dx\n" + - "cissyYBxjsfsAndErh065A8=\n" + - "-----END MEGOLM SESSION DATA-----", + "AXNhbHRzYWx0c2FsdHNhbHSIiIiIiIiIiIiIiIiIiIiIAAAACmIRUW2OjZ3L2l6j9h0lHlV3M2dx\n" + + "cissyYBxjsfsAndErh065A8=\n" + + "-----END MEGOLM SESSION DATA-----", ], [ "Hello, World", "betterpassword", "-----BEGIN MEGOLM SESSION DATA-----\n" + - "AW1vcmVzYWx0bW9yZXNhbHT//////////wAAAAAAAAAAAAAD6KyBpe1Niv5M5NPm4ZATsJo5nghk\n" + - "KYu63a0YQ5DRhUWEKk7CcMkrKnAUiZny\n" + - "-----END MEGOLM SESSION DATA-----", + "AW1vcmVzYWx0bW9yZXNhbHT//////////wAAAAAAAAAAAAAD6KyBpe1Niv5M5NPm4ZATsJo5nghk\n" + + "KYu63a0YQ5DRhUWEKk7CcMkrKnAUiZny\n" + + "-----END MEGOLM SESSION DATA-----", ], [ "alphanumericallyalphanumericallyalphanumericallyalphanumerically", "SWORDFISH", "-----BEGIN MEGOLM SESSION DATA-----\n" + - "AXllc3NhbHR5Z29vZG5lc3P//////////wAAAAAAAAAAAAAD6OIW+Je7gwvjd4kYrb+49gKCfExw\n" + - "MgJBMD4mrhLkmgAngwR1pHjbWXaoGybtiAYr0moQ93GrBQsCzPbvl82rZhaXO3iH5uHo/RCEpOqp\n" + - "Pgg29363BGR+/Ripq/VCLKGNbw==\n" + - "-----END MEGOLM SESSION DATA-----", + "AXllc3NhbHR5Z29vZG5lc3P//////////wAAAAAAAAAAAAAD6OIW+Je7gwvjd4kYrb+49gKCfExw\n" + + "MgJBMD4mrhLkmgAngwR1pHjbWXaoGybtiAYr0moQ93GrBQsCzPbvl82rZhaXO3iH5uHo/RCEpOqp\n" + + "Pgg29363BGR+/Ripq/VCLKGNbw==\n" + + "-----END MEGOLM SESSION DATA-----", ], [ "alphanumericallyalphanumericallyalphanumericallyalphanumerically", "passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassword" + - "passwordpasswordpasswordpasswordpassword", + "passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpasswordpasswordpasswordpasswordpassword" + + "passwordpasswordpasswordpasswordpassword", "-----BEGIN MEGOLM SESSION DATA-----\n" + - "Af//////////////////////////////////////////AAAD6IAZJy7IQ7Y0idqSw/bmpngEEVVh\n" + - "gsH+8ptgqxw6ZVWQnohr8JsuwH9SwGtiebZuBu5smPCO+RFVWH2cQYslZijXv/BEH/txvhUrrtCd\n" + - "bWnSXS9oymiqwUIGs08sXI33ZA==\n" + - "-----END MEGOLM SESSION DATA-----", + "Af//////////////////////////////////////////AAAD6IAZJy7IQ7Y0idqSw/bmpngEEVVh\n" + + "gsH+8ptgqxw6ZVWQnohr8JsuwH9SwGtiebZuBu5smPCO+RFVWH2cQYslZijXv/BEH/txvhUrrtCd\n" + + "bWnSXS9oymiqwUIGs08sXI33ZA==\n" + + "-----END MEGOLM SESSION DATA-----", ], ]; @@ -69,7 +69,7 @@ function stringToArray(s: string): ArrayBufferLike { return new TextEncoder().encode(s).buffer; } -describe('MegolmExportEncryption', function() { +describe("MegolmExportEncryption", function () { let MegolmExportEncryption; beforeEach(() => { @@ -87,76 +87,78 @@ describe('MegolmExportEncryption', function() { window.crypto = undefined; }); - describe('decrypt', function() { - it('should handle missing header', function() { - const input=stringToArray(`-----`); - return MegolmExportEncryption.decryptMegolmKeyFile(input, '') - .then((res) => { - throw new Error('expected to throw'); - }, (error) => { - expect(error.message).toEqual('Header line not found'); - }); + describe("decrypt", function () { + it("should handle missing header", function () { + const input = stringToArray(`-----`); + return MegolmExportEncryption.decryptMegolmKeyFile(input, "").then( + (res) => { + throw new Error("expected to throw"); + }, + (error) => { + expect(error.message).toEqual("Header line not found"); + }, + ); }); - it('should handle missing trailer', function() { - const input=stringToArray(`-----BEGIN MEGOLM SESSION DATA----- + it("should handle missing trailer", function () { + const input = stringToArray(`-----BEGIN MEGOLM SESSION DATA----- -----`); - return MegolmExportEncryption.decryptMegolmKeyFile(input, '') - .then((res) => { - throw new Error('expected to throw'); - }, (error) => { - expect(error.message).toEqual('Trailer line not found'); - }); + return MegolmExportEncryption.decryptMegolmKeyFile(input, "").then( + (res) => { + throw new Error("expected to throw"); + }, + (error) => { + expect(error.message).toEqual("Trailer line not found"); + }, + ); }); - it('should handle a too-short body', function() { - const input=stringToArray(`-----BEGIN MEGOLM SESSION DATA----- + it("should handle a too-short body", function () { + const input = stringToArray(`-----BEGIN MEGOLM SESSION DATA----- AXNhbHRzYWx0c2FsdHNhbHSIiIiIiIiIiIiIiIiIiIiIAAAACmIRUW2OjZ3L2l6j9h0lHlV3M2dx cissyYBxjsfsAn -----END MEGOLM SESSION DATA----- `); - return MegolmExportEncryption.decryptMegolmKeyFile(input, '') - .then((res) => { - throw new Error('expected to throw'); - }, (error) => { - expect(error.message).toEqual('Invalid file: too short'); - }); + return MegolmExportEncryption.decryptMegolmKeyFile(input, "").then( + (res) => { + throw new Error("expected to throw"); + }, + (error) => { + expect(error.message).toEqual("Invalid file: too short"); + }, + ); }); // TODO find a subtlecrypto shim which doesn't break this test - it.skip('should decrypt a range of inputs', function() { + it.skip("should decrypt a range of inputs", function () { function next(i) { if (i >= TEST_VECTORS.length) { return; } const [plain, password, input] = TEST_VECTORS[i]; - return MegolmExportEncryption.decryptMegolmKeyFile( - stringToArray(input), password, - ).then((decrypted) => { + return MegolmExportEncryption.decryptMegolmKeyFile(stringToArray(input), password).then((decrypted) => { expect(decrypted).toEqual(plain); - return next(i+1); + return next(i + 1); }); } return next(0); }); }); - describe('encrypt', function() { - it('should round-trip', function() { - const input = 'words words many words in plain text here'.repeat(100); + describe("encrypt", function () { + it("should round-trip", function () { + const input = "words words many words in plain text here".repeat(100); - const password = 'my super secret passphrase'; + const password = "my super secret passphrase"; - return MegolmExportEncryption.encryptMegolmKeyFile( - input, password, { kdf_rounds: 1000 }, - ).then((ciphertext) => { - return MegolmExportEncryption.decryptMegolmKeyFile( - ciphertext, password, - ); - }).then((plaintext) => { - expect(plaintext).toEqual(input); - }); + return MegolmExportEncryption.encryptMegolmKeyFile(input, password, { kdf_rounds: 1000 }) + .then((ciphertext) => { + return MegolmExportEncryption.decryptMegolmKeyFile(ciphertext, password); + }) + .then((plaintext) => { + expect(plaintext).toEqual(input); + }); }); }); }); diff --git a/test/utils/MultiInviter-test.ts b/test/utils/MultiInviter-test.ts index 49c2ebbeaf..042f857bed 100644 --- a/test/utils/MultiInviter-test.ts +++ b/test/utils/MultiInviter-test.ts @@ -14,32 +14,32 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { mocked } from 'jest-mock'; -import { MatrixClient } from 'matrix-js-sdk/src/matrix'; +import { mocked } from "jest-mock"; +import { MatrixClient } from "matrix-js-sdk/src/matrix"; -import { MatrixClientPeg } from '../../src/MatrixClientPeg'; -import Modal, { ModalManager } from '../../src/Modal'; -import SettingsStore from '../../src/settings/SettingsStore'; -import MultiInviter, { CompletionStates } from '../../src/utils/MultiInviter'; -import * as TestUtilsMatrix from '../test-utils'; +import { MatrixClientPeg } from "../../src/MatrixClientPeg"; +import Modal, { ModalManager } from "../../src/Modal"; +import SettingsStore from "../../src/settings/SettingsStore"; +import MultiInviter, { CompletionStates } from "../../src/utils/MultiInviter"; +import * as TestUtilsMatrix from "../test-utils"; -const ROOMID = '!room:server'; +const ROOMID = "!room:server"; -const MXID1 = '@user1:server'; -const MXID2 = '@user2:server'; -const MXID3 = '@user3:server'; +const MXID1 = "@user1:server"; +const MXID2 = "@user2:server"; +const MXID3 = "@user3:server"; const MXID_PROFILE_STATES = { [MXID1]: Promise.resolve({}), - [MXID2]: Promise.reject({ errcode: 'M_FORBIDDEN' }), - [MXID3]: Promise.reject({ errcode: 'M_NOT_FOUND' }), + [MXID2]: Promise.reject({ errcode: "M_FORBIDDEN" }), + [MXID3]: Promise.reject({ errcode: "M_NOT_FOUND" }), }; -jest.mock('../../src/Modal', () => ({ +jest.mock("../../src/Modal", () => ({ createDialog: jest.fn(), })); -jest.mock('../../src/settings/SettingsStore', () => ({ +jest.mock("../../src/settings/SettingsStore", () => ({ getValue: jest.fn(), monitorSetting: jest.fn(), watchSetting: jest.fn(), @@ -48,30 +48,28 @@ jest.mock('../../src/settings/SettingsStore', () => ({ const mockPromptBeforeInviteUnknownUsers = (value: boolean) => { mocked(SettingsStore.getValue).mockImplementation( (settingName: string, roomId: string = null, _excludeDefault = false): any => { - if (settingName === 'promptBeforeInviteUnknownUsers' && roomId === ROOMID) { + if (settingName === "promptBeforeInviteUnknownUsers" && roomId === ROOMID) { return value; } }, ); }; -const mockCreateTrackedDialog = (callbackName: 'onInviteAnyways'|'onGiveUp') => { - mocked(Modal.createDialog).mockImplementation( - (...rest: Parameters): any => { - rest[1][callbackName](); - }, - ); +const mockCreateTrackedDialog = (callbackName: "onInviteAnyways" | "onGiveUp") => { + mocked(Modal.createDialog).mockImplementation((...rest: Parameters): any => { + rest[1][callbackName](); + }); }; const expectAllInvitedResult = (result: CompletionStates) => { expect(result).toEqual({ - [MXID1]: 'invited', - [MXID2]: 'invited', - [MXID3]: 'invited', + [MXID1]: "invited", + [MXID2]: "invited", + [MXID3]: "invited", }); }; -describe('MultiInviter', () => { +describe("MultiInviter", () => { let client: jest.Mocked; let inviter: MultiInviter; @@ -92,11 +90,11 @@ describe('MultiInviter', () => { inviter = new MultiInviter(ROOMID); }); - describe('invite', () => { - describe('with promptBeforeInviteUnknownUsers = false', () => { + describe("invite", () => { + describe("with promptBeforeInviteUnknownUsers = false", () => { beforeEach(() => mockPromptBeforeInviteUnknownUsers(false)); - it('should invite all users', async () => { + it("should invite all users", async () => { const result = await inviter.invite([MXID1, MXID2, MXID3]); expect(client.invite).toHaveBeenCalledTimes(3); @@ -108,13 +106,13 @@ describe('MultiInviter', () => { }); }); - describe('with promptBeforeInviteUnknownUsers = true and', () => { + describe("with promptBeforeInviteUnknownUsers = true and", () => { beforeEach(() => mockPromptBeforeInviteUnknownUsers(true)); - describe('confirming the unknown user dialog', () => { - beforeEach(() => mockCreateTrackedDialog('onInviteAnyways')); + describe("confirming the unknown user dialog", () => { + beforeEach(() => mockCreateTrackedDialog("onInviteAnyways")); - it('should invite all users', async () => { + it("should invite all users", async () => { const result = await inviter.invite([MXID1, MXID2, MXID3]); expect(client.invite).toHaveBeenCalledTimes(3); @@ -126,10 +124,10 @@ describe('MultiInviter', () => { }); }); - describe('declining the unknown user dialog', () => { - beforeEach(() => mockCreateTrackedDialog('onGiveUp')); + describe("declining the unknown user dialog", () => { + beforeEach(() => mockCreateTrackedDialog("onGiveUp")); - it('should only invite existing users', async () => { + it("should only invite existing users", async () => { const result = await inviter.invite([MXID1, MXID2, MXID3]); expect(client.invite).toHaveBeenCalledTimes(1); diff --git a/test/utils/ShieldUtils-test.ts b/test/utils/ShieldUtils-test.ts index 10ccb80f96..45bf241ae4 100644 --- a/test/utils/ShieldUtils-test.ts +++ b/test/utils/ShieldUtils-test.ts @@ -14,13 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { - MatrixClient, - Room, -} from 'matrix-js-sdk/src/matrix'; +import { MatrixClient, Room } from "matrix-js-sdk/src/matrix"; -import { shieldStatusForRoom } from '../../src/utils/ShieldUtils'; -import DMRoomMap from '../../src/utils/DMRoomMap'; +import { shieldStatusForRoom } from "../../src/utils/ShieldUtils"; +import DMRoomMap from "../../src/utils/DMRoomMap"; function mkClient(selfTrust = false) { return { @@ -30,13 +27,13 @@ function mkClient(selfTrust = false) { wasCrossSigningVerified: () => userId[1] == "T" || userId[1] == "W", }), checkDeviceTrust: (userId, deviceId) => ({ - isVerified: () => userId === "@self:localhost" ? selfTrust : userId[2] == "T", + isVerified: () => (userId === "@self:localhost" ? selfTrust : userId[2] == "T"), }), getStoredDevicesForUser: (userId) => ["DEVICE"], } as unknown as MatrixClient; } -describe("mkClient self-test", function() { +describe("mkClient self-test", function () { test.each([true, false])("behaves well for self-trust=%s", (v) => { const client = mkClient(v); expect(client.checkDeviceTrust("@self:localhost", "DEVICE").isVerified()).toBe(v); @@ -46,8 +43,8 @@ describe("mkClient self-test", function() { ["@TT:h", true], ["@TF:h", true], ["@FT:h", false], - ["@FF:h", false]], - )("behaves well for user trust %s", (userId, trust) => { + ["@FF:h", false], + ])("behaves well for user trust %s", (userId, trust) => { expect(mkClient().checkUserTrust(userId).isCrossSigningVerified()).toBe(trust); }); @@ -55,28 +52,30 @@ describe("mkClient self-test", function() { ["@TT:h", true], ["@TF:h", false], ["@FT:h", true], - ["@FF:h", false]], - )("behaves well for device trust %s", (userId, trust) => { + ["@FF:h", false], + ])("behaves well for device trust %s", (userId, trust) => { expect(mkClient().checkDeviceTrust(userId, "device").isVerified()).toBe(trust); }); }); -describe("shieldStatusForMembership self-trust behaviour", function() { +describe("shieldStatusForMembership self-trust behaviour", function () { beforeAll(() => { const mockInstance = { - getUserIdForRoomId: (roomId) => roomId === "DM" ? "@any:h" : null, + getUserIdForRoomId: (roomId) => (roomId === "DM" ? "@any:h" : null), } as unknown as DMRoomMap; - jest.spyOn(DMRoomMap, 'shared').mockReturnValue(mockInstance); + jest.spyOn(DMRoomMap, "shared").mockReturnValue(mockInstance); }); afterAll(() => { - jest.spyOn(DMRoomMap, 'shared').mockRestore(); + jest.spyOn(DMRoomMap, "shared").mockRestore(); }); - it.each( - [[true, true], [true, false], - [false, true], [false, false]], - )("2 unverified: returns 'normal', self-trust = %s, DM = %s", async (trusted, dm) => { + it.each([ + [true, true], + [true, false], + [false, true], + [false, false], + ])("2 unverified: returns 'normal', self-trust = %s, DM = %s", async (trusted, dm) => { const client = mkClient(trusted); const room = { roomId: dm ? "DM" : "other", @@ -86,10 +85,12 @@ describe("shieldStatusForMembership self-trust behaviour", function() { expect(status).toEqual("normal"); }); - it.each( - [["verified", true, true], ["verified", true, false], - ["verified", false, true], ["warning", false, false]], - )("2 verified: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => { + it.each([ + ["verified", true, true], + ["verified", true, false], + ["verified", false, true], + ["warning", false, false], + ])("2 verified: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => { const client = mkClient(trusted); const room = { roomId: dm ? "DM" : "other", @@ -99,10 +100,12 @@ describe("shieldStatusForMembership self-trust behaviour", function() { expect(status).toEqual(result); }); - it.each( - [["normal", true, true], ["normal", true, false], - ["normal", false, true], ["warning", false, false]], - )("2 mixed: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => { + it.each([ + ["normal", true, true], + ["normal", true, false], + ["normal", false, true], + ["warning", false, false], + ])("2 mixed: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => { const client = mkClient(trusted); const room = { roomId: dm ? "DM" : "other", @@ -112,10 +115,12 @@ describe("shieldStatusForMembership self-trust behaviour", function() { expect(status).toEqual(result); }); - it.each( - [["verified", true, true], ["verified", true, false], - ["warning", false, true], ["warning", false, false]], - )("0 others: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => { + it.each([ + ["verified", true, true], + ["verified", true, false], + ["warning", false, true], + ["warning", false, false], + ])("0 others: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => { const client = mkClient(trusted); const room = { roomId: dm ? "DM" : "other", @@ -125,10 +130,12 @@ describe("shieldStatusForMembership self-trust behaviour", function() { expect(status).toEqual(result); }); - it.each( - [["verified", true, true], ["verified", true, false], - ["verified", false, true], ["verified", false, false]], - )("1 verified: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => { + it.each([ + ["verified", true, true], + ["verified", true, false], + ["verified", false, true], + ["verified", false, false], + ])("1 verified: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => { const client = mkClient(trusted); const room = { roomId: dm ? "DM" : "other", @@ -138,10 +145,12 @@ describe("shieldStatusForMembership self-trust behaviour", function() { expect(status).toEqual(result); }); - it.each( - [["normal", true, true], ["normal", true, false], - ["normal", false, true], ["normal", false, false]], - )("1 unverified: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => { + it.each([ + ["normal", true, true], + ["normal", true, false], + ["normal", false, true], + ["normal", false, false], + ])("1 unverified: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => { const client = mkClient(trusted); const room = { roomId: dm ? "DM" : "other", @@ -152,17 +161,18 @@ describe("shieldStatusForMembership self-trust behaviour", function() { }); }); -describe("shieldStatusForMembership other-trust behaviour", function() { +describe("shieldStatusForMembership other-trust behaviour", function () { beforeAll(() => { const mockInstance = { - getUserIdForRoomId: (roomId) => roomId === "DM" ? "@any:h" : null, + getUserIdForRoomId: (roomId) => (roomId === "DM" ? "@any:h" : null), } as unknown as DMRoomMap; - jest.spyOn(DMRoomMap, 'shared').mockReturnValue(mockInstance); + jest.spyOn(DMRoomMap, "shared").mockReturnValue(mockInstance); }); - it.each( - [["warning", true], ["warning", false]], - )("1 verified/untrusted: returns '%s', DM = %s", async (result, dm) => { + it.each([ + ["warning", true], + ["warning", false], + ])("1 verified/untrusted: returns '%s', DM = %s", async (result, dm) => { const client = mkClient(true); const room = { roomId: dm ? "DM" : "other", @@ -172,9 +182,10 @@ describe("shieldStatusForMembership other-trust behaviour", function() { expect(status).toEqual(result); }); - it.each( - [["warning", true], ["warning", false]], - )("2 verified/untrusted: returns '%s', DM = %s", async (result, dm) => { + it.each([ + ["warning", true], + ["warning", false], + ])("2 verified/untrusted: returns '%s', DM = %s", async (result, dm) => { const client = mkClient(true); const room = { roomId: dm ? "DM" : "other", @@ -184,9 +195,10 @@ describe("shieldStatusForMembership other-trust behaviour", function() { expect(status).toEqual(result); }); - it.each( - [["normal", true], ["normal", false]], - )("2 unverified/untrusted: returns '%s', DM = %s", async (result, dm) => { + it.each([ + ["normal", true], + ["normal", false], + ])("2 unverified/untrusted: returns '%s', DM = %s", async (result, dm) => { const client = mkClient(true); const room = { roomId: dm ? "DM" : "other", @@ -196,9 +208,10 @@ describe("shieldStatusForMembership other-trust behaviour", function() { expect(status).toEqual(result); }); - it.each( - [["warning", true], ["warning", false]], - )("2 was verified: returns '%s', DM = %s", async (result, dm) => { + it.each([ + ["warning", true], + ["warning", false], + ])("2 was verified: returns '%s', DM = %s", async (result, dm) => { const client = mkClient(true); const room = { roomId: dm ? "DM" : "other", diff --git a/test/utils/Singleflight-test.ts b/test/utils/Singleflight-test.ts index 148388dc71..a0b392c7c5 100644 --- a/test/utils/Singleflight-test.ts +++ b/test/utils/Singleflight-test.ts @@ -16,12 +16,12 @@ limitations under the License. import { Singleflight } from "../../src/utils/Singleflight"; -describe('Singleflight', () => { +describe("Singleflight", () => { afterEach(() => { Singleflight.forgetAll(); }); - it('should throw for bad context variables', () => { + it("should throw for bad context variables", () => { const permutations: [Object, string][] = [ [null, null], [{}, null], @@ -38,7 +38,7 @@ describe('Singleflight', () => { } }); - it('should execute the function once', () => { + it("should execute the function once", () => { const instance = {}; const key = "test"; const val = {}; // unique object for reference check @@ -52,7 +52,7 @@ describe('Singleflight', () => { expect(fn.mock.calls.length).toBe(1); }); - it('should execute the function once, even with new contexts', () => { + it("should execute the function once, even with new contexts", () => { const instance = {}; const key = "test"; const val = {}; // unique object for reference check @@ -67,7 +67,7 @@ describe('Singleflight', () => { expect(fn.mock.calls.length).toBe(1); }); - it('should execute the function twice if the result was forgotten', () => { + it("should execute the function twice if the result was forgotten", () => { const instance = {}; const key = "test"; const val = {}; // unique object for reference check @@ -82,7 +82,7 @@ describe('Singleflight', () => { expect(fn.mock.calls.length).toBe(2); }); - it('should execute the function twice if the instance was forgotten', () => { + it("should execute the function twice if the instance was forgotten", () => { const instance = {}; const key = "test"; const val = {}; // unique object for reference check @@ -97,7 +97,7 @@ describe('Singleflight', () => { expect(fn.mock.calls.length).toBe(2); }); - it('should execute the function twice if everything was forgotten', () => { + it("should execute the function twice if everything was forgotten", () => { const instance = {}; const key = "test"; const val = {}; // unique object for reference check @@ -112,4 +112,3 @@ describe('Singleflight', () => { expect(fn.mock.calls.length).toBe(2); }); }); - diff --git a/test/utils/SnakedObject-test.ts b/test/utils/SnakedObject-test.ts index d1a525773f..783df36e44 100644 --- a/test/utils/SnakedObject-test.ts +++ b/test/utils/SnakedObject-test.ts @@ -16,15 +16,15 @@ limitations under the License. import { SnakedObject, snakeToCamel } from "../../src/utils/SnakedObject"; -describe('snakeToCamel', () => { - it('should convert snake_case to camelCase in simple scenarios', () => { +describe("snakeToCamel", () => { + it("should convert snake_case to camelCase in simple scenarios", () => { expect(snakeToCamel("snake_case")).toBe("snakeCase"); expect(snakeToCamel("snake_case_but_longer")).toBe("snakeCaseButLonger"); expect(snakeToCamel("numbered_123")).toBe("numbered123"); // not a thing we would see normally }); // Not really something we expect to see, but it's defined behaviour of the function - it('should not camelCase a trailing or leading underscore', () => { + it("should not camelCase a trailing or leading underscore", () => { expect(snakeToCamel("_snake")).toBe("_snake"); expect(snakeToCamel("snake_")).toBe("snake_"); expect(snakeToCamel("_snake_case")).toBe("_snakeCase"); @@ -32,13 +32,13 @@ describe('snakeToCamel', () => { }); // Another thing we don't really expect to see, but is "defined behaviour" - it('should be predictable with double underscores', () => { + it("should be predictable with double underscores", () => { expect(snakeToCamel("__snake__")).toBe("_Snake_"); expect(snakeToCamel("snake__case")).toBe("snake_case"); }); }); -describe('SnakedObject', () => { +describe("SnakedObject", () => { /* eslint-disable camelcase*/ const input = { snake_case: "woot", @@ -48,12 +48,12 @@ describe('SnakedObject', () => { const snake = new SnakedObject(input); /* eslint-enable camelcase*/ - it('should prefer snake_case keys', () => { + it("should prefer snake_case keys", () => { expect(snake.get("snake_case")).toBe(input.snake_case); expect(snake.get("snake_case", "camelCase")).toBe(input.snake_case); }); - it('should fall back to camelCase keys when needed', () => { + it("should fall back to camelCase keys when needed", () => { // @ts-ignore - we're deliberately supplying a key that doesn't exist expect(snake.get("camel_case")).toBe(input.camelCase); diff --git a/test/utils/WidgetUtils-test.ts b/test/utils/WidgetUtils-test.ts index b43afaefec..9225c70d7f 100644 --- a/test/utils/WidgetUtils-test.ts +++ b/test/utils/WidgetUtils-test.ts @@ -19,23 +19,23 @@ import WidgetUtils from "../../src/utils/WidgetUtils"; import { mockPlatformPeg } from "../test-utils"; describe("getLocalJitsiWrapperUrl", () => { - it('should generate jitsi URL (for defaults)', () => { + it("should generate jitsi URL (for defaults)", () => { mockPlatformPeg(); expect(WidgetUtils.getLocalJitsiWrapperUrl()).toEqual( - 'https://app.element.io/jitsi.html' - + '#conferenceDomain=$domain' - + '&conferenceId=$conferenceId' - + '&isAudioOnly=$isAudioOnly' - + '&isVideoChannel=$isVideoChannel' - + '&displayName=$matrix_display_name' - + '&avatarUrl=$matrix_avatar_url' - + '&userId=$matrix_user_id' - + '&roomId=$matrix_room_id' - + '&theme=$theme' - + '&roomName=$roomName' - + '&supportsScreensharing=true' - + '&language=$org.matrix.msc2873.client_language', + "https://app.element.io/jitsi.html" + + "#conferenceDomain=$domain" + + "&conferenceId=$conferenceId" + + "&isAudioOnly=$isAudioOnly" + + "&isVideoChannel=$isVideoChannel" + + "&displayName=$matrix_display_name" + + "&avatarUrl=$matrix_avatar_url" + + "&userId=$matrix_user_id" + + "&roomId=$matrix_room_id" + + "&theme=$theme" + + "&roomName=$roomName" + + "&supportsScreensharing=true" + + "&language=$org.matrix.msc2873.client_language", ); }); }); diff --git a/test/utils/arrays-test.ts b/test/utils/arrays-test.ts index f0cc52e0a9..ccfa79915b 100644 --- a/test/utils/arrays-test.ts +++ b/test/utils/arrays-test.ts @@ -31,7 +31,7 @@ import { concat, } from "../../src/utils/arrays"; -type TestParams = { input: number[], output: number[] }; +type TestParams = { input: number[]; output: number[] }; type TestCase = [string, TestParams]; function expectSample(input: number[], expected: number[], smooth = false) { @@ -41,74 +41,70 @@ function expectSample(input: number[], expected: number[], smooth = false) { expect(result).toEqual(expected); } -describe('arrays', () => { - describe('arrayFastResample', () => { +describe("arrays", () => { + describe("arrayFastResample", () => { const downsampleCases: TestCase[] = [ - ['Odd -> Even', { input: [1, 2, 3, 4, 5], output: [1, 4] }], - ['Odd -> Odd', { input: [1, 2, 3, 4, 5], output: [1, 3, 5] }], - ['Even -> Odd', { input: [1, 2, 3, 4], output: [1, 2, 3] }], - ['Even -> Even', { input: [1, 2, 3, 4], output: [1, 3] }], + ["Odd -> Even", { input: [1, 2, 3, 4, 5], output: [1, 4] }], + ["Odd -> Odd", { input: [1, 2, 3, 4, 5], output: [1, 3, 5] }], + ["Even -> Odd", { input: [1, 2, 3, 4], output: [1, 2, 3] }], + ["Even -> Even", { input: [1, 2, 3, 4], output: [1, 3] }], ]; - it.each(downsampleCases)('downsamples correctly from %s', (_d, { input, output }) => + it.each(downsampleCases)("downsamples correctly from %s", (_d, { input, output }) => expectSample(input, output), ); const upsampleCases: TestCase[] = [ - ['Odd -> Even', { input: [1, 2, 3], output: [1, 1, 2, 2, 3, 3] }], - ['Odd -> Odd', { input: [1, 2, 3], output: [1, 1, 2, 2, 3] }], - ['Even -> Odd', { input: [1, 2], output: [1, 1, 1, 2, 2] }], - ['Even -> Even', { input: [1, 2], output: [1, 1, 1, 2, 2, 2] }], + ["Odd -> Even", { input: [1, 2, 3], output: [1, 1, 2, 2, 3, 3] }], + ["Odd -> Odd", { input: [1, 2, 3], output: [1, 1, 2, 2, 3] }], + ["Even -> Odd", { input: [1, 2], output: [1, 1, 1, 2, 2] }], + ["Even -> Even", { input: [1, 2], output: [1, 1, 1, 2, 2, 2] }], ]; - it.each(upsampleCases)('upsamples correctly from %s', (_d, { input, output }) => - expectSample(input, output), - ); + it.each(upsampleCases)("upsamples correctly from %s", (_d, { input, output }) => expectSample(input, output)); const maintainSampleCases: TestCase[] = [ - ['Odd', { input: [1, 2, 3], output: [1, 2, 3] }], // Odd - ['Even', { input: [1, 2], output: [1, 2] }], // Even + ["Odd", { input: [1, 2, 3], output: [1, 2, 3] }], // Odd + ["Even", { input: [1, 2], output: [1, 2] }], // Even ]; - it.each(maintainSampleCases)('maintains samples for %s', (_d, { input, output }) => + it.each(maintainSampleCases)("maintains samples for %s", (_d, { input, output }) => expectSample(input, output), ); }); - describe('arraySmoothingResample', () => { + describe("arraySmoothingResample", () => { // Dev note: these aren't great samples, but they demonstrate the bare minimum. Ideally // we'd be feeding a thousand values in and seeing what a curve of 250 values looks like, // but that's not really feasible to manually verify accuracy. const downsampleCases: TestCase[] = [ - ['Odd -> Even', { input: [4, 4, 1, 4, 4, 1, 4, 4, 1], output: [3, 3, 3, 3] }], - ['Odd -> Odd', { input: [4, 4, 1, 4, 4, 1, 4, 4, 1], output: [3, 3, 3] }], - ['Even -> Odd', { input: [4, 4, 1, 4, 4, 1, 4, 4], output: [3, 3, 3] }], - ['Even -> Even', { input: [4, 4, 1, 4, 4, 1, 4, 4], output: [3, 3] }], + ["Odd -> Even", { input: [4, 4, 1, 4, 4, 1, 4, 4, 1], output: [3, 3, 3, 3] }], + ["Odd -> Odd", { input: [4, 4, 1, 4, 4, 1, 4, 4, 1], output: [3, 3, 3] }], + ["Even -> Odd", { input: [4, 4, 1, 4, 4, 1, 4, 4], output: [3, 3, 3] }], + ["Even -> Even", { input: [4, 4, 1, 4, 4, 1, 4, 4], output: [3, 3] }], ]; - it.each(downsampleCases)('downsamples correctly from %s', (_d, { input, output }) => + it.each(downsampleCases)("downsamples correctly from %s", (_d, { input, output }) => expectSample(input, output, true), ); const upsampleCases: TestCase[] = [ - ['Odd -> Even', { input: [2, 0, 2], output: [2, 2, 0, 0, 2, 2] }], - ['Odd -> Odd', { input: [2, 0, 2], output: [2, 2, 0, 0, 2] }], - ['Even -> Odd', { input: [2, 0], output: [2, 2, 2, 0, 0] }], - ['Even -> Even', { input: [2, 0], output: [2, 2, 2, 0, 0, 0] }], + ["Odd -> Even", { input: [2, 0, 2], output: [2, 2, 0, 0, 2, 2] }], + ["Odd -> Odd", { input: [2, 0, 2], output: [2, 2, 0, 0, 2] }], + ["Even -> Odd", { input: [2, 0], output: [2, 2, 2, 0, 0] }], + ["Even -> Even", { input: [2, 0], output: [2, 2, 2, 0, 0, 0] }], ]; - it.each(upsampleCases)('upsamples correctly from %s', (_d, { input, output }) => + it.each(upsampleCases)("upsamples correctly from %s", (_d, { input, output }) => expectSample(input, output, true), ); const maintainCases: TestCase[] = [ - ['Odd', { input: [2, 0, 2], output: [2, 0, 2] }], - ['Even', { input: [2, 0], output: [2, 0] }], + ["Odd", { input: [2, 0, 2], output: [2, 0, 2] }], + ["Even", { input: [2, 0], output: [2, 0] }], ]; - it.each(maintainCases)('maintains samples for %s', (_d, { input, output }) => - expectSample(input, output), - ); + it.each(maintainCases)("maintains samples for %s", (_d, { input, output }) => expectSample(input, output)); }); - describe('arrayRescale', () => { - it('should rescale', () => { + describe("arrayRescale", () => { + it("should rescale", () => { const input = [8, 9, 1, 0, 2, 7, 10]; const output = [80, 90, 10, 0, 20, 70, 100]; const result = arrayRescale(input, 0, 100); @@ -118,8 +114,8 @@ describe('arrays', () => { }); }); - describe('arrayTrimFill', () => { - it('should shrink arrays', () => { + describe("arrayTrimFill", () => { + it("should shrink arrays", () => { const input = [1, 2, 3]; const output = [1, 2]; const seed = [4, 5, 6]; @@ -129,7 +125,7 @@ describe('arrays', () => { expect(result).toEqual(output); }); - it('should expand arrays', () => { + it("should expand arrays", () => { const input = [1, 2, 3]; const output = [1, 2, 3, 4, 5]; const seed = [4, 5, 6]; @@ -139,7 +135,7 @@ describe('arrays', () => { expect(result).toEqual(output); }); - it('should keep arrays the same', () => { + it("should keep arrays the same", () => { const input = [1, 2, 3]; const output = [1, 2, 3]; const seed = [4, 5, 6]; @@ -150,8 +146,8 @@ describe('arrays', () => { }); }); - describe('arraySeed', () => { - it('should create an array of given length', () => { + describe("arraySeed", () => { + it("should create an array of given length", () => { const val = 1; const output = [val, val, val]; const result = arraySeed(val, output.length); @@ -159,7 +155,7 @@ describe('arrays', () => { expect(result).toHaveLength(output.length); expect(result).toEqual(output); }); - it('should maintain pointers', () => { + it("should maintain pointers", () => { const val = {}; // this works because `{} !== {}`, which is what toEqual checks const output = [val, val, val]; const result = arraySeed(val, output.length); @@ -169,8 +165,8 @@ describe('arrays', () => { }); }); - describe('arrayFastClone', () => { - it('should break pointer reference on source array', () => { + describe("arrayFastClone", () => { + it("should break pointer reference on source array", () => { const val = {}; // we'll test to make sure the values maintain pointers too const input = [val, val, val]; const result = arrayFastClone(input); @@ -181,29 +177,29 @@ describe('arrays', () => { }); }); - describe('arrayHasOrderChange', () => { - it('should flag true on B ordering difference', () => { + describe("arrayHasOrderChange", () => { + it("should flag true on B ordering difference", () => { const a = [1, 2, 3]; const b = [3, 2, 1]; const result = arrayHasOrderChange(a, b); expect(result).toBe(true); }); - it('should flag false on no ordering difference', () => { + it("should flag false on no ordering difference", () => { const a = [1, 2, 3]; const b = [1, 2, 3]; const result = arrayHasOrderChange(a, b); expect(result).toBe(false); }); - it('should flag true on A length > B length', () => { + it("should flag true on A length > B length", () => { const a = [1, 2, 3, 4]; const b = [1, 2, 3]; const result = arrayHasOrderChange(a, b); expect(result).toBe(true); }); - it('should flag true on A length < B length', () => { + it("should flag true on A length < B length", () => { const a = [1, 2, 3]; const b = [1, 2, 3, 4]; const result = arrayHasOrderChange(a, b); @@ -211,36 +207,36 @@ describe('arrays', () => { }); }); - describe('arrayHasDiff', () => { - it('should flag true on A length > B length', () => { + describe("arrayHasDiff", () => { + it("should flag true on A length > B length", () => { const a = [1, 2, 3, 4]; const b = [1, 2, 3]; const result = arrayHasDiff(a, b); expect(result).toBe(true); }); - it('should flag true on A length < B length', () => { + it("should flag true on A length < B length", () => { const a = [1, 2, 3]; const b = [1, 2, 3, 4]; const result = arrayHasDiff(a, b); expect(result).toBe(true); }); - it('should flag true on element differences', () => { + it("should flag true on element differences", () => { const a = [1, 2, 3]; const b = [4, 5, 6]; const result = arrayHasDiff(a, b); expect(result).toBe(true); }); - it('should flag false if same but order different', () => { + it("should flag false if same but order different", () => { const a = [1, 2, 3]; const b = [3, 1, 2]; const result = arrayHasDiff(a, b); expect(result).toBe(false); }); - it('should flag false if same', () => { + it("should flag false if same", () => { const a = [1, 2, 3]; const b = [1, 2, 3]; const result = arrayHasDiff(a, b); @@ -248,8 +244,8 @@ describe('arrays', () => { }); }); - describe('arrayDiff', () => { - it('should see added from A->B', () => { + describe("arrayDiff", () => { + it("should see added from A->B", () => { const a = [1, 2, 3]; const b = [1, 2, 3, 4]; const result = arrayDiff(a, b); @@ -261,7 +257,7 @@ describe('arrays', () => { expect(result.added).toEqual([4]); }); - it('should see removed from A->B', () => { + it("should see removed from A->B", () => { const a = [1, 2, 3]; const b = [1, 2]; const result = arrayDiff(a, b); @@ -273,7 +269,7 @@ describe('arrays', () => { expect(result.removed).toEqual([3]); }); - it('should see added and removed in the same set', () => { + it("should see added and removed in the same set", () => { const a = [1, 2, 3]; const b = [1, 2, 4]; // note diff const result = arrayDiff(a, b); @@ -287,8 +283,8 @@ describe('arrays', () => { }); }); - describe('arrayIntersection', () => { - it('should return the intersection', () => { + describe("arrayIntersection", () => { + it("should return the intersection", () => { const a = [1, 2, 3]; const b = [1, 2, 4]; // note diff const result = arrayIntersection(a, b); @@ -297,7 +293,7 @@ describe('arrays', () => { expect(result).toEqual([1, 2]); }); - it('should return an empty array on no matches', () => { + it("should return an empty array on no matches", () => { const a = [1, 2, 3]; const b = [4, 5, 6]; const result = arrayIntersection(a, b); @@ -306,8 +302,8 @@ describe('arrays', () => { }); }); - describe('arrayUnion', () => { - it('should union 3 arrays with deduplication', () => { + describe("arrayUnion", () => { + it("should union 3 arrays with deduplication", () => { const a = [1, 2, 3]; const b = [1, 2, 4, 5]; // note missing 3 const c = [6, 7, 8, 9]; @@ -317,7 +313,7 @@ describe('arrays', () => { expect(result).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]); }); - it('should deduplicate a single array', () => { + it("should deduplicate a single array", () => { // dev note: this is technically an edge case, but it is described behaviour if the // function is only provided one array (it'll merge the array against itself) const a = [1, 1, 2, 2, 3, 3]; @@ -328,21 +324,35 @@ describe('arrays', () => { }); }); - describe('ArrayUtil', () => { - it('should maintain the pointer to the given array', () => { + describe("ArrayUtil", () => { + it("should maintain the pointer to the given array", () => { const input = [1, 2, 3]; const result = new ArrayUtil(input); expect(result.value).toBe(input); }); - it('should group appropriately', () => { - const input = [['a', 1], ['b', 2], ['c', 3], ['a', 4], ['a', 5], ['b', 6]]; + it("should group appropriately", () => { + const input = [ + ["a", 1], + ["b", 2], + ["c", 3], + ["a", 4], + ["a", 5], + ["b", 6], + ]; const output = { - 'a': [['a', 1], ['a', 4], ['a', 5]], - 'b': [['b', 2], ['b', 6]], - 'c': [['c', 3]], + a: [ + ["a", 1], + ["a", 4], + ["a", 5], + ], + b: [ + ["b", 2], + ["b", 6], + ], + c: [["c", 3]], }; - const result = new ArrayUtil(input).groupBy(p => p[0]); + const result = new ArrayUtil(input).groupBy((p) => p[0]); expect(result).toBeDefined(); expect(result.value).toBeDefined(); @@ -351,25 +361,25 @@ describe('arrays', () => { }); }); - describe('GroupedArray', () => { - it('should maintain the pointer to the given map', () => { + describe("GroupedArray", () => { + it("should maintain the pointer to the given map", () => { const input = new Map([ - ['a', [1, 2, 3]], - ['b', [7, 8, 9]], - ['c', [4, 5, 6]], + ["a", [1, 2, 3]], + ["b", [7, 8, 9]], + ["c", [4, 5, 6]], ]); const result = new GroupedArray(input); expect(result.value).toBe(input); }); - it('should ordering by the provided key order', () => { + it("should ordering by the provided key order", () => { const input = new Map([ - ['a', [1, 2, 3]], - ['b', [7, 8, 9]], // note counting diff - ['c', [4, 5, 6]], + ["a", [1, 2, 3]], + ["b", [7, 8, 9]], // note counting diff + ["c", [4, 5, 6]], ]); const output = [4, 5, 6, 1, 2, 3, 7, 8, 9]; - const keyOrder = ['c', 'a', 'b']; // note weird order to cause the `output` to be strange + const keyOrder = ["c", "a", "b"]; // note weird order to cause the `output` to be strange const result = new GroupedArray(input).orderBy(keyOrder); expect(result).toBeDefined(); expect(result.value).toBeDefined(); @@ -404,4 +414,3 @@ describe('arrays', () => { }); }); }); - diff --git a/test/utils/beacon/bounds-test.ts b/test/utils/beacon/bounds-test.ts index bd4b37234b..c24a72383a 100644 --- a/test/utils/beacon/bounds-test.ts +++ b/test/utils/beacon/bounds-test.ts @@ -19,10 +19,10 @@ import { Beacon } from "matrix-js-sdk/src/matrix"; import { Bounds, getBeaconBounds } from "../../../src/utils/beacon/bounds"; import { makeBeaconEvent, makeBeaconInfoEvent } from "../../test-utils"; -describe('getBeaconBounds()', () => { - const userId = '@user:server'; - const roomId = '!room:server'; - const makeBeaconWithLocation = (latLon: {lat: number, lon: number}) => { +describe("getBeaconBounds()", () => { + const userId = "@user:server"; + const roomId = "!room:server"; + const makeBeaconWithLocation = (latLon: { lat: number; lon: number }) => { const geoUri = `geo:${latLon.lat},${latLon.lon}`; const beacon = new Beacon(makeBeaconInfoEvent(userId, roomId, { isLive: true })); // @ts-ignore private prop, sets internal live property so addLocations works @@ -57,39 +57,45 @@ describe('getBeaconBounds()', () => { const auckland = makeBeaconWithLocation(geo.auckland); const lima = makeBeaconWithLocation(geo.lima); - it('should return undefined when there are no beacons', () => { + it("should return undefined when there are no beacons", () => { expect(getBeaconBounds([])).toBeUndefined(); }); - it('should return undefined when no beacons have locations', () => { + it("should return undefined when no beacons have locations", () => { const beacon = new Beacon(makeBeaconInfoEvent(userId, roomId)); expect(getBeaconBounds([beacon])).toBeUndefined(); }); type TestCase = [string, Beacon[], Bounds]; it.each([ - ['one beacon', [london], + [ + "one beacon", + [london], { north: geo.london.lat, south: geo.london.lat, east: geo.london.lon, west: geo.london.lon }, ], - ['beacons in the northern hemisphere, west of meridian', + [ + "beacons in the northern hemisphere, west of meridian", [london, reykjavik], { north: geo.reykjavik.lat, south: geo.london.lat, east: geo.london.lon, west: geo.reykjavik.lon }, ], - ['beacons in the northern hemisphere, both sides of meridian', + [ + "beacons in the northern hemisphere, both sides of meridian", [london, reykjavik, paris], // reykjavik northmost and westmost, paris southmost and eastmost { north: geo.reykjavik.lat, south: geo.paris.lat, east: geo.paris.lon, west: geo.reykjavik.lon }, ], - ['beacons in the southern hemisphere', + [ + "beacons in the southern hemisphere", [auckland, lima], // lima northmost and westmost, auckland southmost and eastmost { north: geo.lima.lat, south: geo.auckland.lat, east: geo.auckland.lon, west: geo.lima.lon }, ], - ['beacons in both hemispheres', + [ + "beacons in both hemispheres", [auckland, lima, paris], { north: geo.paris.lat, south: geo.auckland.lat, east: geo.auckland.lon, west: geo.lima.lon }, ], - ])('gets correct bounds for %s', (_description, beacons, expectedBounds) => { + ])("gets correct bounds for %s", (_description, beacons, expectedBounds) => { expect(getBeaconBounds(beacons)).toEqual(expectedBounds); }); }); diff --git a/test/utils/beacon/duration-test.ts b/test/utils/beacon/duration-test.ts index e8a0d36c63..ed71865f29 100644 --- a/test/utils/beacon/duration-test.ts +++ b/test/utils/beacon/duration-test.ts @@ -16,35 +16,31 @@ limitations under the License. import { Beacon } from "matrix-js-sdk/src/matrix"; -import { - msUntilExpiry, - sortBeaconsByLatestExpiry, - sortBeaconsByLatestCreation, -} from "../../../src/utils/beacon"; +import { msUntilExpiry, sortBeaconsByLatestExpiry, sortBeaconsByLatestCreation } from "../../../src/utils/beacon"; import { makeBeaconInfoEvent } from "../../test-utils"; -describe('beacon utils', () => { +describe("beacon utils", () => { // 14.03.2022 16:15 const now = 1647270879403; const HOUR_MS = 3600000; beforeEach(() => { - jest.spyOn(global.Date, 'now').mockReturnValue(now); + jest.spyOn(global.Date, "now").mockReturnValue(now); }); afterAll(() => { - jest.spyOn(global.Date, 'now').mockRestore(); + jest.spyOn(global.Date, "now").mockRestore(); }); - describe('msUntilExpiry', () => { - it('returns remaining duration', () => { + describe("msUntilExpiry", () => { + it("returns remaining duration", () => { const start = now - HOUR_MS; const durationMs = HOUR_MS * 3; expect(msUntilExpiry(start, durationMs)).toEqual(HOUR_MS * 2); }); - it('returns 0 when expiry has already passed', () => { + it("returns 0 when expiry has already passed", () => { // created 3h ago const start = now - HOUR_MS * 3; // 1h durations @@ -54,65 +50,49 @@ describe('beacon utils', () => { }); }); - describe('sortBeaconsByLatestExpiry()', () => { - const roomId = '!room:server'; - const aliceId = '@alive:server'; + describe("sortBeaconsByLatestExpiry()", () => { + const roomId = "!room:server"; + const aliceId = "@alive:server"; // 12h old, 12h left - const beacon1 = new Beacon(makeBeaconInfoEvent(aliceId, - roomId, - { timeout: HOUR_MS * 24, timestamp: now - 12 * HOUR_MS }, - '$1', - )); + const beacon1 = new Beacon( + makeBeaconInfoEvent(aliceId, roomId, { timeout: HOUR_MS * 24, timestamp: now - 12 * HOUR_MS }, "$1"), + ); // 10h left - const beacon2 = new Beacon(makeBeaconInfoEvent(aliceId, - roomId, - { timeout: HOUR_MS * 10, timestamp: now }, - '$2', - )); + const beacon2 = new Beacon( + makeBeaconInfoEvent(aliceId, roomId, { timeout: HOUR_MS * 10, timestamp: now }, "$2"), + ); // 1ms left - const beacon3 = new Beacon(makeBeaconInfoEvent(aliceId, - roomId, - { timeout: HOUR_MS + 1, timestamp: now - HOUR_MS }, - '$3', - )); + const beacon3 = new Beacon( + makeBeaconInfoEvent(aliceId, roomId, { timeout: HOUR_MS + 1, timestamp: now - HOUR_MS }, "$3"), + ); - it('sorts beacons by descending expiry time', () => { - expect([beacon2, beacon3, beacon1].sort(sortBeaconsByLatestExpiry)).toEqual([ - beacon1, beacon2, beacon3, - ]); + it("sorts beacons by descending expiry time", () => { + expect([beacon2, beacon3, beacon1].sort(sortBeaconsByLatestExpiry)).toEqual([beacon1, beacon2, beacon3]); }); }); - describe('sortBeaconsByLatestCreation()', () => { - const roomId = '!room:server'; - const aliceId = '@alive:server'; + describe("sortBeaconsByLatestCreation()", () => { + const roomId = "!room:server"; + const aliceId = "@alive:server"; // 12h old, 12h left - const beacon1 = new Beacon(makeBeaconInfoEvent(aliceId, - roomId, - { timeout: HOUR_MS * 24, timestamp: now - 12 * HOUR_MS }, - '$1', - )); + const beacon1 = new Beacon( + makeBeaconInfoEvent(aliceId, roomId, { timeout: HOUR_MS * 24, timestamp: now - 12 * HOUR_MS }, "$1"), + ); // 10h left - const beacon2 = new Beacon(makeBeaconInfoEvent(aliceId, - roomId, - { timeout: HOUR_MS * 10, timestamp: now }, - '$2', - )); + const beacon2 = new Beacon( + makeBeaconInfoEvent(aliceId, roomId, { timeout: HOUR_MS * 10, timestamp: now }, "$2"), + ); // 1ms left - const beacon3 = new Beacon(makeBeaconInfoEvent(aliceId, - roomId, - { timeout: HOUR_MS + 1, timestamp: now - HOUR_MS }, - '$3', - )); + const beacon3 = new Beacon( + makeBeaconInfoEvent(aliceId, roomId, { timeout: HOUR_MS + 1, timestamp: now - HOUR_MS }, "$3"), + ); - it('sorts beacons by descending creation time', () => { - expect([beacon1, beacon2, beacon3].sort(sortBeaconsByLatestCreation)).toEqual([ - beacon2, beacon3, beacon1, - ]); + it("sorts beacons by descending creation time", () => { + expect([beacon1, beacon2, beacon3].sort(sortBeaconsByLatestCreation)).toEqual([beacon2, beacon3, beacon1]); }); }); }); diff --git a/test/utils/beacon/geolocation-test.ts b/test/utils/beacon/geolocation-test.ts index 9b64105e62..9ee6cf6b50 100644 --- a/test/utils/beacon/geolocation-test.ts +++ b/test/utils/beacon/geolocation-test.ts @@ -24,13 +24,9 @@ import { watchPosition, } from "../../../src/utils/beacon"; import { getCurrentPosition } from "../../../src/utils/beacon/geolocation"; -import { - makeGeolocationPosition, - mockGeolocation, - getMockGeolocationPositionError, -} from "../../test-utils"; +import { makeGeolocationPosition, mockGeolocation, getMockGeolocationPositionError } from "../../test-utils"; -describe('geolocation utilities', () => { +describe("geolocation utilities", () => { let geolocation; const defaultPosition = makeGeolocationPosition({}); @@ -39,15 +35,15 @@ describe('geolocation utilities', () => { beforeEach(() => { geolocation = mockGeolocation(); - jest.spyOn(Date, 'now').mockReturnValue(now); + jest.spyOn(Date, "now").mockReturnValue(now); }); afterEach(() => { - jest.spyOn(Date, 'now').mockRestore(); - jest.spyOn(logger, 'error').mockRestore(); + jest.spyOn(Date, "now").mockRestore(); + jest.spyOn(logger, "error").mockRestore(); }); - describe('getGeoUri', () => { + describe("getGeoUri", () => { it("Renders a URI with only lat and lon", () => { const pos = { latitude: 43.2, @@ -106,50 +102,51 @@ describe('geolocation utilities', () => { }); }); - describe('mapGeolocationError', () => { + describe("mapGeolocationError", () => { beforeEach(() => { // suppress expected errors from test log - jest.spyOn(logger, 'error').mockImplementation(() => { }); + jest.spyOn(logger, "error").mockImplementation(() => {}); }); - it('returns default for other error', () => { - const error = new Error('oh no..'); + it("returns default for other error", () => { + const error = new Error("oh no.."); expect(mapGeolocationError(error)).toEqual(GeolocationError.Default); }); - it('returns unavailable for unavailable error', () => { + it("returns unavailable for unavailable error", () => { const error = new Error(GeolocationError.Unavailable); expect(mapGeolocationError(error)).toEqual(GeolocationError.Unavailable); }); - it('maps geo error permissiondenied correctly', () => { - const error = getMockGeolocationPositionError(1, 'message'); + it("maps geo error permissiondenied correctly", () => { + const error = getMockGeolocationPositionError(1, "message"); expect(mapGeolocationError(error)).toEqual(GeolocationError.PermissionDenied); }); - it('maps geo position unavailable error correctly', () => { - const error = getMockGeolocationPositionError(2, 'message'); + it("maps geo position unavailable error correctly", () => { + const error = getMockGeolocationPositionError(2, "message"); expect(mapGeolocationError(error)).toEqual(GeolocationError.PositionUnavailable); }); - it('maps geo timeout error correctly', () => { - const error = getMockGeolocationPositionError(3, 'message'); + it("maps geo timeout error correctly", () => { + const error = getMockGeolocationPositionError(3, "message"); expect(mapGeolocationError(error)).toEqual(GeolocationError.Timeout); }); }); - describe('mapGeolocationPositionToTimedGeo()', () => { - it('maps geolocation position correctly', () => { + describe("mapGeolocationPositionToTimedGeo()", () => { + it("maps geolocation position correctly", () => { expect(mapGeolocationPositionToTimedGeo(defaultPosition)).toEqual({ - timestamp: now, geoUri: 'geo:54.001927,-8.253491;u=1', + timestamp: now, + geoUri: "geo:54.001927,-8.253491;u=1", }); }); }); - describe('watchPosition()', () => { - it('throws with unavailable error when geolocation is not available', () => { + describe("watchPosition()", () => { + it("throws with unavailable error when geolocation is not available", () => { // suppress expected errors from test log - jest.spyOn(logger, 'error').mockImplementation(() => { }); + jest.spyOn(logger, "error").mockImplementation(() => {}); // remove the mock we added // @ts-ignore illegal assignment to readonly property @@ -161,7 +158,7 @@ describe('geolocation utilities', () => { expect(() => watchPosition(positionHandler, errorHandler)).toThrow(GeolocationError.Unavailable); }); - it('sets up position handler with correct options', () => { + it("sets up position handler with correct options", () => { const positionHandler = jest.fn(); const errorHandler = jest.fn(); watchPosition(positionHandler, errorHandler); @@ -173,7 +170,7 @@ describe('geolocation utilities', () => { }); }); - it('returns clearWatch function', () => { + it("returns clearWatch function", () => { const watchId = 1; geolocation.watchPosition.mockReturnValue(watchId); const positionHandler = jest.fn(); @@ -185,7 +182,7 @@ describe('geolocation utilities', () => { expect(geolocation.clearWatch).toHaveBeenCalledWith(watchId); }); - it('calls position handler with position', () => { + it("calls position handler with position", () => { const positionHandler = jest.fn(); const errorHandler = jest.fn(); watchPosition(positionHandler, errorHandler); @@ -193,11 +190,11 @@ describe('geolocation utilities', () => { expect(positionHandler).toHaveBeenCalledWith(defaultPosition); }); - it('maps geolocation position error and calls error handler', () => { + it("maps geolocation position error and calls error handler", () => { // suppress expected errors from test log - jest.spyOn(logger, 'error').mockImplementation(() => { }); - geolocation.watchPosition.mockImplementation( - (_callback, error) => error(getMockGeolocationPositionError(1, 'message')), + jest.spyOn(logger, "error").mockImplementation(() => {}); + geolocation.watchPosition.mockImplementation((_callback, error) => + error(getMockGeolocationPositionError(1, "message")), ); const positionHandler = jest.fn(); const errorHandler = jest.fn(); @@ -207,10 +204,10 @@ describe('geolocation utilities', () => { }); }); - describe('getCurrentPosition()', () => { - it('throws with unavailable error when geolocation is not available', async () => { + describe("getCurrentPosition()", () => { + it("throws with unavailable error when geolocation is not available", async () => { // suppress expected errors from test log - jest.spyOn(logger, 'error').mockImplementation(() => { }); + jest.spyOn(logger, "error").mockImplementation(() => {}); // remove the mock we added // @ts-ignore illegal assignment to readonly property @@ -219,17 +216,17 @@ describe('geolocation utilities', () => { await expect(() => getCurrentPosition()).rejects.toThrow(GeolocationError.Unavailable); }); - it('throws with geolocation error when geolocation.getCurrentPosition fails', async () => { + it("throws with geolocation error when geolocation.getCurrentPosition fails", async () => { // suppress expected errors from test log - jest.spyOn(logger, 'error').mockImplementation(() => { }); + jest.spyOn(logger, "error").mockImplementation(() => {}); - const timeoutError = getMockGeolocationPositionError(3, 'message'); + const timeoutError = getMockGeolocationPositionError(3, "message"); geolocation.getCurrentPosition.mockImplementation((callback, error) => error(timeoutError)); await expect(() => getCurrentPosition()).rejects.toThrow(GeolocationError.Timeout); }); - it('resolves with current location', async () => { + it("resolves with current location", async () => { geolocation.getCurrentPosition.mockImplementation((callback, error) => callback(defaultPosition)); const result = await getCurrentPosition(); diff --git a/test/utils/beacon/timeline-test.ts b/test/utils/beacon/timeline-test.ts index 610530c57a..716c5bf6b7 100644 --- a/test/utils/beacon/timeline-test.ts +++ b/test/utils/beacon/timeline-test.ts @@ -19,28 +19,28 @@ import { EventType, MatrixEvent } from "matrix-js-sdk/src/matrix"; import { shouldDisplayAsBeaconTile } from "../../../src/utils/beacon/timeline"; import { makeBeaconInfoEvent } from "../../test-utils"; -describe('shouldDisplayAsBeaconTile', () => { - const userId = '@user:server'; - const roomId = '!room:server'; +describe("shouldDisplayAsBeaconTile", () => { + const userId = "@user:server"; + const roomId = "!room:server"; const liveBeacon = makeBeaconInfoEvent(userId, roomId, { isLive: true }); const notLiveBeacon = makeBeaconInfoEvent(userId, roomId, { isLive: false }); const memberEvent = new MatrixEvent({ type: EventType.RoomMember }); const redactedBeacon = makeBeaconInfoEvent(userId, roomId, { isLive: false }); redactedBeacon.makeRedacted(redactedBeacon); - it('returns true for a beacon with live property set to true', () => { + it("returns true for a beacon with live property set to true", () => { expect(shouldDisplayAsBeaconTile(liveBeacon)).toBe(true); }); - it('returns true for a redacted beacon', () => { + it("returns true for a redacted beacon", () => { expect(shouldDisplayAsBeaconTile(redactedBeacon)).toBe(true); }); - it('returns false for a beacon with live property set to false', () => { + it("returns false for a beacon with live property set to false", () => { expect(shouldDisplayAsBeaconTile(notLiveBeacon)).toBe(false); }); - it('returns false for a non beacon event', () => { + it("returns false for a non beacon event", () => { expect(shouldDisplayAsBeaconTile(memberEvent)).toBe(false); }); }); diff --git a/test/utils/colour-test.ts b/test/utils/colour-test.ts index 720c34e07b..02a559447a 100644 --- a/test/utils/colour-test.ts +++ b/test/utils/colour-test.ts @@ -17,8 +17,8 @@ limitations under the License. import { textToHtmlRainbow } from "../../src/utils/colour"; describe("textToHtmlRainbow", () => { - it('correctly transform text to html without splitting the emoji in two', () => { - expect(textToHtmlRainbow('🐻')).toBe('🐻'); - expect(textToHtmlRainbow('🐕‍🦺')).toBe('🐕‍🦺'); + it("correctly transform text to html without splitting the emoji in two", () => { + expect(textToHtmlRainbow("🐻")).toBe('🐻'); + expect(textToHtmlRainbow("🐕‍🦺")).toBe('🐕‍🦺'); }); }); diff --git a/test/utils/createVoiceMessageContent-test.ts b/test/utils/createVoiceMessageContent-test.ts index 0043cf292a..876722baf7 100644 --- a/test/utils/createVoiceMessageContent-test.ts +++ b/test/utils/createVoiceMessageContent-test.ts @@ -20,13 +20,15 @@ import { createVoiceMessageContent } from "../../src/utils/createVoiceMessageCon describe("createVoiceMessageContent", () => { it("should create a voice message content", () => { - expect(createVoiceMessageContent( - "mxc://example.com/file", - "ogg/opus", - 23000, - 42000, - {} as unknown as IEncryptedFile, - [1, 2, 3], - )).toMatchSnapshot(); + expect( + createVoiceMessageContent( + "mxc://example.com/file", + "ogg/opus", + 23000, + 42000, + {} as unknown as IEncryptedFile, + [1, 2, 3], + ), + ).toMatchSnapshot(); }); }); diff --git a/test/utils/device/clientInformation-test.ts b/test/utils/device/clientInformation-test.ts index 24355d49c8..4133619f91 100644 --- a/test/utils/device/clientInformation-test.ts +++ b/test/utils/device/clientInformation-test.ts @@ -18,15 +18,12 @@ import { MatrixEvent } from "matrix-js-sdk/src/matrix"; import BasePlatform from "../../../src/BasePlatform"; import { IConfigOptions } from "../../../src/IConfigOptions"; -import { - getDeviceClientInformation, - recordClientInformation, -} from "../../../src/utils/device/clientInformation"; +import { getDeviceClientInformation, recordClientInformation } from "../../../src/utils/device/clientInformation"; import { getMockClientWithEventEmitter } from "../../test-utils"; -describe('recordClientInformation()', () => { - const deviceId = 'my-device-id'; - const version = '1.2.3'; +describe("recordClientInformation()", () => { + const deviceId = "my-device-id"; + const version = "1.2.3"; const isElectron = window.electron; const mockClient = getMockClientWithEventEmitter({ @@ -35,8 +32,8 @@ describe('recordClientInformation()', () => { }); const sdkConfig: IConfigOptions = { - brand: 'Test Brand', - element_call: { url: '', use_exclusively: false, brand: "Element Call" }, + brand: "Test Brand", + element_call: { url: "", use_exclusively: false, brand: "Element Call" }, }; const platform = { @@ -53,45 +50,31 @@ describe('recordClientInformation()', () => { window.electron = isElectron; }); - it('saves client information without url for electron clients', async () => { + it("saves client information without url for electron clients", async () => { window.electron = true; - await recordClientInformation( - mockClient, - sdkConfig, - platform, - ); + await recordClientInformation(mockClient, sdkConfig, platform); - expect(mockClient.setAccountData).toHaveBeenCalledWith( - `io.element.matrix_client_information.${deviceId}`, - { - name: sdkConfig.brand, - version, - url: undefined, - }, - ); + expect(mockClient.setAccountData).toHaveBeenCalledWith(`io.element.matrix_client_information.${deviceId}`, { + name: sdkConfig.brand, + version, + url: undefined, + }); }); - it('saves client information with url for non-electron clients', async () => { - await recordClientInformation( - mockClient, - sdkConfig, - platform, - ); + it("saves client information with url for non-electron clients", async () => { + await recordClientInformation(mockClient, sdkConfig, platform); - expect(mockClient.setAccountData).toHaveBeenCalledWith( - `io.element.matrix_client_information.${deviceId}`, - { - name: sdkConfig.brand, - version, - url: 'localhost', - }, - ); + expect(mockClient.setAccountData).toHaveBeenCalledWith(`io.element.matrix_client_information.${deviceId}`, { + name: sdkConfig.brand, + version, + url: "localhost", + }); }); }); -describe('getDeviceClientInformation()', () => { - const deviceId = 'my-device-id'; +describe("getDeviceClientInformation()", () => { + const deviceId = "my-device-id"; const mockClient = getMockClientWithEventEmitter({ getAccountData: jest.fn(), @@ -101,19 +84,17 @@ describe('getDeviceClientInformation()', () => { jest.resetAllMocks(); }); - it('returns an empty object when no event exists for the device', () => { + it("returns an empty object when no event exists for the device", () => { expect(getDeviceClientInformation(mockClient, deviceId)).toEqual({}); - expect(mockClient.getAccountData).toHaveBeenCalledWith( - `io.element.matrix_client_information.${deviceId}`, - ); + expect(mockClient.getAccountData).toHaveBeenCalledWith(`io.element.matrix_client_information.${deviceId}`); }); - it('returns client information for the device', () => { + it("returns client information for the device", () => { const eventContent = { - name: 'Element Web', - version: '1.2.3', - url: 'test.com', + name: "Element Web", + version: "1.2.3", + url: "test.com", }; const event = new MatrixEvent({ type: `io.element.matrix_client_information.${deviceId}`, @@ -123,13 +104,13 @@ describe('getDeviceClientInformation()', () => { expect(getDeviceClientInformation(mockClient, deviceId)).toEqual(eventContent); }); - it('excludes values with incorrect types', () => { + it("excludes values with incorrect types", () => { const eventContent = { - extraField: 'hello', - name: 'Element Web', + extraField: "hello", + name: "Element Web", // wrong format - version: { value: '1.2.3' }, - url: 'test.com', + version: { value: "1.2.3" }, + url: "test.com", }; const event = new MatrixEvent({ type: `io.element.matrix_client_information.${deviceId}`, @@ -143,4 +124,3 @@ describe('getDeviceClientInformation()', () => { }); }); }); - diff --git a/test/utils/device/parseUserAgent-test.ts b/test/utils/device/parseUserAgent-test.ts index e4ae89d2e0..e6a1a228fa 100644 --- a/test/utils/device/parseUserAgent-test.ts +++ b/test/utils/device/parseUserAgent-test.ts @@ -26,7 +26,7 @@ const makeDeviceExtendedInfo = ( deviceType, deviceModel, deviceOperatingSystem, - client: clientName && [clientName, clientVersion].filter(Boolean).join(' '), + client: clientName && [clientName, clientVersion].filter(Boolean).join(" "), }); /* eslint-disable max-len */ @@ -66,7 +66,7 @@ const IOS_EXPECTED_RESULT = [ ]; const DESKTOP_UA = [ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) ElementNightly/2022091301 Chrome/104.0.5112.102" + - " Electron/20.1.1 Safari/537.36", + " Electron/20.1.1 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) ElementNightly/2022091301 Chrome/104.0.5112.102 Electron/20.1.1 Safari/537.36", ]; const DESKTOP_EXPECTED_RESULT = [ @@ -98,7 +98,6 @@ const WEB_EXPECTED_RESULT = [ makeDeviceExtendedInfo(DeviceType.Web, "Apple iPad", "iOS", "Mobile Safari", "8.0"), makeDeviceExtendedInfo(DeviceType.Web, "Apple iPhone", "iOS", "Mobile Safari", "8.0"), makeDeviceExtendedInfo(DeviceType.Web, "Samsung SM-G973U", "Android", "Chrome", "69.0.3497.100"), - ]; const MISC_UA = [ @@ -119,8 +118,8 @@ const MISC_EXPECTED_RESULT = [ ]; /* eslint-disable max-len */ -describe('parseUserAgent()', () => { - it('returns deviceType unknown when user agent is falsy', () => { +describe("parseUserAgent()", () => { + it("returns deviceType unknown when user agent is falsy", () => { expect(parseUserAgent(undefined)).toEqual({ deviceType: DeviceType.Unknown, }); @@ -132,17 +131,15 @@ describe('parseUserAgent()', () => { const testCases: TestCase[] = userAgents.map((userAgent, index) => [userAgent, results[index]]); describe(platform, () => { - it.each( - testCases, - )('Parses user agent correctly - %s', (userAgent, expectedResult) => { + it.each(testCases)("Parses user agent correctly - %s", (userAgent, expectedResult) => { expect(parseUserAgent(userAgent)).toEqual(expectedResult); }); }); }; - testPlatform('Android', ANDROID_UA, ANDROID_EXPECTED_RESULT); - testPlatform('iOS', IOS_UA, IOS_EXPECTED_RESULT); - testPlatform('Desktop', DESKTOP_UA, DESKTOP_EXPECTED_RESULT); - testPlatform('Web', WEB_UA, WEB_EXPECTED_RESULT); - testPlatform('Misc', MISC_UA, MISC_EXPECTED_RESULT); + testPlatform("Android", ANDROID_UA, ANDROID_EXPECTED_RESULT); + testPlatform("iOS", IOS_UA, IOS_EXPECTED_RESULT); + testPlatform("Desktop", DESKTOP_UA, DESKTOP_EXPECTED_RESULT); + testPlatform("Web", WEB_UA, WEB_EXPECTED_RESULT); + testPlatform("Misc", MISC_UA, MISC_EXPECTED_RESULT); }); diff --git a/test/utils/device/snoozeBulkUnverifiedDeviceReminder-test.ts b/test/utils/device/snoozeBulkUnverifiedDeviceReminder-test.ts index e7abf4b56a..7b8e07d152 100644 --- a/test/utils/device/snoozeBulkUnverifiedDeviceReminder-test.ts +++ b/test/utils/device/snoozeBulkUnverifiedDeviceReminder-test.ts @@ -21,12 +21,12 @@ import { snoozeBulkUnverifiedDeviceReminder, } from "../../../src/utils/device/snoozeBulkUnverifiedDeviceReminder"; -const SNOOZE_KEY = 'mx_snooze_bulk_unverified_device_nag'; +const SNOOZE_KEY = "mx_snooze_bulk_unverified_device_nag"; -describe('snooze bulk unverified device nag', () => { - const localStorageSetSpy = jest.spyOn(localStorage.__proto__, 'setItem'); - const localStorageGetSpy = jest.spyOn(localStorage.__proto__, 'getItem'); - const localStorageRemoveSpy = jest.spyOn(localStorage.__proto__, 'removeItem'); +describe("snooze bulk unverified device nag", () => { + const localStorageSetSpy = jest.spyOn(localStorage.__proto__, "setItem"); + const localStorageGetSpy = jest.spyOn(localStorage.__proto__, "getItem"); + const localStorageRemoveSpy = jest.spyOn(localStorage.__proto__, "removeItem"); // 14.03.2022 16:15 const now = 1647270879403; @@ -36,61 +36,65 @@ describe('snooze bulk unverified device nag', () => { localStorageGetSpy.mockClear().mockReturnValue(null); localStorageRemoveSpy.mockClear().mockImplementation(() => {}); - jest.spyOn(Date, 'now').mockReturnValue(now); + jest.spyOn(Date, "now").mockReturnValue(now); }); afterAll(() => { jest.restoreAllMocks(); }); - describe('snoozeBulkUnverifiedDeviceReminder()', () => { - it('sets the current time in local storage', () => { + describe("snoozeBulkUnverifiedDeviceReminder()", () => { + it("sets the current time in local storage", () => { snoozeBulkUnverifiedDeviceReminder(); expect(localStorageSetSpy).toHaveBeenCalledWith(SNOOZE_KEY, now.toString()); }); - it('catches an error from localstorage', () => { - const loggerErrorSpy = jest.spyOn(logger, 'error'); - localStorageSetSpy.mockImplementation(() => { throw new Error('oups'); }); + it("catches an error from localstorage", () => { + const loggerErrorSpy = jest.spyOn(logger, "error"); + localStorageSetSpy.mockImplementation(() => { + throw new Error("oups"); + }); snoozeBulkUnverifiedDeviceReminder(); expect(loggerErrorSpy).toHaveBeenCalled(); }); }); - describe('isBulkUnverifiedDeviceReminderSnoozed()', () => { - it('returns false when there is no snooze in storage', () => { + describe("isBulkUnverifiedDeviceReminderSnoozed()", () => { + it("returns false when there is no snooze in storage", () => { const result = isBulkUnverifiedDeviceReminderSnoozed(); expect(localStorageGetSpy).toHaveBeenCalledWith(SNOOZE_KEY); expect(result).toBe(false); }); - it('catches an error from localstorage and returns false', () => { - const loggerErrorSpy = jest.spyOn(logger, 'error'); - localStorageGetSpy.mockImplementation(() => { throw new Error('oups'); }); + it("catches an error from localstorage and returns false", () => { + const loggerErrorSpy = jest.spyOn(logger, "error"); + localStorageGetSpy.mockImplementation(() => { + throw new Error("oups"); + }); const result = isBulkUnverifiedDeviceReminderSnoozed(); expect(result).toBe(false); expect(loggerErrorSpy).toHaveBeenCalled(); }); - it('returns false when snooze timestamp in storage is not a number', () => { - localStorageGetSpy.mockReturnValue('test'); + it("returns false when snooze timestamp in storage is not a number", () => { + localStorageGetSpy.mockReturnValue("test"); const result = isBulkUnverifiedDeviceReminderSnoozed(); expect(result).toBe(false); }); - it('returns false when snooze timestamp in storage is over a week ago', () => { + it("returns false when snooze timestamp in storage is over a week ago", () => { const msDay = 1000 * 60 * 60 * 24; // snoozed 8 days ago - localStorageGetSpy.mockReturnValue(now - (msDay * 8)); + localStorageGetSpy.mockReturnValue(now - msDay * 8); const result = isBulkUnverifiedDeviceReminderSnoozed(); expect(result).toBe(false); }); - it('returns true when snooze timestamp in storage is less than a week ago', () => { + it("returns true when snooze timestamp in storage is less than a week ago", () => { const msDay = 1000 * 60 * 60 * 24; // snoozed 8 days ago - localStorageGetSpy.mockReturnValue(now - (msDay * 6)); + localStorageGetSpy.mockReturnValue(now - msDay * 6); const result = isBulkUnverifiedDeviceReminderSnoozed(); expect(result).toBe(true); }); diff --git a/test/utils/direct-messages-test.ts b/test/utils/direct-messages-test.ts index 13542440ce..b803b9b23d 100644 --- a/test/utils/direct-messages-test.ts +++ b/test/utils/direct-messages-test.ts @@ -158,18 +158,12 @@ describe("direct-messages", () => { mocked(startDm).mockResolvedValue(room1.roomId); }); - it( - "should set the room into creating state and call waitForRoomReadyAndApplyAfterCreateCallbacks", - async () => { - const result = await dmModule.createRoomFromLocalRoom(mockClient, localRoom); - expect(result).toBe(room1.roomId); - expect(localRoom.state).toBe(LocalRoomState.CREATING); - expect(waitForRoomReadyAndApplyAfterCreateCallbacks).toHaveBeenCalledWith( - mockClient, - localRoom, - ); - }, - ); + it("should set the room into creating state and call waitForRoomReadyAndApplyAfterCreateCallbacks", async () => { + const result = await dmModule.createRoomFromLocalRoom(mockClient, localRoom); + expect(result).toBe(room1.roomId); + expect(localRoom.state).toBe(LocalRoomState.CREATING); + expect(waitForRoomReadyAndApplyAfterCreateCallbacks).toHaveBeenCalledWith(mockClient, localRoom); + }); }); }); }); diff --git a/test/utils/enums-test.ts b/test/utils/enums-test.ts index e1d34ee688..3391f9ae7a 100644 --- a/test/utils/enums-test.ts +++ b/test/utils/enums-test.ts @@ -26,16 +26,16 @@ enum TestNumberEnum { SecondKey = 20, } -describe('enums', () => { - describe('getEnumValues', () => { - it('should work on string enums', () => { +describe("enums", () => { + describe("getEnumValues", () => { + it("should work on string enums", () => { const result = getEnumValues(TestStringEnum); expect(result).toBeDefined(); expect(result).toHaveLength(2); - expect(result).toEqual(['__first__', '__second__']); + expect(result).toEqual(["__first__", "__second__"]); }); - it('should work on number enums', () => { + it("should work on number enums", () => { const result = getEnumValues(TestNumberEnum); expect(result).toBeDefined(); expect(result).toHaveLength(2); @@ -43,23 +43,23 @@ describe('enums', () => { }); }); - describe('isEnumValue', () => { - it('should return true on values in a string enum', () => { - const result = isEnumValue(TestStringEnum, '__first__'); + describe("isEnumValue", () => { + it("should return true on values in a string enum", () => { + const result = isEnumValue(TestStringEnum, "__first__"); expect(result).toBe(true); }); - it('should return false on values not in a string enum', () => { - const result = isEnumValue(TestStringEnum, 'not a value'); + it("should return false on values not in a string enum", () => { + const result = isEnumValue(TestStringEnum, "not a value"); expect(result).toBe(false); }); - it('should return true on values in a number enum', () => { + it("should return true on values in a number enum", () => { const result = isEnumValue(TestNumberEnum, 10); expect(result).toBe(true); }); - it('should return false on values not in a number enum', () => { + it("should return false on values not in a number enum", () => { const result = isEnumValue(TestStringEnum, 99); expect(result).toBe(false); }); diff --git a/test/utils/export-test.tsx b/test/utils/export-test.tsx index 00a614b855..f01416a181 100644 --- a/test/utils/export-test.tsx +++ b/test/utils/export-test.tsx @@ -29,22 +29,22 @@ import { MatrixClientPeg } from "../../src/MatrixClientPeg"; import { IExportOptions, ExportType, ExportFormat } from "../../src/utils/exportUtils/exportUtils"; import PlainTextExporter from "../../src/utils/exportUtils/PlainTextExport"; import HTMLExporter from "../../src/utils/exportUtils/HtmlExport"; -import * as TestUtilsMatrix from '../test-utils'; -import { stubClient } from '../test-utils'; +import * as TestUtilsMatrix from "../test-utils"; +import { stubClient } from "../test-utils"; let client: MatrixClient; const MY_USER_ID = "@me:here"; function generateRoomId() { - return '!' + Math.random().toString().slice(2, 10) + ':domain'; + return "!" + Math.random().toString().slice(2, 10) + ":domain"; } interface ITestContent extends IContent { expectedText: string; } -describe('export', function() { +describe("export", function () { stubClient(); client = MatrixClientPeg.get(); client.getUserId = () => { @@ -71,20 +71,20 @@ describe('export', function() { sender: MY_USER_ID, content: {}, unsigned: { - "age": 72, - "transaction_id": "m1212121212.23", - "redacted_because": { - "content": {}, - "origin_server_ts": ts0 + i*1000, - "redacts": "$9999999999999999999999999999999999999999998", - "sender": "@me:here", - "type": EventType.RoomRedaction, - "unsigned": { - "age": 94, - "transaction_id": "m1111111111.1", + age: 72, + transaction_id: "m1212121212.23", + redacted_because: { + content: {}, + origin_server_ts: ts0 + i * 1000, + redacts: "$9999999999999999999999999999999999999999998", + sender: "@me:here", + type: EventType.RoomRedaction, + unsigned: { + age: 94, + transaction_id: "m1111111111.1", }, - "event_id": "$9999999999999999999999999999999999999999998", - "room_id": mockRoom.roomId, + event_id: "$9999999999999999999999999999999999999999998", + room_id: mockRoom.roomId, }, }, event_id: "$9999999999999999999999999999999999999999999", @@ -94,47 +94,47 @@ describe('export', function() { function mkFileEvent() { return new MatrixEvent({ - "content": { - "body": "index.html", - "info": { - "mimetype": "text/html", - "size": 31613, + content: { + body: "index.html", + info: { + mimetype: "text/html", + size: 31613, }, - "msgtype": "m.file", - "url": "mxc://test.org", + msgtype: "m.file", + url: "mxc://test.org", }, - "origin_server_ts": 1628872988364, - "sender": MY_USER_ID, - "type": "m.room.message", - "unsigned": { - "age": 266, - "transaction_id": "m99999999.2", + origin_server_ts: 1628872988364, + sender: MY_USER_ID, + type: "m.room.message", + unsigned: { + age: 266, + transaction_id: "m99999999.2", }, - "event_id": "$99999999999999999999", - "room_id": mockRoom.roomId, + event_id: "$99999999999999999999", + room_id: mockRoom.roomId, }); } function mkImageEvent() { return new MatrixEvent({ - "content": { - "body": "image.png", - "info": { - "mimetype": "image/png", - "size": 31613, + content: { + body: "image.png", + info: { + mimetype: "image/png", + size: 31613, }, - "msgtype": "m.image", - "url": "mxc://test.org", + msgtype: "m.image", + url: "mxc://test.org", }, - "origin_server_ts": 1628872988364, - "sender": MY_USER_ID, - "type": "m.room.message", - "unsigned": { - "age": 266, - "transaction_id": "m99999999.2", + origin_server_ts: 1628872988364, + sender: MY_USER_ID, + type: "m.room.message", + unsigned: { + age: 266, + transaction_id: "m99999999.2", }, - "event_id": "$99999999999999999999", - "room_id": mockRoom.roomId, + event_id: "$99999999999999999999", + room_id: mockRoom.roomId, }); } @@ -143,62 +143,74 @@ describe('export', function() { let i: number; // plain text for (i = 0; i < 10; i++) { - matrixEvents.push(TestUtilsMatrix.mkMessage({ - event: true, room: "!room:id", user: "@user:id", - ts: ts0 + i * 1000, - })); + matrixEvents.push( + TestUtilsMatrix.mkMessage({ + event: true, + room: "!room:id", + user: "@user:id", + ts: ts0 + i * 1000, + }), + ); } // reply events for (i = 0; i < 10; i++) { const eventId = "$" + Math.random() + "-" + Math.random(); - matrixEvents.push(TestUtilsMatrix.mkEvent({ - "content": { - "body": "> <@me:here> Hi\n\nTest", - "format": "org.matrix.custom.html", - "m.relates_to": { - "rel_type": RelationType.Reference, - "event_id": eventId, - "m.in_reply_to": { + matrixEvents.push( + TestUtilsMatrix.mkEvent({ + content: { + "body": "> <@me:here> Hi\n\nTest", + "format": "org.matrix.custom.html", + "m.relates_to": { + "rel_type": RelationType.Reference, "event_id": eventId, + "m.in_reply_to": { + event_id: eventId, + }, }, + "msgtype": "m.text", }, - "msgtype": "m.text", - }, - "user": "@me:here", - "type": "m.room.message", - "room": mockRoom.roomId, - "event": true, - })); + user: "@me:here", + type: "m.room.message", + room: mockRoom.roomId, + event: true, + }), + ); } // membership events for (i = 0; i < 10; i++) { - matrixEvents.push(TestUtilsMatrix.mkMembership({ - event: true, room: "!room:id", user: "@user:id", - target: { - userId: "@user:id", - name: "Bob", - getAvatarUrl: () => { - return "avatar.jpeg"; - }, - getMxcAvatarUrl: () => 'mxc://avatar.url/image.png', - } as unknown as RoomMember, - ts: ts0 + i*1000, - mship: 'join', - prevMship: 'join', - name: 'A user', - })); + matrixEvents.push( + TestUtilsMatrix.mkMembership({ + event: true, + room: "!room:id", + user: "@user:id", + target: { + userId: "@user:id", + name: "Bob", + getAvatarUrl: () => { + return "avatar.jpeg"; + }, + getMxcAvatarUrl: () => "mxc://avatar.url/image.png", + } as unknown as RoomMember, + ts: ts0 + i * 1000, + mship: "join", + prevMship: "join", + name: "A user", + }), + ); } // emote - matrixEvents.push(TestUtilsMatrix.mkEvent({ - "content": { - "body": "waves", - "msgtype": "m.emote", - }, - "user": "@me:here", - "type": "m.room.message", - "room": mockRoom.roomId, - "event": true, - })); + matrixEvents.push( + TestUtilsMatrix.mkEvent({ + content: { + body: "waves", + msgtype: "m.emote", + }, + user: "@me:here", + type: "m.room.message", + room: mockRoom.roomId, + event: true, + }), + ); // redacted events for (i = 0; i < 10; i++) { matrixEvents.push(mkRedactedEvent(i)); @@ -208,7 +220,7 @@ describe('export', function() { const events: MatrixEvent[] = mkEvents(); - it('checks if the export format is valid', function() { + it("checks if the export format is valid", function () { function isValidFormat(format: string): boolean { const options: string[] = Object.values(ExportFormat); return options.includes(format); @@ -219,51 +231,60 @@ describe('export', function() { expect(isValidFormat("Pdf")).toBeFalsy(); }); - it("checks if the icons' html corresponds to export regex", function() { + it("checks if the icons' html corresponds to export regex", function () { const exporter = new HTMLExporter(mockRoom, ExportType.Beginning, mockExportOptions, null); const fileRegex = /.*?<\/span>/; - expect(fileRegex.test( - renderToString(exporter.getEventTile(mkFileEvent(), true))), - ).toBeTruthy(); + expect(fileRegex.test(renderToString(exporter.getEventTile(mkFileEvent(), true)))).toBeTruthy(); }); it("should export images if attachments are enabled", () => { - const exporter = new HTMLExporter(mockRoom, ExportType.Beginning, { - numberOfMessages: 5, - maxSize: 100 * 1024 * 1024, - attachmentsIncluded: true, - }, null); + const exporter = new HTMLExporter( + mockRoom, + ExportType.Beginning, + { + numberOfMessages: 5, + maxSize: 100 * 1024 * 1024, + attachmentsIncluded: true, + }, + null, + ); const imageRegex = //; - expect(imageRegex.test( - renderToString(exporter.getEventTile(mkImageEvent(), true))), - ).toBeTruthy(); + expect(imageRegex.test(renderToString(exporter.getEventTile(mkImageEvent(), true)))).toBeTruthy(); }); const invalidExportOptions: [string, IExportOptions][] = [ - ['numberOfMessages exceeds max', { - numberOfMessages: 10 ** 9, - maxSize: 1024 * 1024 * 1024, - attachmentsIncluded: false, - }], - ['maxSize exceeds 8GB', { - numberOfMessages: -1, - maxSize: 8001 * 1024 * 1024, - attachmentsIncluded: false, - }], - ['maxSize is less than 1mb', { - numberOfMessages: 0, - maxSize: 0, - attachmentsIncluded: false, - }], + [ + "numberOfMessages exceeds max", + { + numberOfMessages: 10 ** 9, + maxSize: 1024 * 1024 * 1024, + attachmentsIncluded: false, + }, + ], + [ + "maxSize exceeds 8GB", + { + numberOfMessages: -1, + maxSize: 8001 * 1024 * 1024, + attachmentsIncluded: false, + }, + ], + [ + "maxSize is less than 1mb", + { + numberOfMessages: 0, + maxSize: 0, + attachmentsIncluded: false, + }, + ], ]; - it.each(invalidExportOptions)('%s', (_d, options) => { - expect( - () => - new PlainTextExporter(mockRoom, ExportType.Beginning, options, null), - ).toThrowError("Invalid export options"); + it.each(invalidExportOptions)("%s", (_d, options) => { + expect(() => new PlainTextExporter(mockRoom, ExportType.Beginning, options, null)).toThrowError( + "Invalid export options", + ); }); - it('tests the file extension splitter', function() { + it("tests the file extension splitter", function () { const exporter = new PlainTextExporter(mockRoom, ExportType.Beginning, mockExportOptions, null); const fileNameWithExtensions = { "": ["", ""], @@ -277,28 +298,28 @@ describe('export', function() { } }); - it('checks if the reply regex executes correctly', function() { + it("checks if the reply regex executes correctly", function () { const eventContents: ITestContent[] = [ { - "msgtype": "m.text", - "body": "> <@me:here> Source\n\nReply", - "expectedText": "<@me:here \"Source\"> Reply", + msgtype: "m.text", + body: "> <@me:here> Source\n\nReply", + expectedText: '<@me:here "Source"> Reply', }, { - "msgtype": "m.text", + msgtype: "m.text", // if the reply format is invalid, then return the body - "body": "Invalid reply format", - "expectedText": "Invalid reply format", + body: "Invalid reply format", + expectedText: "Invalid reply format", }, { - "msgtype": "m.text", - "body": "> <@me:here> The source is more than 32 characters\n\nReply", - "expectedText": "<@me:here \"The source is more than 32 chara...\"> Reply", + msgtype: "m.text", + body: "> <@me:here> The source is more than 32 characters\n\nReply", + expectedText: '<@me:here "The source is more than 32 chara..."> Reply', }, { - "msgtype": "m.text", - "body": "> <@me:here> This\nsource\nhas\nnew\nlines\n\nReply", - "expectedText": "<@me:here \"This\"> Reply", + msgtype: "m.text", + body: "> <@me:here> This\nsource\nhas\nnew\nlines\n\nReply", + expectedText: '<@me:here "This"> Reply', }, ]; const exporter = new PlainTextExporter(mockRoom, ExportType.Beginning, mockExportOptions, null); @@ -307,11 +328,10 @@ describe('export', function() { } }); - it("checks if the render to string doesn't throw any error for different types of events", function() { + it("checks if the render to string doesn't throw any error for different types of events", function () { const exporter = new HTMLExporter(mockRoom, ExportType.Beginning, mockExportOptions, null); for (const event of events) { expect(renderToString(exporter.getEventTile(event, false))).toBeTruthy(); } }); }); - diff --git a/test/utils/iterables-test.ts b/test/utils/iterables-test.ts index fe0bd61149..c365dd0fe2 100644 --- a/test/utils/iterables-test.ts +++ b/test/utils/iterables-test.ts @@ -16,9 +16,9 @@ limitations under the License. import { iterableDiff, iterableIntersection } from "../../src/utils/iterables"; -describe('iterables', () => { - describe('iterableIntersection', () => { - it('should return the intersection', () => { +describe("iterables", () => { + describe("iterableIntersection", () => { + it("should return the intersection", () => { const a = [1, 2, 3]; const b = [1, 2, 4]; // note diff const result = iterableIntersection(a, b); @@ -27,7 +27,7 @@ describe('iterables', () => { expect(result).toEqual([1, 2]); }); - it('should return an empty array on no matches', () => { + it("should return an empty array on no matches", () => { const a = [1, 2, 3]; const b = [4, 5, 6]; const result = iterableIntersection(a, b); @@ -36,8 +36,8 @@ describe('iterables', () => { }); }); - describe('iterableDiff', () => { - it('should see added from A->B', () => { + describe("iterableDiff", () => { + it("should see added from A->B", () => { const a = [1, 2, 3]; const b = [1, 2, 3, 4]; const result = iterableDiff(a, b); @@ -49,7 +49,7 @@ describe('iterables', () => { expect(result.added).toEqual([4]); }); - it('should see removed from A->B', () => { + it("should see removed from A->B", () => { const a = [1, 2, 3]; const b = [1, 2]; const result = iterableDiff(a, b); @@ -61,7 +61,7 @@ describe('iterables', () => { expect(result.removed).toEqual([3]); }); - it('should see added and removed in the same set', () => { + it("should see added and removed in the same set", () => { const a = [1, 2, 3]; const b = [1, 2, 4]; // note diff const result = iterableDiff(a, b); diff --git a/test/utils/leave-behaviour-test.ts b/test/utils/leave-behaviour-test.ts index 48618bd1f6..330a812205 100644 --- a/test/utils/leave-behaviour-test.ts +++ b/test/utils/leave-behaviour-test.ts @@ -45,11 +45,14 @@ describe("leaveRoomBehaviour", () => { room = mkRoom(client, "!1:example.org"); space = mkRoom(client, "!2:example.org"); space.isSpaceRoom.mockReturnValue(true); - client.getRoom.mockImplementation(roomId => { + client.getRoom.mockImplementation((roomId) => { switch (roomId) { - case room.roomId: return room; - case space.roomId: return space; - default: return null; + case room.roomId: + return room; + case space.roomId: + return space; + default: + return null; } }); @@ -62,16 +65,20 @@ describe("leaveRoomBehaviour", () => { jest.restoreAllMocks(); }); - const viewRoom = (room: Room) => defaultDispatcher.dispatch({ - action: Action.ViewRoom, - room_id: room.roomId, - metricsTrigger: undefined, - }, true); + const viewRoom = (room: Room) => + defaultDispatcher.dispatch( + { + action: Action.ViewRoom, + room_id: room.roomId, + metricsTrigger: undefined, + }, + true, + ); const expectDispatch = async (payload: T) => { const dispatcherSpy = jest.fn(); const dispatcherRef = defaultDispatcher.register(dispatcherSpy); - await new Promise(resolve => setImmediate(resolve)); // Flush the dispatcher + await new Promise((resolve) => setImmediate(resolve)); // Flush the dispatcher expect(dispatcherSpy).toHaveBeenCalledWith(payload); defaultDispatcher.unregister(dispatcherRef); }; @@ -84,8 +91,8 @@ describe("leaveRoomBehaviour", () => { }); it("returns to the parent space after leaving a room inside of a space that was being viewed", async () => { - jest.spyOn(SpaceStore.instance, "getCanonicalParent").mockImplementation( - roomId => roomId === room.roomId ? space : null, + jest.spyOn(SpaceStore.instance, "getCanonicalParent").mockImplementation((roomId) => + roomId === room.roomId ? space : null, ); viewRoom(room); SpaceStore.instance.setActiveSpace(space.roomId, false); @@ -108,8 +115,8 @@ describe("leaveRoomBehaviour", () => { it("returns to the parent space after leaving a subspace that was being viewed", async () => { room.isSpaceRoom.mockReturnValue(true); - jest.spyOn(SpaceStore.instance, "getCanonicalParent").mockImplementation( - roomId => roomId === room.roomId ? space : null, + jest.spyOn(SpaceStore.instance, "getCanonicalParent").mockImplementation((roomId) => + roomId === room.roomId ? space : null, ); viewRoom(room); SpaceStore.instance.setActiveSpace(room.roomId, false); diff --git a/test/utils/localRoom/isRoomReady-test.ts b/test/utils/localRoom/isRoomReady-test.ts index 962db3896c..f7babe8065 100644 --- a/test/utils/localRoom/isRoomReady-test.ts +++ b/test/utils/localRoom/isRoomReady-test.ts @@ -80,13 +80,15 @@ describe("isRoomReady", () => { describe("and a RoomHistoryVisibility event", () => { beforeEach(() => { - room1.currentState.setStateEvents([mkEvent({ - user: userId1, - event: true, - type: EventType.RoomHistoryVisibility, - room: room1.roomId, - content: {}, - })]); + room1.currentState.setStateEvents([ + mkEvent({ + user: userId1, + event: true, + type: EventType.RoomHistoryVisibility, + room: room1.roomId, + content: {}, + }), + ]); }); it("it should return true", () => { @@ -104,13 +106,15 @@ describe("isRoomReady", () => { describe("and a room encryption state event", () => { beforeEach(() => { - room1.currentState.setStateEvents([mkEvent({ - user: userId1, - event: true, - type: EventType.RoomEncryption, - room: room1.roomId, - content: {}, - })]); + room1.currentState.setStateEvents([ + mkEvent({ + user: userId1, + event: true, + type: EventType.RoomEncryption, + room: room1.roomId, + content: {}, + }), + ]); }); it("it should return true", () => { @@ -123,4 +127,3 @@ describe("isRoomReady", () => { }); }); }); - diff --git a/test/utils/location/isSelfLocation-test.ts b/test/utils/location/isSelfLocation-test.ts index cd1b3452a9..fd89ae104b 100644 --- a/test/utils/location/isSelfLocation-test.ts +++ b/test/utils/location/isSelfLocation-test.ts @@ -28,7 +28,7 @@ import { isSelfLocation } from "../../../src/utils/location"; describe("isSelfLocation", () => { it("Returns true for a full m.asset event", () => { - const content = makeLocationContent("", '0', Date.now()); + const content = makeLocationContent("", "0", Date.now()); expect(isSelfLocation(content)).toBe(true); }); @@ -62,11 +62,12 @@ describe("isSelfLocation", () => { it("Returns false for an unknown asset type", () => { const content = makeLocationContent( - undefined, /* text */ + undefined /* text */, "geo:foo", 0, - undefined, /* description */ - "org.example.unknown" as unknown as LocationAssetType); + undefined /* description */, + "org.example.unknown" as unknown as LocationAssetType, + ); expect(isSelfLocation(content)).toBe(false); }); }); diff --git a/test/utils/location/locationEventGeoUri-test.ts b/test/utils/location/locationEventGeoUri-test.ts index 52626f98b7..50411aa6ff 100644 --- a/test/utils/location/locationEventGeoUri-test.ts +++ b/test/utils/location/locationEventGeoUri-test.ts @@ -17,12 +17,12 @@ limitations under the License. import { locationEventGeoUri } from "../../../src/utils/location"; import { makeLegacyLocationEvent, makeLocationEvent } from "../../test-utils/location"; -describe('locationEventGeoUri()', () => { - it('returns m.location uri when available', () => { +describe("locationEventGeoUri()", () => { + it("returns m.location uri when available", () => { expect(locationEventGeoUri(makeLocationEvent("geo:51.5076,-0.1276"))).toEqual("geo:51.5076,-0.1276"); }); - it('returns legacy uri when m.location content not found', () => { + it("returns legacy uri when m.location content not found", () => { expect(locationEventGeoUri(makeLegacyLocationEvent("geo:51.5076,-0.1276"))).toEqual("geo:51.5076,-0.1276"); }); }); diff --git a/test/utils/location/map-test.ts b/test/utils/location/map-test.ts index d090926f07..09e91d9fb3 100644 --- a/test/utils/location/map-test.ts +++ b/test/utils/location/map-test.ts @@ -20,28 +20,26 @@ import { makeLegacyLocationEvent, makeLocationEvent } from "../../test-utils/loc describe("createMapSiteLinkFromEvent", () => { it("returns null if event does not contain geouri", () => { - expect(createMapSiteLinkFromEvent(mkMessage({ - room: '1', user: '@sender:server', event: true, - }))).toBeNull(); + expect( + createMapSiteLinkFromEvent( + mkMessage({ + room: "1", + user: "@sender:server", + event: true, + }), + ), + ).toBeNull(); }); it("returns OpenStreetMap link if event contains m.location", () => { - expect( - createMapSiteLinkFromEvent(makeLocationEvent("geo:51.5076,-0.1276")), - ).toEqual( - "https://www.openstreetmap.org/" + - "?mlat=51.5076&mlon=-0.1276" + - "#map=16/51.5076/-0.1276", + expect(createMapSiteLinkFromEvent(makeLocationEvent("geo:51.5076,-0.1276"))).toEqual( + "https://www.openstreetmap.org/" + "?mlat=51.5076&mlon=-0.1276" + "#map=16/51.5076/-0.1276", ); }); it("returns OpenStreetMap link if event contains geo_uri", () => { - expect( - createMapSiteLinkFromEvent(makeLegacyLocationEvent("geo:51.5076,-0.1276")), - ).toEqual( - "https://www.openstreetmap.org/" + - "?mlat=51.5076&mlon=-0.1276" + - "#map=16/51.5076/-0.1276", + expect(createMapSiteLinkFromEvent(makeLegacyLocationEvent("geo:51.5076,-0.1276"))).toEqual( + "https://www.openstreetmap.org/" + "?mlat=51.5076&mlon=-0.1276" + "#map=16/51.5076/-0.1276", ); }); }); diff --git a/test/utils/location/parseGeoUri-test.ts b/test/utils/location/parseGeoUri-test.ts index b0d36929d2..7e7a9020a8 100644 --- a/test/utils/location/parseGeoUri-test.ts +++ b/test/utils/location/parseGeoUri-test.ts @@ -30,128 +30,110 @@ describe("parseGeoUri", () => { // these, but it is permitted, and we will fail to parse in that case. it("rfc5870 6.1 Simple 3-dimensional", () => { - expect(parseGeoUri("geo:48.2010,16.3695,183")).toEqual( - { - latitude: 48.2010, - longitude: 16.3695, - altitude: 183, - accuracy: undefined, - altitudeAccuracy: undefined, - heading: undefined, - speed: undefined, - }, - ); + expect(parseGeoUri("geo:48.2010,16.3695,183")).toEqual({ + latitude: 48.201, + longitude: 16.3695, + altitude: 183, + accuracy: undefined, + altitudeAccuracy: undefined, + heading: undefined, + speed: undefined, + }); }); it("rfc5870 6.2 Explicit CRS and accuracy", () => { - expect(parseGeoUri("geo:48.198634,16.371648;crs=wgs84;u=40")).toEqual( - { - latitude: 48.198634, - longitude: 16.371648, - altitude: undefined, - accuracy: 40, - altitudeAccuracy: undefined, - heading: undefined, - speed: undefined, - }, - ); + expect(parseGeoUri("geo:48.198634,16.371648;crs=wgs84;u=40")).toEqual({ + latitude: 48.198634, + longitude: 16.371648, + altitude: undefined, + accuracy: 40, + altitudeAccuracy: undefined, + heading: undefined, + speed: undefined, + }); }); it("rfc5870 6.4 Negative longitude and explicit CRS", () => { - expect(parseGeoUri("geo:90,-22.43;crs=WGS84")).toEqual( - { - latitude: 90, - longitude: -22.43, - altitude: undefined, - accuracy: undefined, - altitudeAccuracy: undefined, - heading: undefined, - speed: undefined, - }, - ); + expect(parseGeoUri("geo:90,-22.43;crs=WGS84")).toEqual({ + latitude: 90, + longitude: -22.43, + altitude: undefined, + accuracy: undefined, + altitudeAccuracy: undefined, + heading: undefined, + speed: undefined, + }); }); it("rfc5870 6.4 Integer lat and lon", () => { - expect(parseGeoUri("geo:90,46")).toEqual( - { - latitude: 90, - longitude: 46, - altitude: undefined, - accuracy: undefined, - altitudeAccuracy: undefined, - heading: undefined, - speed: undefined, - }, - ); + expect(parseGeoUri("geo:90,46")).toEqual({ + latitude: 90, + longitude: 46, + altitude: undefined, + accuracy: undefined, + altitudeAccuracy: undefined, + heading: undefined, + speed: undefined, + }); }); it("rfc5870 6.4 Percent-encoded param value", () => { - expect(parseGeoUri("geo:66,30;u=6.500;FOo=this%2dthat")).toEqual( - { - latitude: 66, - longitude: 30, - altitude: undefined, - accuracy: 6.500, - altitudeAccuracy: undefined, - heading: undefined, - speed: undefined, - }, - ); + expect(parseGeoUri("geo:66,30;u=6.500;FOo=this%2dthat")).toEqual({ + latitude: 66, + longitude: 30, + altitude: undefined, + accuracy: 6.5, + altitudeAccuracy: undefined, + heading: undefined, + speed: undefined, + }); }); it("rfc5870 6.4 Unknown param", () => { - expect(parseGeoUri("geo:66.0,30;u=6.5;foo=this-that>")).toEqual( - { - latitude: 66.0, - longitude: 30, - altitude: undefined, - accuracy: 6.5, - altitudeAccuracy: undefined, - heading: undefined, - speed: undefined, - }, - ); + expect(parseGeoUri("geo:66.0,30;u=6.5;foo=this-that>")).toEqual({ + latitude: 66.0, + longitude: 30, + altitude: undefined, + accuracy: 6.5, + altitudeAccuracy: undefined, + heading: undefined, + speed: undefined, + }); }); it("rfc5870 6.4 Multiple unknown params", () => { - expect(parseGeoUri("geo:70,20;foo=1.00;bar=white")).toEqual( - { - latitude: 70, - longitude: 20, - altitude: undefined, - accuracy: undefined, - altitudeAccuracy: undefined, - heading: undefined, - speed: undefined, - }, - ); + expect(parseGeoUri("geo:70,20;foo=1.00;bar=white")).toEqual({ + latitude: 70, + longitude: 20, + altitude: undefined, + accuracy: undefined, + altitudeAccuracy: undefined, + heading: undefined, + speed: undefined, + }); }); it("Negative latitude", () => { - expect(parseGeoUri("geo:-7.5,20")).toEqual( - { - latitude: -7.5, - longitude: 20, - altitude: undefined, - accuracy: undefined, - altitudeAccuracy: undefined, - heading: undefined, - speed: undefined, - }, - ); + expect(parseGeoUri("geo:-7.5,20")).toEqual({ + latitude: -7.5, + longitude: 20, + altitude: undefined, + accuracy: undefined, + altitudeAccuracy: undefined, + heading: undefined, + speed: undefined, + }); }); it("Zero altitude is not unknown", () => { - expect(parseGeoUri("geo:-7.5,-20,0")).toEqual( - { - latitude: -7.5, - longitude: -20, - altitude: 0, - accuracy: undefined, - altitudeAccuracy: undefined, - heading: undefined, - speed: undefined, - }, - ); + expect(parseGeoUri("geo:-7.5,-20,0")).toEqual({ + latitude: -7.5, + longitude: -20, + altitude: 0, + accuracy: undefined, + altitudeAccuracy: undefined, + heading: undefined, + speed: undefined, + }); }); }); diff --git a/test/utils/maps-test.ts b/test/utils/maps-test.ts index aea444b2ec..5afc607843 100644 --- a/test/utils/maps-test.ts +++ b/test/utils/maps-test.ts @@ -16,10 +16,14 @@ limitations under the License. import { EnhancedMap, mapDiff } from "../../src/utils/maps"; -describe('maps', () => { - describe('mapDiff', () => { - it('should indicate no differences when the pointers are the same', () => { - const a = new Map([[1, 1], [2, 2], [3, 3]]); +describe("maps", () => { + describe("mapDiff", () => { + it("should indicate no differences when the pointers are the same", () => { + const a = new Map([ + [1, 1], + [2, 2], + [3, 3], + ]); const result = mapDiff(a, a); expect(result).toBeDefined(); expect(result.added).toBeDefined(); @@ -30,9 +34,17 @@ describe('maps', () => { expect(result.changed).toHaveLength(0); }); - it('should indicate no differences when there are none', () => { - const a = new Map([[1, 1], [2, 2], [3, 3]]); - const b = new Map([[1, 1], [2, 2], [3, 3]]); + it("should indicate no differences when there are none", () => { + const a = new Map([ + [1, 1], + [2, 2], + [3, 3], + ]); + const b = new Map([ + [1, 1], + [2, 2], + [3, 3], + ]); const result = mapDiff(a, b); expect(result).toBeDefined(); expect(result.added).toBeDefined(); @@ -43,9 +55,18 @@ describe('maps', () => { expect(result.changed).toHaveLength(0); }); - it('should indicate added properties', () => { - const a = new Map([[1, 1], [2, 2], [3, 3]]); - const b = new Map([[1, 1], [2, 2], [3, 3], [4, 4]]); + it("should indicate added properties", () => { + const a = new Map([ + [1, 1], + [2, 2], + [3, 3], + ]); + const b = new Map([ + [1, 1], + [2, 2], + [3, 3], + [4, 4], + ]); const result = mapDiff(a, b); expect(result).toBeDefined(); expect(result.added).toBeDefined(); @@ -57,9 +78,16 @@ describe('maps', () => { expect(result.added).toEqual([4]); }); - it('should indicate removed properties', () => { - const a = new Map([[1, 1], [2, 2], [3, 3]]); - const b = new Map([[1, 1], [2, 2]]); + it("should indicate removed properties", () => { + const a = new Map([ + [1, 1], + [2, 2], + [3, 3], + ]); + const b = new Map([ + [1, 1], + [2, 2], + ]); const result = mapDiff(a, b); expect(result).toBeDefined(); expect(result.added).toBeDefined(); @@ -71,9 +99,17 @@ describe('maps', () => { expect(result.removed).toEqual([3]); }); - it('should indicate changed properties', () => { - const a = new Map([[1, 1], [2, 2], [3, 3]]); - const b = new Map([[1, 1], [2, 2], [3, 4]]); // note change + it("should indicate changed properties", () => { + const a = new Map([ + [1, 1], + [2, 2], + [3, 3], + ]); + const b = new Map([ + [1, 1], + [2, 2], + [3, 4], + ]); // note change const result = mapDiff(a, b); expect(result).toBeDefined(); expect(result.added).toBeDefined(); @@ -85,9 +121,17 @@ describe('maps', () => { expect(result.changed).toEqual([3]); }); - it('should indicate changed, added, and removed properties', () => { - const a = new Map([[1, 1], [2, 2], [3, 3]]); - const b = new Map([[1, 1], [2, 8], [4, 4]]); // note change + it("should indicate changed, added, and removed properties", () => { + const a = new Map([ + [1, 1], + [2, 2], + [3, 3], + ]); + const b = new Map([ + [1, 1], + [2, 8], + [4, 4], + ]); // note change const result = mapDiff(a, b); expect(result).toBeDefined(); expect(result.added).toBeDefined(); @@ -101,7 +145,7 @@ describe('maps', () => { expect(result.changed).toEqual([2]); }); - it('should indicate changes for difference in pointers', () => { + it("should indicate changes for difference in pointers", () => { const a = new Map([[1, {}]]); // {} always creates a new object const b = new Map([[1, {}]]); const result = mapDiff(a, b); @@ -116,24 +160,24 @@ describe('maps', () => { }); }); - describe('EnhancedMap', () => { + describe("EnhancedMap", () => { // Most of these tests will make sure it implements the Map class - it('should be empty by default', () => { + it("should be empty by default", () => { const result = new EnhancedMap(); expect(result.size).toBe(0); }); - it('should use the provided entries', () => { + it("should use the provided entries", () => { const obj = { a: 1, b: 2 }; const result = new EnhancedMap(Object.entries(obj)); expect(result.size).toBe(2); - expect(result.get('a')).toBe(1); - expect(result.get('b')).toBe(2); + expect(result.get("a")).toBe(1); + expect(result.get("b")).toBe(2); }); - it('should create keys if they do not exist', () => { - const key = 'a'; + it("should create keys if they do not exist", () => { + const key = "a"; const val = {}; // we'll check pointers const result = new EnhancedMap(); @@ -155,27 +199,27 @@ describe('maps', () => { expect(result.size).toBe(1); }); - it('should proxy remove to delete and return it', () => { + it("should proxy remove to delete and return it", () => { const val = {}; const result = new EnhancedMap(); - result.set('a', val); + result.set("a", val); expect(result.size).toBe(1); - const removed = result.remove('a'); + const removed = result.remove("a"); expect(result.size).toBe(0); expect(removed).toBeDefined(); expect(removed).toBe(val); }); - it('should support removing unknown keys', () => { + it("should support removing unknown keys", () => { const val = {}; const result = new EnhancedMap(); - result.set('a', val); + result.set("a", val); expect(result.size).toBe(1); - const removed = result.remove('not-a'); + const removed = result.remove("not-a"); expect(result.size).toBe(1); expect(removed).not.toBeDefined(); }); diff --git a/test/utils/media/requestMediaPermissions-test.tsx b/test/utils/media/requestMediaPermissions-test.tsx index 732a9d8723..0239e6c30e 100644 --- a/test/utils/media/requestMediaPermissions-test.tsx +++ b/test/utils/media/requestMediaPermissions-test.tsx @@ -28,10 +28,7 @@ describe("requestMediaPermissions", () => { const itShouldLogTheErrorAndShowTheNoMediaPermissionsModal = () => { it("should log the error and show the »No media permissions« modal", () => { - expect(logger.log).toHaveBeenCalledWith( - "Failed to list userMedia devices", - error, - ); + expect(logger.log).toHaveBeenCalledWith("Failed to list userMedia devices", error); screen.getByText("No media permissions"); }); }; @@ -91,11 +88,9 @@ describe("requestMediaPermissions", () => { describe("when no device is available", () => { beforeEach(async () => { error.name = "NotFoundError"; - mocked(navigator.mediaDevices.getUserMedia).mockImplementation( - async (): Promise => { - throw error; - }, - ); + mocked(navigator.mediaDevices.getUserMedia).mockImplementation(async (): Promise => { + throw error; + }); await requestMediaPermissions(); // required for the modal to settle await flushPromises(); diff --git a/test/utils/notifications-test.ts b/test/utils/notifications-test.ts index 4d5ec53249..5aa8e2427f 100644 --- a/test/utils/notifications-test.ts +++ b/test/utils/notifications-test.ts @@ -34,7 +34,7 @@ import { MatrixClientPeg } from "../../src/MatrixClientPeg"; jest.mock("../../src/settings/SettingsStore"); -describe('notifications', () => { +describe("notifications", () => { let accountDataStore = {}; let mockClient; let accountDataEventKey; @@ -43,7 +43,7 @@ describe('notifications', () => { jest.clearAllMocks(); mockClient = getMockClientWithEventEmitter({ isGuest: jest.fn().mockReturnValue(false), - getAccountData: jest.fn().mockImplementation(eventType => accountDataStore[eventType]), + getAccountData: jest.fn().mockImplementation((eventType) => accountDataStore[eventType]), setAccountData: jest.fn().mockImplementation((eventType, content) => { accountDataStore[eventType] = new MatrixEvent({ type: eventType, @@ -56,14 +56,14 @@ describe('notifications', () => { mocked(SettingsStore).getValue.mockReturnValue(false); }); - describe('createLocalNotification', () => { - it('creates account data event', async () => { + describe("createLocalNotification", () => { + it("creates account data event", async () => { await createLocalNotificationSettingsIfNeeded(mockClient); const event = mockClient.getAccountData(accountDataEventKey); expect(event?.getContent().is_silenced).toBe(true); }); - it('does not do anything for guests', async () => { + it("does not do anything for guests", async () => { mockClient.isGuest.mockReset().mockReturnValue(true); await createLocalNotificationSettingsIfNeeded(mockClient); const event = mockClient.getAccountData(accountDataEventKey); @@ -71,7 +71,7 @@ describe('notifications', () => { }); it.each(deviceNotificationSettingsKeys)( - 'unsilenced for existing sessions when %s setting is truthy', + "unsilenced for existing sessions when %s setting is truthy", async (settingKey) => { mocked(SettingsStore).getValue.mockImplementation((key): any => { return key === settingKey; @@ -80,7 +80,8 @@ describe('notifications', () => { await createLocalNotificationSettingsIfNeeded(mockClient); const event = mockClient.getAccountData(accountDataEventKey); expect(event?.getContent().is_silenced).toBe(false); - }); + }, + ); it("does not override an existing account event data", async () => { mockClient.setAccountData(accountDataEventKey, { @@ -93,11 +94,11 @@ describe('notifications', () => { }); }); - describe('localNotificationsAreSilenced', () => { - it('defaults to false when no setting exists', () => { + describe("localNotificationsAreSilenced", () => { + it("defaults to false when no setting exists", () => { expect(localNotificationsAreSilenced(mockClient)).toBeFalsy(); }); - it('checks the persisted value', () => { + it("checks the persisted value", () => { mockClient.setAccountData(accountDataEventKey, { is_silenced: true }); expect(localNotificationsAreSilenced(mockClient)).toBeTruthy(); diff --git a/test/utils/numbers-test.ts b/test/utils/numbers-test.ts index 340ffe3302..34ffaaa4ee 100644 --- a/test/utils/numbers-test.ts +++ b/test/utils/numbers-test.ts @@ -16,9 +16,9 @@ limitations under the License. import { clamp, defaultNumber, percentageOf, percentageWithin, sum } from "../../src/utils/numbers"; -describe('numbers', () => { - describe('defaultNumber', () => { - it('should use the default when the input is not a number', () => { +describe("numbers", () => { + describe("defaultNumber", () => { + it("should use the default when the input is not a number", () => { const def = 42; let result = defaultNumber(null, def); @@ -31,7 +31,7 @@ describe('numbers', () => { expect(result).toBe(def); }); - it('should use the number when it is a number', () => { + it("should use the number when it is a number", () => { const input = 24; const def = 42; const result = defaultNumber(input, def); @@ -39,8 +39,8 @@ describe('numbers', () => { }); }); - describe('clamp', () => { - it('should clamp high numbers', () => { + describe("clamp", () => { + it("should clamp high numbers", () => { const input = 101; const min = 0; const max = 100; @@ -48,7 +48,7 @@ describe('numbers', () => { expect(result).toBe(max); }); - it('should clamp low numbers', () => { + it("should clamp low numbers", () => { const input = -1; const min = 0; const max = 100; @@ -56,7 +56,7 @@ describe('numbers', () => { expect(result).toBe(min); }); - it('should not clamp numbers in range', () => { + it("should not clamp numbers in range", () => { const input = 50; const min = 0; const max = 100; @@ -64,9 +64,9 @@ describe('numbers', () => { expect(result).toBe(input); }); - it('should clamp floats', () => { - const min = -0.10; - const max = +0.10; + it("should clamp floats", () => { + const min = -0.1; + const max = +0.1; let result = clamp(-1.2, min, max); expect(result).toBe(min); @@ -79,83 +79,84 @@ describe('numbers', () => { }); }); - describe('sum', () => { - it('should sum', () => { // duh + describe("sum", () => { + it("should sum", () => { + // duh const result = sum(1, 2, 1, 4); expect(result).toBe(8); }); }); - describe('percentageWithin', () => { - it('should work within 0-100', () => { + describe("percentageWithin", () => { + it("should work within 0-100", () => { const result = percentageWithin(0.4, 0, 100); expect(result).toBe(40); }); - it('should work within 0-100 when pct > 1', () => { + it("should work within 0-100 when pct > 1", () => { const result = percentageWithin(1.4, 0, 100); expect(result).toBe(140); }); - it('should work within 0-100 when pct < 0', () => { + it("should work within 0-100 when pct < 0", () => { const result = percentageWithin(-1.4, 0, 100); expect(result).toBe(-140); }); - it('should work with ranges other than 0-100', () => { + it("should work with ranges other than 0-100", () => { const result = percentageWithin(0.4, 10, 20); expect(result).toBe(14); }); - it('should work with ranges other than 0-100 when pct > 1', () => { + it("should work with ranges other than 0-100 when pct > 1", () => { const result = percentageWithin(1.4, 10, 20); expect(result).toBe(24); }); - it('should work with ranges other than 0-100 when pct < 0', () => { + it("should work with ranges other than 0-100 when pct < 0", () => { const result = percentageWithin(-1.4, 10, 20); expect(result).toBe(-4); }); - it('should work with floats', () => { + it("should work with floats", () => { const result = percentageWithin(0.4, 10.2, 20.4); expect(result).toBe(14.28); }); }); // These are the inverse of percentageWithin - describe('percentageOf', () => { - it('should work within 0-100', () => { + describe("percentageOf", () => { + it("should work within 0-100", () => { const result = percentageOf(40, 0, 100); expect(result).toBe(0.4); }); - it('should work within 0-100 when val > 100', () => { + it("should work within 0-100 when val > 100", () => { const result = percentageOf(140, 0, 100); - expect(result).toBe(1.40); + expect(result).toBe(1.4); }); - it('should work within 0-100 when val < 0', () => { + it("should work within 0-100 when val < 0", () => { const result = percentageOf(-140, 0, 100); - expect(result).toBe(-1.40); + expect(result).toBe(-1.4); }); - it('should work with ranges other than 0-100', () => { + it("should work with ranges other than 0-100", () => { const result = percentageOf(14, 10, 20); expect(result).toBe(0.4); }); - it('should work with ranges other than 0-100 when val > 100', () => { + it("should work with ranges other than 0-100 when val > 100", () => { const result = percentageOf(24, 10, 20); expect(result).toBe(1.4); }); - it('should work with ranges other than 0-100 when val < 0', () => { + it("should work with ranges other than 0-100 when val < 0", () => { const result = percentageOf(-4, 10, 20); expect(result).toBe(-1.4); }); - it('should work with floats', () => { + it("should work with floats", () => { const result = percentageOf(14.28, 10.2, 20.4); expect(result).toBe(0.4); }); diff --git a/test/utils/objects-test.ts b/test/utils/objects-test.ts index b360fbd1d1..b6e4d3cba7 100644 --- a/test/utils/objects-test.ts +++ b/test/utils/objects-test.ts @@ -24,9 +24,9 @@ import { objectWithOnly, } from "../../src/utils/objects"; -describe('objects', () => { - describe('objectExcluding', () => { - it('should exclude the given properties', () => { +describe("objects", () => { + describe("objectExcluding", () => { + it("should exclude the given properties", () => { const input = { hello: "world", test: true }; const output = { hello: "world" }; const props = ["test", "doesnotexist"]; // we also make sure it doesn't explode on missing props @@ -36,8 +36,8 @@ describe('objects', () => { }); }); - describe('objectWithOnly', () => { - it('should exclusively use the given properties', () => { + describe("objectWithOnly", () => { + it("should exclusively use the given properties", () => { const input = { hello: "world", test: true }; const output = { hello: "world" }; const props = ["hello", "doesnotexist"]; // we also make sure it doesn't explode on missing props @@ -47,8 +47,8 @@ describe('objects', () => { }); }); - describe('objectShallowClone', () => { - it('should create a new object', () => { + describe("objectShallowClone", () => { + it("should create a new object", () => { const input = { test: 1 }; const result = objectShallowClone(input); expect(result).toBeDefined(); @@ -56,7 +56,7 @@ describe('objects', () => { expect(result).toMatchObject(input); }); - it('should only clone the top level properties', () => { + it("should only clone the top level properties", () => { const input = { a: 1, b: { c: 2 } }; const result = objectShallowClone(input); expect(result).toBeDefined(); @@ -64,7 +64,7 @@ describe('objects', () => { expect(result.b).toBe(input.b); }); - it('should support custom clone functions', () => { + it("should support custom clone functions", () => { const input = { a: 1, b: 2 }; const output = { a: 4, b: 8 }; const result = objectShallowClone(input, (k, v) => { @@ -78,35 +78,35 @@ describe('objects', () => { }); }); - describe('objectHasDiff', () => { - it('should return false for the same pointer', () => { + describe("objectHasDiff", () => { + it("should return false for the same pointer", () => { const a = {}; const result = objectHasDiff(a, a); expect(result).toBe(false); }); - it('should return true if keys for A > keys for B', () => { + it("should return true if keys for A > keys for B", () => { const a = { a: 1, b: 2 }; const b = { a: 1 }; const result = objectHasDiff(a, b); expect(result).toBe(true); }); - it('should return true if keys for A < keys for B', () => { + it("should return true if keys for A < keys for B", () => { const a = { a: 1 }; const b = { a: 1, b: 2 }; const result = objectHasDiff(a, b); expect(result).toBe(true); }); - it('should return false if the objects are the same but different pointers', () => { + it("should return false if the objects are the same but different pointers", () => { const a = { a: 1, b: 2 }; const b = { a: 1, b: 2 }; const result = objectHasDiff(a, b); expect(result).toBe(false); }); - it('should consider pointers when testing values', () => { + it("should consider pointers when testing values", () => { const a = { a: {}, b: 2 }; // `{}` is shorthand for `new Object()` const b = { a: {}, b: 2 }; const result = objectHasDiff(a, b); @@ -114,8 +114,8 @@ describe('objects', () => { }); }); - describe('objectDiff', () => { - it('should return empty sets for the same object', () => { + describe("objectDiff", () => { + it("should return empty sets for the same object", () => { const a = { a: 1, b: 2 }; const b = { a: 1, b: 2 }; const result = objectDiff(a, b); @@ -128,7 +128,7 @@ describe('objects', () => { expect(result.removed).toHaveLength(0); }); - it('should return empty sets for the same object pointer', () => { + it("should return empty sets for the same object pointer", () => { const a = { a: 1, b: 2 }; const result = objectDiff(a, a); expect(result).toBeDefined(); @@ -140,7 +140,7 @@ describe('objects', () => { expect(result.removed).toHaveLength(0); }); - it('should indicate when property changes are made', () => { + it("should indicate when property changes are made", () => { const a = { a: 1, b: 2 }; const b = { a: 11, b: 2 }; const result = objectDiff(a, b); @@ -150,10 +150,10 @@ describe('objects', () => { expect(result.changed).toHaveLength(1); expect(result.added).toHaveLength(0); expect(result.removed).toHaveLength(0); - expect(result.changed).toEqual(['a']); + expect(result.changed).toEqual(["a"]); }); - it('should indicate when properties are added', () => { + it("should indicate when properties are added", () => { const a = { a: 1, b: 2 }; const b = { a: 1, b: 2, c: 3 }; const result = objectDiff(a, b); @@ -163,10 +163,10 @@ describe('objects', () => { expect(result.changed).toHaveLength(0); expect(result.added).toHaveLength(1); expect(result.removed).toHaveLength(0); - expect(result.added).toEqual(['c']); + expect(result.added).toEqual(["c"]); }); - it('should indicate when properties are removed', () => { + it("should indicate when properties are removed", () => { const a = { a: 1, b: 2 }; const b = { a: 1 }; const result = objectDiff(a, b); @@ -176,12 +176,12 @@ describe('objects', () => { expect(result.changed).toHaveLength(0); expect(result.added).toHaveLength(0); expect(result.removed).toHaveLength(1); - expect(result.removed).toEqual(['b']); + expect(result.removed).toEqual(["b"]); }); - it('should indicate when multiple aspects change', () => { + it("should indicate when multiple aspects change", () => { const a = { a: 1, b: 2, c: 3 }; - const b: (typeof a | {d: number}) = { a: 1, b: 22, d: 4 }; + const b: typeof a | { d: number } = { a: 1, b: 22, d: 4 }; const result = objectDiff(a, b); expect(result.changed).toBeDefined(); expect(result.added).toBeDefined(); @@ -189,14 +189,14 @@ describe('objects', () => { expect(result.changed).toHaveLength(1); expect(result.added).toHaveLength(1); expect(result.removed).toHaveLength(1); - expect(result.changed).toEqual(['b']); - expect(result.removed).toEqual(['c']); - expect(result.added).toEqual(['d']); + expect(result.changed).toEqual(["b"]); + expect(result.removed).toEqual(["c"]); + expect(result.added).toEqual(["d"]); }); }); - describe('objectKeyChanges', () => { - it('should return an empty set if no properties changed', () => { + describe("objectKeyChanges", () => { + it("should return an empty set if no properties changed", () => { const a = { a: 1, b: 2 }; const b = { a: 1, b: 2 }; const result = objectKeyChanges(a, b); @@ -204,25 +204,25 @@ describe('objects', () => { expect(result).toHaveLength(0); }); - it('should return an empty set if no properties changed for the same pointer', () => { + it("should return an empty set if no properties changed for the same pointer", () => { const a = { a: 1, b: 2 }; const result = objectKeyChanges(a, a); expect(result).toBeDefined(); expect(result).toHaveLength(0); }); - it('should return properties which were changed, added, or removed', () => { + it("should return properties which were changed, added, or removed", () => { const a = { a: 1, b: 2, c: 3 }; - const b: (typeof a | {d: number}) = { a: 1, b: 22, d: 4 }; + const b: typeof a | { d: number } = { a: 1, b: 22, d: 4 }; const result = objectKeyChanges(a, b); expect(result).toBeDefined(); expect(result).toHaveLength(3); - expect(result).toEqual(['c', 'd', 'b']); // order isn't important, but the test cares + expect(result).toEqual(["c", "d", "b"]); // order isn't important, but the test cares }); }); - describe('objectClone', () => { - it('should deep clone an object', () => { + describe("objectClone", () => { + it("should deep clone an object", () => { const a = { hello: "world", test: { diff --git a/test/utils/permalinks/Permalinks-test.ts b/test/utils/permalinks/Permalinks-test.ts index 85a80e966a..3c38827dcd 100644 --- a/test/utils/permalinks/Permalinks-test.ts +++ b/test/utils/permalinks/Permalinks-test.ts @@ -12,40 +12,37 @@ 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 { - Room, - RoomMember, - EventType, - MatrixEvent, -} from 'matrix-js-sdk/src/matrix'; +import { Room, RoomMember, EventType, MatrixEvent } from "matrix-js-sdk/src/matrix"; -import { MatrixClientPeg } from '../../../src/MatrixClientPeg'; +import { MatrixClientPeg } from "../../../src/MatrixClientPeg"; import { makeRoomPermalink, makeUserPermalink, parsePermalink, RoomPermalinkCreator, } from "../../../src/utils/permalinks/Permalinks"; -import { getMockClientWithEventEmitter } from '../../test-utils'; +import { getMockClientWithEventEmitter } from "../../test-utils"; -describe('Permalinks', function() { - const userId = '@test:example.com'; +describe("Permalinks", function () { + const userId = "@test:example.com"; const mockClient = getMockClientWithEventEmitter({ getUserId: jest.fn().mockReturnValue(userId), getRoom: jest.fn(), }); mockClient.credentials = { userId }; - const makeMemberWithPL = (roomId: Room['roomId'], userId: string, powerLevel: number): RoomMember => { + const makeMemberWithPL = (roomId: Room["roomId"], userId: string, powerLevel: number): RoomMember => { const member = new RoomMember(roomId, userId); member.powerLevel = powerLevel; return member; }; function mockRoom( - roomId: Room['roomId'], members: RoomMember[], serverACLContent?: { deny?: string[], allow?: string[]}, + roomId: Room["roomId"], + members: RoomMember[], + serverACLContent?: { deny?: string[]; allow?: string[] }, ): Room { - members.forEach(m => m.membership = "join"); + members.forEach((m) => (m.membership = "join")); const powerLevelsUsers = members.reduce((pl, member) => { if (Number.isFinite(member.powerLevel)) { pl[member.userId] = member.powerLevel; @@ -58,35 +55,38 @@ describe('Permalinks', function() { const powerLevels = new MatrixEvent({ type: EventType.RoomPowerLevels, room_id: roomId, - state_key: '', + state_key: "", content: { - users: powerLevelsUsers, users_default: 0, + users: powerLevelsUsers, + users_default: 0, }, }); - const serverACL = serverACLContent ? new MatrixEvent({ - type: EventType.RoomServerAcl, - room_id: roomId, - state_key: '', - content: serverACLContent, - }) : undefined; + const serverACL = serverACLContent + ? new MatrixEvent({ + type: EventType.RoomServerAcl, + room_id: roomId, + state_key: "", + content: serverACLContent, + }) + : undefined; const stateEvents = serverACL ? [powerLevels, serverACL] : [powerLevels]; room.currentState.setStateEvents(stateEvents); - jest.spyOn(room, 'getCanonicalAlias').mockReturnValue(null); - jest.spyOn(room, 'getJoinedMembers').mockReturnValue(members); - jest.spyOn(room, 'getMember').mockImplementation((userId) => members.find(m => m.userId === userId)); + jest.spyOn(room, "getCanonicalAlias").mockReturnValue(null); + jest.spyOn(room, "getJoinedMembers").mockReturnValue(members); + jest.spyOn(room, "getMember").mockImplementation((userId) => members.find((m) => m.userId === userId)); return room; } - beforeEach(function() { + beforeEach(function () { jest.clearAllMocks(); }); afterAll(() => { - jest.spyOn(MatrixClientPeg, 'get').mockRestore(); + jest.spyOn(MatrixClientPeg, "get").mockRestore(); }); - it('should pick no candidate servers when the room has no members', function() { + it("should pick no candidate servers when the room has no members", function () { const room = mockRoom("!fake:example.org", []); const creator = new RoomPermalinkCreator(room); creator.load(); @@ -94,7 +94,7 @@ describe('Permalinks', function() { expect(creator.serverCandidates.length).toBe(0); }); - it('should gracefully handle invalid MXIDs', () => { + it("should gracefully handle invalid MXIDs", () => { const roomId = "!fake:example.org"; const alice50 = makeMemberWithPL(roomId, "@alice:pl_50:org", 50); const room = mockRoom(roomId, [alice50]); @@ -103,16 +103,12 @@ describe('Permalinks', function() { expect(creator.serverCandidates).toBeTruthy(); }); - it('should pick a candidate server for the highest power level user in the room', function() { + it("should pick a candidate server for the highest power level user in the room", function () { const roomId = "!fake:example.org"; const alice50 = makeMemberWithPL(roomId, "@alice:pl_50", 50); const alice75 = makeMemberWithPL(roomId, "@alice:pl_75", 75); const alice95 = makeMemberWithPL(roomId, "@alice:pl_95", 95); - const room = mockRoom("!fake:example.org", [ - alice50, - alice75, - alice95, - ]); + const room = mockRoom("!fake:example.org", [alice50, alice75, alice95]); const creator = new RoomPermalinkCreator(room); creator.load(); expect(creator.serverCandidates).toBeTruthy(); @@ -121,7 +117,7 @@ describe('Permalinks', function() { // we don't check the 2nd and 3rd servers because that is done by the next test }); - it('should change candidate server when highest power level user leaves the room', function() { + it("should change candidate server when highest power level user leaves the room", function () { const roomId = "!fake:example.org"; const member95 = makeMemberWithPL(roomId, "@alice:pl_95", 95); @@ -143,7 +139,7 @@ describe('Permalinks', function() { expect(creator.serverCandidates[0]).toBe("pl_95"); }); - it('should pick candidate servers based on user population', function() { + it("should pick candidate servers based on user population", function () { const roomId = "!fake:example.org"; const room = mockRoom(roomId, [ makeMemberWithPL(roomId, "@alice:first", 0), @@ -162,7 +158,7 @@ describe('Permalinks', function() { expect(creator.serverCandidates[2]).toBe("third"); }); - it('should pick prefer candidate servers with higher power levels', function() { + it("should pick prefer candidate servers with higher power levels", function () { const roomId = "!fake:example.org"; const room = mockRoom(roomId, [ makeMemberWithPL(roomId, "@alice:first", 100), @@ -178,7 +174,7 @@ describe('Permalinks', function() { expect(creator.serverCandidates[2]).toBe("third"); }); - it('should pick a maximum of 3 candidate servers', function() { + it("should pick a maximum of 3 candidate servers", function () { const roomId = "!fake:example.org"; const room = mockRoom(roomId, [ makeMemberWithPL(roomId, "@alice:alpha", 100), @@ -193,55 +189,45 @@ describe('Permalinks', function() { expect(creator.serverCandidates.length).toBe(3); }); - it('should not consider IPv4 hosts', function() { + it("should not consider IPv4 hosts", function () { const roomId = "!fake:example.org"; - const room = mockRoom(roomId, [ - makeMemberWithPL(roomId, "@alice:127.0.0.1", 100), - ]); + const room = mockRoom(roomId, [makeMemberWithPL(roomId, "@alice:127.0.0.1", 100)]); const creator = new RoomPermalinkCreator(room); creator.load(); expect(creator.serverCandidates).toBeTruthy(); expect(creator.serverCandidates.length).toBe(0); }); - it('should not consider IPv6 hosts', function() { + it("should not consider IPv6 hosts", function () { const roomId = "!fake:example.org"; - const room = mockRoom(roomId, [ - makeMemberWithPL(roomId, "@alice:[::1]", 100), - ]); + const room = mockRoom(roomId, [makeMemberWithPL(roomId, "@alice:[::1]", 100)]); const creator = new RoomPermalinkCreator(room); creator.load(); expect(creator.serverCandidates).toBeTruthy(); expect(creator.serverCandidates.length).toBe(0); }); - it('should not consider IPv4 hostnames with ports', function() { + it("should not consider IPv4 hostnames with ports", function () { const roomId = "!fake:example.org"; - const room = mockRoom(roomId, [ - makeMemberWithPL(roomId, "@alice:127.0.0.1:8448", 100), - ]); + const room = mockRoom(roomId, [makeMemberWithPL(roomId, "@alice:127.0.0.1:8448", 100)]); const creator = new RoomPermalinkCreator(room); creator.load(); expect(creator.serverCandidates).toBeTruthy(); expect(creator.serverCandidates.length).toBe(0); }); - it('should not consider IPv6 hostnames with ports', function() { + it("should not consider IPv6 hostnames with ports", function () { const roomId = "!fake:example.org"; - const room = mockRoom(roomId, [ - makeMemberWithPL(roomId, "@alice:[::1]:8448", 100), - ]); + const room = mockRoom(roomId, [makeMemberWithPL(roomId, "@alice:[::1]:8448", 100)]); const creator = new RoomPermalinkCreator(room); creator.load(); expect(creator.serverCandidates).toBeTruthy(); expect(creator.serverCandidates.length).toBe(0); }); - it('should work with hostnames with ports', function() { + it("should work with hostnames with ports", function () { const roomId = "!fake:example.org"; - const room = mockRoom(roomId, [ - makeMemberWithPL(roomId, "@alice:example.org:8448", 100), - ]); + const room = mockRoom(roomId, [makeMemberWithPL(roomId, "@alice:example.org:8448", 100)]); const creator = new RoomPermalinkCreator(room); creator.load(); @@ -250,45 +236,57 @@ describe('Permalinks', function() { expect(creator.serverCandidates[0]).toBe("example.org:8448"); }); - it('should not consider servers explicitly denied by ACLs', function() { + it("should not consider servers explicitly denied by ACLs", function () { const roomId = "!fake:example.org"; - const room = mockRoom(roomId, [ - makeMemberWithPL(roomId, "@alice:evilcorp.com", 100), - makeMemberWithPL(roomId, "@bob:chat.evilcorp.com", 0), - ], { - deny: ["evilcorp.com", "*.evilcorp.com"], - allow: ["*"], - }); + const room = mockRoom( + roomId, + [ + makeMemberWithPL(roomId, "@alice:evilcorp.com", 100), + makeMemberWithPL(roomId, "@bob:chat.evilcorp.com", 0), + ], + { + deny: ["evilcorp.com", "*.evilcorp.com"], + allow: ["*"], + }, + ); const creator = new RoomPermalinkCreator(room); creator.load(); expect(creator.serverCandidates).toBeTruthy(); expect(creator.serverCandidates.length).toBe(0); }); - it('should not consider servers not allowed by ACLs', function() { + it("should not consider servers not allowed by ACLs", function () { const roomId = "!fake:example.org"; - const room = mockRoom(roomId, [ - makeMemberWithPL(roomId, "@alice:evilcorp.com", 100), - makeMemberWithPL(roomId, "@bob:chat.evilcorp.com", 0), - ], { - deny: [], - allow: [], // implies "ban everyone" - }); + const room = mockRoom( + roomId, + [ + makeMemberWithPL(roomId, "@alice:evilcorp.com", 100), + makeMemberWithPL(roomId, "@bob:chat.evilcorp.com", 0), + ], + { + deny: [], + allow: [], // implies "ban everyone" + }, + ); const creator = new RoomPermalinkCreator(room); creator.load(); expect(creator.serverCandidates).toBeTruthy(); expect(creator.serverCandidates.length).toBe(0); }); - it('should consider servers not explicitly banned by ACLs', function() { + it("should consider servers not explicitly banned by ACLs", function () { const roomId = "!fake:example.org"; - const room = mockRoom(roomId, [ - makeMemberWithPL(roomId, "@alice:evilcorp.com", 100), - makeMemberWithPL(roomId, "@bob:chat.evilcorp.com", 0), - ], { - deny: ["*.evilcorp.com"], // evilcorp.com is still good though - allow: ["*"], - }); + const room = mockRoom( + roomId, + [ + makeMemberWithPL(roomId, "@alice:evilcorp.com", 100), + makeMemberWithPL(roomId, "@bob:chat.evilcorp.com", 0), + ], + { + deny: ["*.evilcorp.com"], // evilcorp.com is still good though + allow: ["*"], + }, + ); const creator = new RoomPermalinkCreator(room); creator.load(); expect(creator.serverCandidates).toBeTruthy(); @@ -296,15 +294,19 @@ describe('Permalinks', function() { expect(creator.serverCandidates[0]).toEqual("evilcorp.com"); }); - it('should consider servers not disallowed by ACLs', function() { + it("should consider servers not disallowed by ACLs", function () { const roomId = "!fake:example.org"; - const room = mockRoom("!fake:example.org", [ - makeMemberWithPL(roomId, "@alice:evilcorp.com", 100), - makeMemberWithPL(roomId, "@bob:chat.evilcorp.com", 0), - ], { - deny: [], - allow: ["evilcorp.com"], // implies "ban everyone else" - }); + const room = mockRoom( + "!fake:example.org", + [ + makeMemberWithPL(roomId, "@alice:evilcorp.com", 100), + makeMemberWithPL(roomId, "@bob:chat.evilcorp.com", 0), + ], + { + deny: [], + allow: ["evilcorp.com"], // implies "ban everyone else" + }, + ); const creator = new RoomPermalinkCreator(room); creator.load(); expect(creator.serverCandidates).toBeTruthy(); @@ -312,7 +314,7 @@ describe('Permalinks', function() { expect(creator.serverCandidates[0]).toEqual("evilcorp.com"); }); - it('should generate an event permalink for room IDs with no candidate servers', function() { + it("should generate an event permalink for room IDs with no candidate servers", function () { const room = mockRoom("!somewhere:example.org", []); const creator = new RoomPermalinkCreator(room); creator.load(); @@ -320,7 +322,7 @@ describe('Permalinks', function() { expect(result).toBe("https://matrix.to/#/!somewhere:example.org/$something:example.com"); }); - it('should generate an event permalink for room IDs with some candidate servers', function() { + it("should generate an event permalink for room IDs with some candidate servers", function () { const roomId = "!somewhere:example.org"; const room = mockRoom(roomId, [ makeMemberWithPL(roomId, "@alice:first", 100), @@ -332,8 +334,8 @@ describe('Permalinks', function() { expect(result).toBe("https://matrix.to/#/!somewhere:example.org/$something:example.com?via=first&via=second"); }); - it('should generate a room permalink for room IDs with some candidate servers', function() { - mockClient.getRoom.mockImplementation((roomId: Room['roomId']) => { + it("should generate a room permalink for room IDs with some candidate servers", function () { + mockClient.getRoom.mockImplementation((roomId: Room["roomId"]) => { return mockRoom(roomId, [ makeMemberWithPL(roomId, "@alice:first", 100), makeMemberWithPL(roomId, "@bob:second", 0), @@ -343,14 +345,14 @@ describe('Permalinks', function() { expect(result).toBe("https://matrix.to/#/!somewhere:example.org?via=first&via=second"); }); - it('should generate a room permalink for room aliases with no candidate servers', function() { + it("should generate a room permalink for room aliases with no candidate servers", function () { mockClient.getRoom.mockReturnValue(null); const result = makeRoomPermalink("#somewhere:example.org"); expect(result).toBe("https://matrix.to/#/#somewhere:example.org"); }); - it('should generate a room permalink for room aliases without candidate servers', function() { - mockClient.getRoom.mockImplementation((roomId: Room['roomId']) => { + it("should generate a room permalink for room aliases without candidate servers", function () { + mockClient.getRoom.mockImplementation((roomId: Room["roomId"]) => { return mockRoom(roomId, [ makeMemberWithPL(roomId, "@alice:first", 100), makeMemberWithPL(roomId, "@bob:second", 0), @@ -360,26 +362,27 @@ describe('Permalinks', function() { expect(result).toBe("https://matrix.to/#/#somewhere:example.org"); }); - it('should generate a user permalink', function() { + it("should generate a user permalink", function () { const result = makeUserPermalink("@someone:example.org"); expect(result).toBe("https://matrix.to/#/@someone:example.org"); }); - it('should correctly parse room permalinks with a via argument', () => { + it("should correctly parse room permalinks with a via argument", () => { const result = parsePermalink("https://matrix.to/#/!room_id:server?via=some.org"); expect(result.roomIdOrAlias).toBe("!room_id:server"); expect(result.viaServers).toEqual(["some.org"]); }); - it('should correctly parse room permalink via arguments', () => { + it("should correctly parse room permalink via arguments", () => { const result = parsePermalink("https://matrix.to/#/!room_id:server?via=foo.bar&via=bar.foo"); expect(result.roomIdOrAlias).toBe("!room_id:server"); expect(result.viaServers).toEqual(["foo.bar", "bar.foo"]); }); - it('should correctly parse event permalink via arguments', () => { - const result = parsePermalink("https://matrix.to/#/!room_id:server/$event_id/some_thing_here/foobar" + - "?via=m1.org&via=m2.org"); + it("should correctly parse event permalink via arguments", () => { + const result = parsePermalink( + "https://matrix.to/#/!room_id:server/$event_id/some_thing_here/foobar" + "?via=m1.org&via=m2.org", + ); expect(result.eventId).toBe("$event_id/some_thing_here/foobar"); expect(result.roomIdOrAlias).toBe("!room_id:server"); expect(result.viaServers).toEqual(["m1.org", "m2.org"]); diff --git a/test/utils/pillify-test.tsx b/test/utils/pillify-test.tsx index 1ceff1cb84..750960c953 100644 --- a/test/utils/pillify-test.tsx +++ b/test/utils/pillify-test.tsx @@ -44,11 +44,13 @@ describe("pillify", () => { rule_id: ".m.rule.roomnotif", default: true, enabled: true, - conditions: [{ - kind: ConditionKind.EventMatch, - key: "content.body", - pattern: "@room", - }], + conditions: [ + { + kind: ConditionKind.EventMatch, + key: "content.body", + pattern: "@room", + }, + ], actions: [ PushRuleActionName.Notify, { diff --git a/test/utils/room/getJoinedNonFunctionalMembers-test.ts b/test/utils/room/getJoinedNonFunctionalMembers-test.ts index 7973f6f384..ddc05fccf3 100644 --- a/test/utils/room/getJoinedNonFunctionalMembers-test.ts +++ b/test/utils/room/getJoinedNonFunctionalMembers-test.ts @@ -50,10 +50,7 @@ describe("getJoinedNonFunctionalMembers", () => { describe("if there are only regular room members", () => { beforeEach(() => { - mocked(room.getJoinedMembers).mockReturnValue([ - roomMember1, - roomMember2, - ]); + mocked(room.getJoinedMembers).mockReturnValue([roomMember1, roomMember2]); mocked(getFunctionalMembers).mockReturnValue([]); }); @@ -67,9 +64,7 @@ describe("getJoinedNonFunctionalMembers", () => { describe("if there are only functional room members", () => { beforeEach(() => { mocked(room.getJoinedMembers).mockReturnValue([]); - mocked(getFunctionalMembers).mockReturnValue([ - "@functional:example.com", - ]); + mocked(getFunctionalMembers).mockReturnValue(["@functional:example.com"]); }); it("should return an empty list", () => { @@ -79,13 +74,8 @@ describe("getJoinedNonFunctionalMembers", () => { describe("if there are some functional room members", () => { beforeEach(() => { - mocked(room.getJoinedMembers).mockReturnValue([ - roomMember1, - roomMember2, - ]); - mocked(getFunctionalMembers).mockReturnValue([ - roomMember1.userId, - ]); + mocked(room.getJoinedMembers).mockReturnValue([roomMember1, roomMember2]); + mocked(getFunctionalMembers).mockReturnValue([roomMember1.userId]); }); it("should only return the non-functional members", () => { diff --git a/test/utils/room/getRoomFunctionalMembers-test.ts b/test/utils/room/getRoomFunctionalMembers-test.ts index bcbba0f226..0c979b46d7 100644 --- a/test/utils/room/getRoomFunctionalMembers-test.ts +++ b/test/utils/room/getRoomFunctionalMembers-test.ts @@ -28,26 +28,30 @@ describe("getRoomFunctionalMembers", () => { }); it("should return an empty array if functional members state event does not have a service_members field", () => { - room.currentState.setStateEvents([mkEvent({ - event: true, - type: UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, - user: "@user:example.com)", - room: room.roomId, - skey: "", - content: {}, - })]); + room.currentState.setStateEvents([ + mkEvent({ + event: true, + type: UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, + user: "@user:example.com)", + room: room.roomId, + skey: "", + content: {}, + }), + ]); expect(getFunctionalMembers(room)).toHaveLength(0); }); it("should return service_members field of the functional users state event", () => { - room.currentState.setStateEvents([mkEvent({ - event: true, - type: UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, - user: "@user:example.com)", - room: room.roomId, - skey: "", - content: { service_members: ["@user:example.com"] }, - })]); + room.currentState.setStateEvents([ + mkEvent({ + event: true, + type: UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, + user: "@user:example.com)", + room: room.roomId, + skey: "", + content: { service_members: ["@user:example.com"] }, + }), + ]); expect(getFunctionalMembers(room)).toEqual(["@user:example.com"]); }); }); diff --git a/test/utils/sets-test.ts b/test/utils/sets-test.ts index fec6cab0f3..ad31909564 100644 --- a/test/utils/sets-test.ts +++ b/test/utils/sets-test.ts @@ -16,37 +16,37 @@ limitations under the License. import { setHasDiff } from "../../src/utils/sets"; -describe('sets', () => { - describe('setHasDiff', () => { - it('should flag true on A length > B length', () => { +describe("sets", () => { + describe("setHasDiff", () => { + it("should flag true on A length > B length", () => { const a = new Set([1, 2, 3, 4]); const b = new Set([1, 2, 3]); const result = setHasDiff(a, b); expect(result).toBe(true); }); - it('should flag true on A length < B length', () => { + it("should flag true on A length < B length", () => { const a = new Set([1, 2, 3]); const b = new Set([1, 2, 3, 4]); const result = setHasDiff(a, b); expect(result).toBe(true); }); - it('should flag true on element differences', () => { + it("should flag true on element differences", () => { const a = new Set([1, 2, 3]); const b = new Set([4, 5, 6]); const result = setHasDiff(a, b); expect(result).toBe(true); }); - it('should flag false if same but order different', () => { + it("should flag false if same but order different", () => { const a = new Set([1, 2, 3]); const b = new Set([3, 1, 2]); const result = setHasDiff(a, b); expect(result).toBe(false); }); - it('should flag false if same', () => { + it("should flag false if same", () => { const a = new Set([1, 2, 3]); const b = new Set([1, 2, 3]); const result = setHasDiff(a, b); diff --git a/test/utils/stringOrderField-test.ts b/test/utils/stringOrderField-test.ts index 331627dfc0..0b0179e659 100644 --- a/test/utils/stringOrderField-test.ts +++ b/test/utils/stringOrderField-test.ts @@ -33,7 +33,7 @@ const moveLexicographicallyTest = ( zipped[index][1] = order; }); - const newOrders = sortBy(zipped, i => i[1]); + const newOrders = sortBy(zipped, (i) => i[1]); expect(newOrders[toIndex][0]).toBe(fromIndex); expect(ops).toHaveLength(expectedChanges); }; @@ -64,73 +64,33 @@ describe("stringOrderField", () => { }); it("should work when all orders are undefined", () => { - moveLexicographicallyTest( - [undefined, undefined, undefined, undefined, undefined, undefined], - 4, - 1, - 2, - ); + moveLexicographicallyTest([undefined, undefined, undefined, undefined, undefined, undefined], 4, 1, 2); }); it("should work when moving to end and all orders are undefined", () => { - moveLexicographicallyTest( - [undefined, undefined, undefined, undefined, undefined, undefined], - 1, - 4, - 5, - ); + moveLexicographicallyTest([undefined, undefined, undefined, undefined, undefined, undefined], 1, 4, 5); }); it("should work when moving left and some orders are undefined", () => { - moveLexicographicallyTest( - ["a", "c", "e", undefined, undefined, undefined], - 5, - 2, - 1, - ); + moveLexicographicallyTest(["a", "c", "e", undefined, undefined, undefined], 5, 2, 1); - moveLexicographicallyTest( - ["a", "a", "e", undefined, undefined, undefined], - 5, - 1, - 2, - ); + moveLexicographicallyTest(["a", "a", "e", undefined, undefined, undefined], 5, 1, 2); }); it("should work moving to the start when all is undefined", () => { - moveLexicographicallyTest( - [undefined, undefined, undefined, undefined], - 2, - 0, - 1, - ); + moveLexicographicallyTest([undefined, undefined, undefined, undefined], 2, 0, 1); }); it("should work moving to the end when all is undefined", () => { - moveLexicographicallyTest( - [undefined, undefined, undefined, undefined], - 1, - 3, - 4, - ); + moveLexicographicallyTest([undefined, undefined, undefined, undefined], 1, 3, 4); }); it("should work moving left when all is undefined", () => { - moveLexicographicallyTest( - [undefined, undefined, undefined, undefined, undefined, undefined], - 4, - 1, - 2, - ); + moveLexicographicallyTest([undefined, undefined, undefined, undefined, undefined, undefined], 4, 1, 2); }); it("should work moving right when all is undefined", () => { - moveLexicographicallyTest( - [undefined, undefined, undefined, undefined], - 1, - 2, - 3, - ); + moveLexicographicallyTest([undefined, undefined, undefined, undefined], 1, 2, 3); }); it("should work moving more right when all is undefined", () => { @@ -143,12 +103,7 @@ describe("stringOrderField", () => { }); it("should work moving left when right is undefined", () => { - moveLexicographicallyTest( - ["20", undefined, undefined, undefined, undefined, undefined], - 4, - 2, - 2, - ); + moveLexicographicallyTest(["20", undefined, undefined, undefined, undefined, undefined], 4, 2, 2); }); it("should work moving right when right is undefined", () => { @@ -161,49 +116,23 @@ describe("stringOrderField", () => { }); it("should work moving left when right is defined", () => { - moveLexicographicallyTest( - ["10", "20", "30", "40", undefined, undefined], - 3, - 1, - 1, - ); + moveLexicographicallyTest(["10", "20", "30", "40", undefined, undefined], 3, 1, 1); }); it("should work moving right when right is defined", () => { - moveLexicographicallyTest( - ["10", "20", "30", "40", "50", undefined], - 1, - 3, - 1, - ); + moveLexicographicallyTest(["10", "20", "30", "40", "50", undefined], 1, 3, 1); }); it("should work moving left when all is defined", () => { - moveLexicographicallyTest( - ["11", "13", "15", "17", "19"], - 2, - 1, - 1, - ); + moveLexicographicallyTest(["11", "13", "15", "17", "19"], 2, 1, 1); }); it("should work moving right when all is defined", () => { - moveLexicographicallyTest( - ["11", "13", "15", "17", "19"], - 1, - 2, - 1, - ); + moveLexicographicallyTest(["11", "13", "15", "17", "19"], 1, 2, 1); }); it("should work moving left into no left space", () => { - moveLexicographicallyTest( - ["11", "12", "13", "14", "19"], - 3, - 1, - 2, - 2, - ); + moveLexicographicallyTest(["11", "12", "13", "14", "19"], 3, 1, 2, 2); moveLexicographicallyTest( [ @@ -223,13 +152,7 @@ describe("stringOrderField", () => { }); it("should work moving right into no right space", () => { - moveLexicographicallyTest( - ["15", "16", "17", "18", "19"], - 1, - 3, - 3, - 2, - ); + moveLexicographicallyTest(["15", "16", "17", "18", "19"], 1, 3, 3, 2); moveLexicographicallyTest( [ @@ -247,30 +170,13 @@ describe("stringOrderField", () => { }); it("should work moving right into no left space", () => { - moveLexicographicallyTest( - ["11", "12", "13", "14", "15", "16", undefined], - 1, - 3, - 3, - ); + moveLexicographicallyTest(["11", "12", "13", "14", "15", "16", undefined], 1, 3, 3); - moveLexicographicallyTest( - ["0", "1", "2", "3", "4", "5"], - 1, - 3, - 3, - 1, - ); + moveLexicographicallyTest(["0", "1", "2", "3", "4", "5"], 1, 3, 3, 1); }); it("should work moving left into no right space", () => { - moveLexicographicallyTest( - ["15", "16", "17", "18", "19"], - 4, - 3, - 4, - 2, - ); + moveLexicographicallyTest(["15", "16", "17", "18", "19"], 4, 3, 4, 2); moveLexicographicallyTest( [ @@ -288,4 +194,3 @@ describe("stringOrderField", () => { }); }); }); - diff --git a/test/utils/tooltipify-test.tsx b/test/utils/tooltipify-test.tsx index 0049bf7acc..c1f49d5e48 100644 --- a/test/utils/tooltipify-test.tsx +++ b/test/utils/tooltipify-test.tsx @@ -14,18 +14,17 @@ 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 React from "react"; +import { render } from "@testing-library/react"; -import { tooltipifyLinks } from '../../src/utils/tooltipify'; -import PlatformPeg from '../../src/PlatformPeg'; -import BasePlatform from '../../src/BasePlatform'; +import { tooltipifyLinks } from "../../src/utils/tooltipify"; +import PlatformPeg from "../../src/PlatformPeg"; +import BasePlatform from "../../src/BasePlatform"; -describe('tooltipify', () => { - jest.spyOn(PlatformPeg, 'get') - .mockReturnValue({ needsUrlTooltips: () => true } as unknown as BasePlatform); +describe("tooltipify", () => { + jest.spyOn(PlatformPeg, "get").mockReturnValue({ needsUrlTooltips: () => true } as unknown as BasePlatform); - it('does nothing for empty element', () => { + it("does nothing for empty element", () => { const { container: root } = render(
); const originalHtml = root.outerHTML; const containers: Element[] = []; @@ -34,8 +33,12 @@ describe('tooltipify', () => { expect(root.outerHTML).toEqual(originalHtml); }); - it('wraps single anchor', () => { - const { container: root } = render(); + it("wraps single anchor", () => { + const { container: root } = render( +
+ click +
, + ); const containers: Element[] = []; tooltipifyLinks([root], [], containers); expect(containers).toHaveLength(1); @@ -45,8 +48,12 @@ describe('tooltipify', () => { expect(tooltip).toBeDefined(); }); - it('ignores node', () => { - const { container: root } = render(); + it("ignores node", () => { + const { container: root } = render( +
+ click +
, + ); const originalHtml = root.outerHTML; const containers: Element[] = []; tooltipifyLinks([root], [root.children[0]], containers); @@ -55,7 +62,11 @@ describe('tooltipify', () => { }); it("does not re-wrap if called multiple times", () => { - const { container: root } = render(); + const { container: root } = render( +
+ click +
, + ); const containers: Element[] = []; tooltipifyLinks([root], [], containers); tooltipifyLinks([root], [], containers); diff --git a/test/utils/validate/numberInRange-test.ts b/test/utils/validate/numberInRange-test.ts index dd28f0fc39..00ba38f7bd 100644 --- a/test/utils/validate/numberInRange-test.ts +++ b/test/utils/validate/numberInRange-test.ts @@ -14,29 +14,30 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { validateNumberInRange } from '../../../src/utils/validate'; +import { validateNumberInRange } from "../../../src/utils/validate"; -describe('validateNumberInRange', () => { - const min = 1; const max = 10; - it('returns false when value is a not a number', () => { - expect(validateNumberInRange(min, max)('test' as unknown as number)).toEqual(false); +describe("validateNumberInRange", () => { + const min = 1; + const max = 10; + it("returns false when value is a not a number", () => { + expect(validateNumberInRange(min, max)("test" as unknown as number)).toEqual(false); }); - it('returns false when value is undefined', () => { + it("returns false when value is undefined", () => { expect(validateNumberInRange(min, max)(undefined)).toEqual(false); }); - it('returns false when value is NaN', () => { + it("returns false when value is NaN", () => { expect(validateNumberInRange(min, max)(NaN)).toEqual(false); }); - it('returns true when value is equal to min', () => { + it("returns true when value is equal to min", () => { expect(validateNumberInRange(min, max)(min)).toEqual(true); }); - it('returns true when value is equal to max', () => { + it("returns true when value is equal to max", () => { expect(validateNumberInRange(min, max)(max)).toEqual(true); }); - it('returns true when value is an int in range', () => { + it("returns true when value is an int in range", () => { expect(validateNumberInRange(min, max)(2)).toEqual(true); }); - it('returns true when value is a float in range', () => { + it("returns true when value is a float in range", () => { expect(validateNumberInRange(min, max)(2.2)).toEqual(true); }); }); diff --git a/test/voice-broadcast/audio/VoiceBroadcastRecorder-test.ts b/test/voice-broadcast/audio/VoiceBroadcastRecorder-test.ts index d4011613b6..9131aa9895 100644 --- a/test/voice-broadcast/audio/VoiceBroadcastRecorder-test.ts +++ b/test/voice-broadcast/audio/VoiceBroadcastRecorder-test.ts @@ -86,13 +86,10 @@ describe("VoiceBroadcastRecorder", () => { }; const expectOnFirstChunkRecorded = (): void => { - expect(onChunkRecorded).toHaveBeenNthCalledWith( - 1, - { - buffer: concat(headers1, headers2, chunk1), - length: 42, - }, - ); + expect(onChunkRecorded).toHaveBeenNthCalledWith(1, { + buffer: concat(headers1, headers2, chunk1), + length: 42, + }); }; const itShouldNotEmitAChunkRecordedEvent = (): void => { @@ -223,13 +220,10 @@ describe("VoiceBroadcastRecorder", () => { it("should emit ChunkRecorded events", () => { expectOnFirstChunkRecorded(); - expect(onChunkRecorded).toHaveBeenNthCalledWith( - 2, - { - buffer: concat(headers1, headers2, chunk2a, chunk2b), - length: 72 - 42, // 72 (position at second chunk) - 42 (position of first chunk) - }, - ); + expect(onChunkRecorded).toHaveBeenNthCalledWith(2, { + buffer: concat(headers1, headers2, chunk2a, chunk2b), + length: 72 - 42, // 72 (position at second chunk) - 42 (position of first chunk) + }); }); }); }); diff --git a/test/voice-broadcast/components/VoiceBroadcastBody-test.tsx b/test/voice-broadcast/components/VoiceBroadcastBody-test.tsx index 0e80d48075..105edb5270 100644 --- a/test/voice-broadcast/components/VoiceBroadcastBody-test.tsx +++ b/test/voice-broadcast/components/VoiceBroadcastBody-test.tsx @@ -57,13 +57,15 @@ describe("VoiceBroadcastBody", () => { let testPlayback: VoiceBroadcastPlayback; const renderVoiceBroadcast = () => { - render( {}} - onMessageAllowed={() => {}} - permalinkCreator={new RoomPermalinkCreator(room)} - />); + render( + {}} + onMessageAllowed={() => {}} + permalinkCreator={new RoomPermalinkCreator(room)} + />, + ); testRecording = VoiceBroadcastRecordingsStore.instance().getByInfoEvent(infoEvent, client); }; @@ -79,12 +81,7 @@ describe("VoiceBroadcastBody", () => { return null; }); - infoEvent = mkVoiceBroadcastInfoStateEvent( - roomId, - VoiceBroadcastInfoState.Started, - userId, - deviceId, - ); + infoEvent = mkVoiceBroadcastInfoStateEvent(roomId, VoiceBroadcastInfoState.Started, userId, deviceId); stoppedEvent = mkVoiceBroadcastInfoStateEvent( roomId, VoiceBroadcastInfoState.Stopped, diff --git a/test/voice-broadcast/components/atoms/VoiceBroadcastControl-test.tsx b/test/voice-broadcast/components/atoms/VoiceBroadcastControl-test.tsx index 391731d593..2870e0634b 100644 --- a/test/voice-broadcast/components/atoms/VoiceBroadcastControl-test.tsx +++ b/test/voice-broadcast/components/atoms/VoiceBroadcastControl-test.tsx @@ -31,11 +31,7 @@ describe("VoiceBroadcastControl", () => { describe("when rendering it", () => { beforeEach(() => { - result = render(); + result = render(); }); it("should render as expected", () => { diff --git a/test/voice-broadcast/components/atoms/VoiceBroadcastHeader-test.tsx b/test/voice-broadcast/components/atoms/VoiceBroadcastHeader-test.tsx index e090841c82..cd26723226 100644 --- a/test/voice-broadcast/components/atoms/VoiceBroadcastHeader-test.tsx +++ b/test/voice-broadcast/components/atoms/VoiceBroadcastHeader-test.tsx @@ -23,7 +23,7 @@ import { mkRoom, stubClient } from "../../../test-utils"; jest.mock("../../../../src/components/views/avatars/RoomAvatar", () => ({ __esModule: true, default: jest.fn().mockImplementation(({ room }) => { - return
room avatar: { room.name }
; + return
room avatar: {room.name}
; }), })); @@ -35,18 +35,16 @@ describe("VoiceBroadcastHeader", () => { const sender = new RoomMember(roomId, userId); let container: Container; - const renderHeader = ( - live: VoiceBroadcastLiveness, - showBroadcast?: boolean, - buffering?: boolean, - ): RenderResult => { - return render(); + const renderHeader = (live: VoiceBroadcastLiveness, showBroadcast?: boolean, buffering?: boolean): RenderResult => { + return render( + , + ); }; beforeAll(() => { diff --git a/test/voice-broadcast/components/molecules/VoiceBroadcastPlaybackBody-test.tsx b/test/voice-broadcast/components/molecules/VoiceBroadcastPlaybackBody-test.tsx index 901a4feb82..02cfff0042 100644 --- a/test/voice-broadcast/components/molecules/VoiceBroadcastPlaybackBody-test.tsx +++ b/test/voice-broadcast/components/molecules/VoiceBroadcastPlaybackBody-test.tsx @@ -35,7 +35,7 @@ import { mkVoiceBroadcastInfoStateEvent } from "../../utils/test-utils"; jest.mock("../../../../src/components/views/avatars/RoomAvatar", () => ({ __esModule: true, default: jest.fn().mockImplementation(({ room }) => { - return
room avatar: { room.name }
; + return
room avatar: {room.name}
; }), })); diff --git a/test/voice-broadcast/components/molecules/VoiceBroadcastPreRecordingPip-test.tsx b/test/voice-broadcast/components/molecules/VoiceBroadcastPreRecordingPip-test.tsx index 61636ce000..cece0b8679 100644 --- a/test/voice-broadcast/components/molecules/VoiceBroadcastPreRecordingPip-test.tsx +++ b/test/voice-broadcast/components/molecules/VoiceBroadcastPreRecordingPip-test.tsx @@ -36,7 +36,7 @@ jest.mock("../../../../src/utils/media/requestMediaPermissions"); jest.mock("../../../../src/components/views/avatars/RoomAvatar", () => ({ __esModule: true, default: jest.fn().mockImplementation(({ room }) => { - return
room avatar: { room.name }
; + return
room avatar: {room.name}
; }), })); @@ -55,11 +55,13 @@ describe("VoiceBroadcastPreRecordingPip", () => { sender = new RoomMember(room.roomId, client.getUserId() || ""); playbacksStore = new VoiceBroadcastPlaybacksStore(); recordingsStore = new VoiceBroadcastRecordingsStore(); - mocked(requestMediaPermissions).mockReturnValue(new Promise((r) => { - r({ - getTracks: () => [], - } as unknown as MediaStream); - })); + mocked(requestMediaPermissions).mockReturnValue( + new Promise((r) => { + r({ + getTracks: () => [], + } as unknown as MediaStream); + }), + ); jest.spyOn(MediaDeviceHandler, "getDevices").mockResolvedValue({ [MediaDeviceKindEnum.AudioInput]: [ { @@ -75,13 +77,7 @@ describe("VoiceBroadcastPreRecordingPip", () => { [MediaDeviceKindEnum.VideoInput]: [], }); jest.spyOn(MediaDeviceHandler.instance, "setDevice").mockImplementation(); - preRecording = new VoiceBroadcastPreRecording( - room, - sender, - client, - playbacksStore, - recordingsStore, - ); + preRecording = new VoiceBroadcastPreRecording(room, sender, client, playbacksStore, recordingsStore); }); afterAll(() => { @@ -90,9 +86,7 @@ describe("VoiceBroadcastPreRecordingPip", () => { describe("when rendered", () => { beforeEach(async () => { - renderResult = render(); + renderResult = render(); await act(async () => { flushPromises(); diff --git a/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody-test.tsx b/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody-test.tsx index d665366580..2edf6b002d 100644 --- a/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody-test.tsx +++ b/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody-test.tsx @@ -30,7 +30,7 @@ import { mkEvent, stubClient } from "../../../test-utils"; jest.mock("../../../../src/components/views/avatars/RoomAvatar", () => ({ __esModule: true, default: jest.fn().mockImplementation(({ room }) => { - return
room avatar: { room.name }
; + return
room avatar: {room.name}
; }), })); diff --git a/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingPip-test.tsx b/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingPip-test.tsx index 5aac28fbeb..b996d3d13b 100644 --- a/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingPip-test.tsx +++ b/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingPip-test.tsx @@ -38,7 +38,7 @@ jest.mock("../../../../src/utils/media/requestMediaPermissions"); jest.mock("../../../../src/components/views/avatars/RoomAvatar", () => ({ __esModule: true, default: jest.fn().mockImplementation(({ room }) => { - return
room avatar: { room.name }
; + return
room avatar: {room.name}
; }), })); @@ -62,12 +62,7 @@ describe("VoiceBroadcastRecordingPip", () => { let restoreConsole: () => void; const renderPip = async (state: VoiceBroadcastInfoState) => { - infoEvent = mkVoiceBroadcastInfoStateEvent( - roomId, - state, - client.getUserId() || "", - client.getDeviceId() || "", - ); + infoEvent = mkVoiceBroadcastInfoStateEvent(roomId, state, client.getUserId() || "", client.getDeviceId() || ""); recording = new VoiceBroadcastRecording(infoEvent, client, state); jest.spyOn(recording, "pause"); jest.spyOn(recording, "resume"); @@ -79,11 +74,13 @@ describe("VoiceBroadcastRecordingPip", () => { beforeAll(() => { client = stubClient(); - mocked(requestMediaPermissions).mockReturnValue(new Promise((r) => { - r({ - getTracks: () => [], - } as unknown as MediaStream); - })); + mocked(requestMediaPermissions).mockReturnValue( + new Promise((r) => { + r({ + getTracks: () => [], + } as unknown as MediaStream); + }), + ); jest.spyOn(MediaDeviceHandler, "getDevices").mockResolvedValue({ [MediaDeviceKindEnum.AudioInput]: [ { diff --git a/test/voice-broadcast/models/VoiceBroadcastPlayback-test.ts b/test/voice-broadcast/models/VoiceBroadcastPlayback-test.ts index 269ee1a3e7..05eb71001b 100644 --- a/test/voice-broadcast/models/VoiceBroadcastPlayback-test.ts +++ b/test/voice-broadcast/models/VoiceBroadcastPlayback-test.ts @@ -115,12 +115,7 @@ describe("VoiceBroadcastPlayback", () => { }; const mkInfoEvent = (state: VoiceBroadcastInfoState) => { - return mkVoiceBroadcastInfoStateEvent( - roomId, - state, - userId, - deviceId, - ); + return mkVoiceBroadcastInfoStateEvent(roomId, state, userId, deviceId); }; const mkPlayback = async () => { @@ -350,7 +345,7 @@ describe("VoiceBroadcastPlayback", () => { }); describe("and skipping to the middle of the second chunk", () => { - const middleOfSecondChunk = (chunk1Length + (chunk2Length / 2)) / 1000; + const middleOfSecondChunk = (chunk1Length + chunk2Length / 2) / 1000; beforeEach(async () => { await playback.skipTo(middleOfSecondChunk); diff --git a/test/voice-broadcast/models/VoiceBroadcastPreRecording-test.ts b/test/voice-broadcast/models/VoiceBroadcastPreRecording-test.ts index 2c2db30b38..985a8156a2 100644 --- a/test/voice-broadcast/models/VoiceBroadcastPreRecording-test.ts +++ b/test/voice-broadcast/models/VoiceBroadcastPreRecording-test.ts @@ -56,12 +56,7 @@ describe("VoiceBroadcastPreRecording", () => { }); it("should start a new voice broadcast recording", () => { - expect(startNewVoiceBroadcastRecording).toHaveBeenCalledWith( - room, - client, - playbacksStore, - recordingsStore, - ); + expect(startNewVoiceBroadcastRecording).toHaveBeenCalledWith(room, client, playbacksStore, recordingsStore); }); it("should emit a dismiss event", () => { diff --git a/test/voice-broadcast/models/VoiceBroadcastRecording-test.ts b/test/voice-broadcast/models/VoiceBroadcastRecording-test.ts index a9df1d70ed..21a8986bbd 100644 --- a/test/voice-broadcast/models/VoiceBroadcastRecording-test.ts +++ b/test/voice-broadcast/models/VoiceBroadcastRecording-test.ts @@ -47,7 +47,7 @@ import dis from "../../../src/dispatcher/dispatcher"; import { VoiceRecording } from "../../../src/audio/VoiceRecording"; jest.mock("../../../src/voice-broadcast/audio/VoiceBroadcastRecorder", () => ({ - ...jest.requireActual("../../../src/voice-broadcast/audio/VoiceBroadcastRecorder") as object, + ...(jest.requireActual("../../../src/voice-broadcast/audio/VoiceBroadcastRecorder") as object), createVoiceBroadcastRecorder: jest.fn(), })); @@ -141,44 +141,41 @@ describe("VoiceBroadcastRecording", () => { new Blob([new Uint8Array(data)], { type: voiceBroadcastRecorder.contentType }), ); - expect(mocked(client.sendMessage)).toHaveBeenCalledWith( - roomId, - { - body: "Voice message", + expect(mocked(client.sendMessage)).toHaveBeenCalledWith(roomId, { + body: "Voice message", + file: { + file: true, + }, + info: { + duration, + mimetype: "audio/ogg", + size, + }, + ["m.relates_to"]: { + event_id: infoEvent.getId(), + rel_type: "m.reference", + }, + msgtype: "m.audio", + ["org.matrix.msc1767.audio"]: { + duration, + waveform: undefined, + }, + ["org.matrix.msc1767.file"]: { file: { file: true, }, - info: { - duration, - mimetype: "audio/ogg", - size, - }, - ["m.relates_to"]: { - event_id: infoEvent.getId(), - rel_type: "m.reference", - }, - msgtype: "m.audio", - ["org.matrix.msc1767.audio"]: { - duration, - waveform: undefined, - }, - ["org.matrix.msc1767.file"]: { - file: { - file: true, - }, - mimetype: "audio/ogg", - name: "Voice message.ogg", - size, - url: "mxc://example.com/vb", - }, - ["org.matrix.msc1767.text"]: "Voice message", - ["org.matrix.msc3245.voice"]: {}, + mimetype: "audio/ogg", + name: "Voice message.ogg", + size, url: "mxc://example.com/vb", - ["io.element.voice_broadcast_chunk"]: { - sequence, - }, }, - ); + ["org.matrix.msc1767.text"]: "Voice message", + ["org.matrix.msc3245.voice"]: {}, + url: "mxc://example.com/vb", + ["io.element.voice_broadcast_chunk"]: { + sequence, + }, + }); }); }; @@ -202,40 +199,42 @@ describe("VoiceBroadcastRecording", () => { file: uploadedFile, }); - mocked(createVoiceMessageContent).mockImplementation(( - mxc: string, - mimetype: string, - duration: number, - size: number, - file?: IEncryptedFile, - waveform?: number[], - ) => { - return { - body: "Voice message", - msgtype: MsgType.Audio, - url: mxc, - file, - info: { - duration, - mimetype, - size, - }, - ["org.matrix.msc1767.text"]: "Voice message", - ["org.matrix.msc1767.file"]: { + mocked(createVoiceMessageContent).mockImplementation( + ( + mxc: string, + mimetype: string, + duration: number, + size: number, + file?: IEncryptedFile, + waveform?: number[], + ) => { + return { + body: "Voice message", + msgtype: MsgType.Audio, url: mxc, file, - name: "Voice message.ogg", - mimetype, - size, - }, - ["org.matrix.msc1767.audio"]: { - duration, - // https://github.com/matrix-org/matrix-doc/pull/3246 - waveform, - }, - ["org.matrix.msc3245.voice"]: {}, // No content, this is a rendering hint - }; - }); + info: { + duration, + mimetype, + size, + }, + ["org.matrix.msc1767.text"]: "Voice message", + ["org.matrix.msc1767.file"]: { + url: mxc, + file, + name: "Voice message.ogg", + mimetype, + size, + }, + ["org.matrix.msc1767.audio"]: { + duration, + // https://github.com/matrix-org/matrix-doc/pull/3246 + waveform, + }, + ["org.matrix.msc3245.voice"]: {}, // No content, this is a rendering hint + }; + }, + ); }); afterEach(() => { @@ -291,9 +290,12 @@ describe("VoiceBroadcastRecording", () => { describe("and receiving a call action", () => { beforeEach(() => { - dis.dispatch({ - action: "call_state", - }, true); + dis.dispatch( + { + action: "call_state", + }, + true, + ); }); itShouldBeInState(VoiceBroadcastInfoState.Paused); @@ -327,13 +329,10 @@ describe("VoiceBroadcastRecording", () => { describe("and a chunk has been recorded", () => { beforeEach(async () => { - voiceBroadcastRecorder.emit( - VoiceBroadcastRecorderEvent.ChunkRecorded, - { - buffer: new Uint8Array([1, 2, 3]), - length: 23, - }, - ); + voiceBroadcastRecorder.emit(VoiceBroadcastRecorderEvent.ChunkRecorded, { + buffer: new Uint8Array([1, 2, 3]), + length: 23, + }); }); itShouldSendAVoiceMessage([1, 2, 3], 3, 23, 1); @@ -451,21 +450,19 @@ describe("VoiceBroadcastRecording", () => { const timelineSet = { relations: { - getChildEventsForEvent: jest.fn().mockImplementation( - ( - eventId: string, - relationType: RelationType | string, - eventType: EventType | string, - ) => { - if ( - eventId === infoEvent.getId() - && relationType === RelationType.Reference - && eventType === VoiceBroadcastInfoEventType - ) { - return relationsContainer; - } - }, - ), + getChildEventsForEvent: jest + .fn() + .mockImplementation( + (eventId: string, relationType: RelationType | string, eventType: EventType | string) => { + if ( + eventId === infoEvent.getId() && + relationType === RelationType.Reference && + eventType === VoiceBroadcastInfoEventType + ) { + return relationsContainer; + } + }, + ), }, } as unknown as EventTimelineSet; mocked(room.getUnfilteredTimelineSet).mockReturnValue(timelineSet); diff --git a/test/voice-broadcast/stores/VoiceBroadcastPlaybacksStore-test.ts b/test/voice-broadcast/stores/VoiceBroadcastPlaybacksStore-test.ts index d234f37637..612ed97653 100644 --- a/test/voice-broadcast/stores/VoiceBroadcastPlaybacksStore-test.ts +++ b/test/voice-broadcast/stores/VoiceBroadcastPlaybacksStore-test.ts @@ -15,11 +15,7 @@ limitations under the License. */ import { mocked } from "jest-mock"; -import { - MatrixClient, - MatrixEvent, - Room, -} from "matrix-js-sdk/src/matrix"; +import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix"; import { VoiceBroadcastInfoState, @@ -61,18 +57,8 @@ describe("VoiceBroadcastPlaybacksStore", () => { return null; }); - infoEvent1 = mkVoiceBroadcastInfoStateEvent( - roomId, - VoiceBroadcastInfoState.Started, - userId, - deviceId, - ); - infoEvent2 = mkVoiceBroadcastInfoStateEvent( - roomId, - VoiceBroadcastInfoState.Started, - userId, - deviceId, - ); + infoEvent1 = mkVoiceBroadcastInfoStateEvent(roomId, VoiceBroadcastInfoState.Started, userId, deviceId); + infoEvent2 = mkVoiceBroadcastInfoStateEvent(roomId, VoiceBroadcastInfoState.Started, userId, deviceId); playback1 = new VoiceBroadcastPlayback(infoEvent1, client); jest.spyOn(playback1, "off"); playback2 = new VoiceBroadcastPlayback(infoEvent2, client); diff --git a/test/voice-broadcast/utils/VoiceBroadcastChunkEvents-test.ts b/test/voice-broadcast/utils/VoiceBroadcastChunkEvents-test.ts index 26fcbc4258..46132d87a9 100644 --- a/test/voice-broadcast/utils/VoiceBroadcastChunkEvents-test.ts +++ b/test/voice-broadcast/utils/VoiceBroadcastChunkEvents-test.ts @@ -50,11 +50,7 @@ describe("VoiceBroadcastChunkEvents", () => { beforeEach(() => { chunkEvents.addEvent(eventSeq2Time4); chunkEvents.addEvent(eventSeq1Time1); - chunkEvents.addEvents([ - eventSeq4Time1, - eventSeq2Time4Dup, - eventSeq3Time2, - ]); + chunkEvents.addEvents([eventSeq4Time1, eventSeq2Time4Dup, eventSeq3Time2]); }); it("should provide the events sort by sequence", () => { @@ -122,12 +118,7 @@ describe("VoiceBroadcastChunkEvents", () => { beforeEach(() => { chunkEvents.addEvent(eventSeq2Time4); chunkEvents.addEvent(eventSeq1Time1); - chunkEvents.addEvents([ - eventSeq4Time1, - eventSeqUTime3, - eventSeq2Time4Dup, - eventSeq3Time2, - ]); + chunkEvents.addEvents([eventSeq4Time1, eventSeqUTime3, eventSeq2Time4Dup, eventSeq3Time2]); }); it("should provide the events sort by timestamp without duplicates", () => { diff --git a/test/voice-broadcast/utils/findRoomLiveVoiceBroadcastFromUserAndDevice-test.ts b/test/voice-broadcast/utils/findRoomLiveVoiceBroadcastFromUserAndDevice-test.ts index 0fa04963ae..6d0bd2d25d 100644 --- a/test/voice-broadcast/utils/findRoomLiveVoiceBroadcastFromUserAndDevice-test.ts +++ b/test/voice-broadcast/utils/findRoomLiveVoiceBroadcastFromUserAndDevice-test.ts @@ -32,11 +32,9 @@ describe("findRoomLiveVoiceBroadcastFromUserAndDevice", () => { const itShouldReturnNull = () => { it("should return null", () => { - expect(findRoomLiveVoiceBroadcastFromUserAndDevice( - room, - client.getUserId(), - client.getDeviceId(), - )).toBeNull(); + expect( + findRoomLiveVoiceBroadcastFromUserAndDevice(room, client.getUserId(), client.getDeviceId()), + ).toBeNull(); }); }; @@ -117,11 +115,9 @@ describe("findRoomLiveVoiceBroadcastFromUserAndDevice", () => { client.getUserId(), ); - expect(findRoomLiveVoiceBroadcastFromUserAndDevice( - room, - client.getUserId(), - client.getDeviceId(), - )).toBe(event); + expect(findRoomLiveVoiceBroadcastFromUserAndDevice(room, client.getUserId(), client.getDeviceId())).toBe( + event, + ); }); }); }); diff --git a/test/voice-broadcast/utils/hasRoomLiveVoiceBroadcast-test.ts b/test/voice-broadcast/utils/hasRoomLiveVoiceBroadcast-test.ts index 5edee8eda6..7e7a34678d 100644 --- a/test/voice-broadcast/utils/hasRoomLiveVoiceBroadcast-test.ts +++ b/test/voice-broadcast/utils/hasRoomLiveVoiceBroadcast-test.ts @@ -31,16 +31,8 @@ describe("hasRoomLiveVoiceBroadcast", () => { let room: Room; let expectedEvent: MatrixEvent | null = null; - const addVoiceBroadcastInfoEvent = ( - state: VoiceBroadcastInfoState, - sender: string, - ): MatrixEvent => { - const infoEvent = mkVoiceBroadcastInfoStateEvent( - room.roomId, - state, - sender, - "ASD123", - ); + const addVoiceBroadcastInfoEvent = (state: VoiceBroadcastInfoState, sender: string): MatrixEvent => { + const infoEvent = mkVoiceBroadcastInfoStateEvent(room.roomId, state, sender, "ASD123"); room.currentState.setStateEvents([infoEvent]); return infoEvent; }; diff --git a/test/voice-broadcast/utils/setUpVoiceBroadcastPreRecording-test.ts b/test/voice-broadcast/utils/setUpVoiceBroadcastPreRecording-test.ts index 4779813165..11d6e45760 100644 --- a/test/voice-broadcast/utils/setUpVoiceBroadcastPreRecording-test.ts +++ b/test/voice-broadcast/utils/setUpVoiceBroadcastPreRecording-test.ts @@ -45,13 +45,9 @@ describe("setUpVoiceBroadcastPreRecording", () => { const itShouldReturnNull = () => { it("should return null", () => { - expect(setUpVoiceBroadcastPreRecording( - room, - client, - playbacksStore, - recordingsStore, - preRecordingStore, - )).toBeNull(); + expect( + setUpVoiceBroadcastPreRecording(room, client, playbacksStore, recordingsStore, preRecordingStore), + ).toBeNull(); expect(checkVoiceBroadcastPreConditions).toHaveBeenCalledWith(room, client, recordingsStore); }); }; @@ -110,9 +106,7 @@ describe("setUpVoiceBroadcastPreRecording", () => { describe("and there is a room member and listening to another broadcast", () => { beforeEach(() => { playbacksStore.setCurrent(playback); - room.currentState.setStateEvents([ - mkRoomMemberJoinEvent(userId, roomId), - ]); + room.currentState.setStateEvents([mkRoomMemberJoinEvent(userId, roomId)]); }); it("should pause the current playback and create a voice broadcast pre-recording", () => { diff --git a/test/voice-broadcast/utils/shouldDisplayAsVoiceBroadcastRecordingTile-test.ts b/test/voice-broadcast/utils/shouldDisplayAsVoiceBroadcastRecordingTile-test.ts index fc4ec2c04b..6c99c6b7c0 100644 --- a/test/voice-broadcast/utils/shouldDisplayAsVoiceBroadcastRecordingTile-test.ts +++ b/test/voice-broadcast/utils/shouldDisplayAsVoiceBroadcastRecordingTile-test.ts @@ -31,42 +31,12 @@ const testCases = [ VoiceBroadcastInfoState.Started, true, // expected return value ], - [ - "@user1:example.com", - "@user1:example.com", - VoiceBroadcastInfoState.Paused, - true, - ], - [ - "@user1:example.com", - "@user1:example.com", - VoiceBroadcastInfoState.Resumed, - true, - ], - [ - "@user1:example.com", - "@user1:example.com", - VoiceBroadcastInfoState.Stopped, - false, - ], - [ - "@user2:example.com", - "@user1:example.com", - VoiceBroadcastInfoState.Started, - false, - ], - [ - null, - null, - null, - false, - ], - [ - undefined, - undefined, - undefined, - false, - ], + ["@user1:example.com", "@user1:example.com", VoiceBroadcastInfoState.Paused, true], + ["@user1:example.com", "@user1:example.com", VoiceBroadcastInfoState.Resumed, true], + ["@user1:example.com", "@user1:example.com", VoiceBroadcastInfoState.Stopped, false], + ["@user2:example.com", "@user1:example.com", VoiceBroadcastInfoState.Started, false], + [null, null, null, false], + [undefined, undefined, undefined, false], ]; describe("shouldDisplayAsVoiceBroadcastRecordingTile", () => { @@ -94,5 +64,6 @@ describe("shouldDisplayAsVoiceBroadcastRecordingTile", () => { it(`should return ${expected}`, () => { expect(shouldDisplayAsVoiceBroadcastRecordingTile(state, client, event)).toBe(expected); }); - }); + }, + ); }); diff --git a/test/voice-broadcast/utils/shouldDisplayAsVoiceBroadcastTile-test.ts b/test/voice-broadcast/utils/shouldDisplayAsVoiceBroadcastTile-test.ts index 394b8c4c11..4dfffac76b 100644 --- a/test/voice-broadcast/utils/shouldDisplayAsVoiceBroadcastTile-test.ts +++ b/test/voice-broadcast/utils/shouldDisplayAsVoiceBroadcastTile-test.ts @@ -125,25 +125,22 @@ describe("shouldDisplayAsVoiceBroadcastTile", () => { itShouldReturnTrue(); }); - describe.each( - [ - VoiceBroadcastInfoState.Paused, - VoiceBroadcastInfoState.Resumed, - VoiceBroadcastInfoState.Stopped, - ], - )("when a voice broadcast info event in state %s occurs", (state: VoiceBroadcastInfoState) => { - beforeEach(() => { - event = mkEvent({ - event: true, - type: VoiceBroadcastInfoEventType, - room: roomId, - user: senderId, - content: { - state, - }, + describe.each([VoiceBroadcastInfoState.Paused, VoiceBroadcastInfoState.Resumed, VoiceBroadcastInfoState.Stopped])( + "when a voice broadcast info event in state %s occurs", + (state: VoiceBroadcastInfoState) => { + beforeEach(() => { + event = mkEvent({ + event: true, + type: VoiceBroadcastInfoEventType, + room: roomId, + user: senderId, + content: { + state, + }, + }); }); - }); - itShouldReturnFalse(); - }); + itShouldReturnFalse(); + }, + ); }); diff --git a/test/voice-broadcast/utils/startNewVoiceBroadcastRecording-test.ts b/test/voice-broadcast/utils/startNewVoiceBroadcastRecording-test.ts index 5eac6ef803..091a452d22 100644 --- a/test/voice-broadcast/utils/startNewVoiceBroadcastRecording-test.ts +++ b/test/voice-broadcast/utils/startNewVoiceBroadcastRecording-test.ts @@ -59,18 +59,15 @@ describe("startNewVoiceBroadcastRecording", () => { return null; }); - mocked(client.sendStateEvent).mockImplementation(( - sendRoomId: string, - eventType: string, - content: any, - stateKey: string, - ): Promise => { - if (sendRoomId === roomId && eventType === VoiceBroadcastInfoEventType) { - return Promise.resolve({ event_id: infoEvent.getId()! }); - } + mocked(client.sendStateEvent).mockImplementation( + (sendRoomId: string, eventType: string, content: any, stateKey: string): Promise => { + if (sendRoomId === roomId && eventType === VoiceBroadcastInfoEventType) { + return Promise.resolve({ event_id: infoEvent.getId()! }); + } - throw new Error("Unexpected sendStateEvent call"); - }); + throw new Error("Unexpected sendStateEvent call"); + }, + ); infoEvent = mkVoiceBroadcastInfoStateEvent( roomId, @@ -93,10 +90,7 @@ describe("startNewVoiceBroadcastRecording", () => { getCurrent: jest.fn(), } as unknown as VoiceBroadcastRecordingsStore; - mocked(VoiceBroadcastRecording).mockImplementation(( - infoEvent: MatrixEvent, - client: MatrixClient, - ): any => { + mocked(VoiceBroadcastRecording).mockImplementation((infoEvent: MatrixEvent, client: MatrixClient): any => { return { infoEvent, client, @@ -124,19 +118,21 @@ describe("startNewVoiceBroadcastRecording", () => { }); it("should stop listen to the current broadcast and create a new recording", async () => { - mocked(client.sendStateEvent).mockImplementation(async ( - _roomId: string, - _eventType: string, - _content: any, - _stateKey = "", - ): Promise => { - window.setTimeout(() => { - // emit state events after resolving the promise - room.currentState.setStateEvents([otherEvent]); - room.currentState.setStateEvents([infoEvent]); - }, 0); - return { event_id: infoEvent.getId()! }; - }); + mocked(client.sendStateEvent).mockImplementation( + async ( + _roomId: string, + _eventType: string, + _content: any, + _stateKey = "", + ): Promise => { + window.setTimeout(() => { + // emit state events after resolving the promise + room.currentState.setStateEvents([otherEvent]); + room.currentState.setStateEvents([infoEvent]); + }, 0); + return { event_id: infoEvent.getId()! }; + }, + ); const recording = await startNewVoiceBroadcastRecording(room, client, playbacksStore, recordingsStore); expect(recording).not.toBeNull(); @@ -161,9 +157,7 @@ describe("startNewVoiceBroadcastRecording", () => { describe("when there is already a current voice broadcast", () => { beforeEach(async () => { - mocked(recordingsStore.getCurrent).mockReturnValue( - new VoiceBroadcastRecording(infoEvent, client), - ); + mocked(recordingsStore.getCurrent).mockReturnValue(new VoiceBroadcastRecording(infoEvent, client)); result = await startNewVoiceBroadcastRecording(room, client, playbacksStore, recordingsStore); }); @@ -203,12 +197,7 @@ describe("startNewVoiceBroadcastRecording", () => { describe("when there already is a live broadcast of another user", () => { beforeEach(async () => { room.currentState.setStateEvents([ - mkVoiceBroadcastInfoStateEvent( - roomId, - VoiceBroadcastInfoState.Resumed, - otherUserId, - "ASD123", - ), + mkVoiceBroadcastInfoStateEvent(roomId, VoiceBroadcastInfoState.Resumed, otherUserId, "ASD123"), ]); result = await startNewVoiceBroadcastRecording(room, client, playbacksStore, recordingsStore); diff --git a/tsconfig.json b/tsconfig.json index 46ac495c86..50b20450ea 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,31 +1,22 @@ { - "compilerOptions": { - "experimentalDecorators": false, - "emitDecoratorMetadata": false, - "resolveJsonModule": true, - "esModuleInterop": true, - "module": "commonjs", - "moduleResolution": "node", - "target": "es2016", - "noImplicitAny": false, - "noUnusedLocals": true, - "sourceMap": false, - "outDir": "./lib", - "declaration": true, - "jsx": "react", - "lib": [ - "es2020", - "dom", - "dom.iterable" - ], - "alwaysStrict": true, - "strictBindCallApply": true, - "noImplicitThis": true - }, - "include": [ - "./src/**/*.ts", - "./src/**/*.tsx", - "./test/**/*.ts", - "./test/**/*.tsx" - ] + "compilerOptions": { + "experimentalDecorators": false, + "emitDecoratorMetadata": false, + "resolveJsonModule": true, + "esModuleInterop": true, + "module": "commonjs", + "moduleResolution": "node", + "target": "es2016", + "noImplicitAny": false, + "noUnusedLocals": true, + "sourceMap": false, + "outDir": "./lib", + "declaration": true, + "jsx": "react", + "lib": ["es2020", "dom", "dom.iterable"], + "alwaysStrict": true, + "strictBindCallApply": true, + "noImplicitThis": true + }, + "include": ["./src/**/*.ts", "./src/**/*.tsx", "./test/**/*.ts", "./test/**/*.tsx"] }