Conform more of the codebase to strictNullChecks + noImplicitAny (#11179)

This commit is contained in:
Michael Telatynski 2023-07-04 14:49:27 +01:00 committed by GitHub
parent 7c211b0587
commit a294ba2ad4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 104 additions and 88 deletions

View file

@ -236,7 +236,7 @@ export default class AddThreepid {
continueKind: "primary", continueKind: "primary",
}, },
}; };
const { finished } = Modal.createDialog(InteractiveAuthDialog, { const { finished } = Modal.createDialog(InteractiveAuthDialog<{}>, {
title: _t("Add Email Address"), title: _t("Add Email Address"),
matrixClient: this.matrixClient, matrixClient: this.matrixClient,
authData: err.data, authData: err.data,
@ -357,7 +357,7 @@ export default class AddThreepid {
continueKind: "primary", continueKind: "primary",
}, },
}; };
const { finished } = Modal.createDialog(InteractiveAuthDialog, { const { finished } = Modal.createDialog(InteractiveAuthDialog<{}>, {
title: _t("Add Phone Number"), title: _t("Add Phone Number"),
matrixClient: this.matrixClient, matrixClient: this.matrixClient,
authData: err.data, authData: err.data,

View file

@ -327,7 +327,7 @@ function registerAsGuest(hsUrl: string, isUrl?: string, defaultDeviceDisplayName
{ {
userId: creds.user_id, userId: creds.user_id,
deviceId: creds.device_id, deviceId: creds.device_id,
accessToken: creds.access_token, accessToken: creds.access_token!,
homeserverUrl: hsUrl, homeserverUrl: hsUrl,
identityServerUrl: isUrl, identityServerUrl: isUrl,
guest: true, guest: true,
@ -920,10 +920,8 @@ async function clearStorage(opts?: { deleteEverything?: boolean }): Promise<void
// now restore those invites and registration time // now restore those invites and registration time
if (!opts?.deleteEverything) { if (!opts?.deleteEverything) {
pendingInvites.forEach((i) => { pendingInvites.forEach(({ roomId, ...invite }) => {
const roomId = i.roomId; ThreepidInviteStore.instance.storeInvite(roomId, invite);
delete i.roomId; // delete to avoid confusing the store
ThreepidInviteStore.instance.storeInvite(roomId, i);
}); });
if (registrationTime) { if (registrationTime) {

View file

@ -19,7 +19,7 @@ limitations under the License.
import { createClient } from "matrix-js-sdk/src/matrix"; import { createClient } from "matrix-js-sdk/src/matrix";
import { MatrixClient } from "matrix-js-sdk/src/client"; import { MatrixClient } from "matrix-js-sdk/src/client";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { DELEGATED_OIDC_COMPATIBILITY, ILoginFlow, ILoginParams, LoginFlow } from "matrix-js-sdk/src/@types/auth"; import { DELEGATED_OIDC_COMPATIBILITY, ILoginFlow, LoginFlow, LoginRequest } from "matrix-js-sdk/src/@types/auth";
import { IMatrixClientCreds } from "./MatrixClientPeg"; import { IMatrixClientCreds } from "./MatrixClientPeg";
import SecurityCustomisations from "./customisations/Security"; import SecurityCustomisations from "./customisations/Security";
@ -238,7 +238,7 @@ export async function sendLoginRequest(
hsUrl: string, hsUrl: string,
isUrl: string | undefined, isUrl: string | undefined,
loginType: string, loginType: string,
loginParams: ILoginParams, loginParams: Omit<LoginRequest, "type">,
): Promise<IMatrixClientCreds> { ): Promise<IMatrixClientCreds> {
const client = createClient({ const client = createClient({
baseUrl: hsUrl, baseUrl: hsUrl,

View file

@ -49,7 +49,7 @@ export interface IMatrixClientCreds {
identityServerUrl?: string; identityServerUrl?: string;
userId: string; userId: string;
deviceId?: string; deviceId?: string;
accessToken?: string; accessToken: string;
guest?: boolean; guest?: boolean;
pickleKey?: string; pickleKey?: string;
freshLogin?: boolean; freshLogin?: boolean;
@ -79,8 +79,6 @@ export interface IMatrixClientPeg {
assign(): Promise<any>; assign(): Promise<any>;
start(): Promise<any>; start(): Promise<any>;
getCredentials(): IMatrixClientCreds;
/** /**
* If we've registered a user ID we set this to the ID of the * If we've registered a user ID we set this to the ID of the
* user we've just registered. If they then go & log in, we * user we've just registered. If they then go & log in, we
@ -138,10 +136,6 @@ class MatrixClientPegClass implements IMatrixClientPeg {
private matrixClient: MatrixClient | null = null; private matrixClient: MatrixClient | null = null;
private justRegisteredUserId: string | null = null; private justRegisteredUserId: string | null = null;
// the credentials used to init the current client object.
// used if we tear it down & recreate it with a different store
private currentClientCreds: IMatrixClientCreds | null = null;
public get(): MatrixClient | null { public get(): MatrixClient | null {
return this.matrixClient; return this.matrixClient;
} }
@ -195,7 +189,6 @@ class MatrixClientPegClass implements IMatrixClientPeg {
} }
public replaceUsingCreds(creds: IMatrixClientCreds): void { public replaceUsingCreds(creds: IMatrixClientCreds): void {
this.currentClientCreds = creds;
this.createClient(creds); this.createClient(creds);
} }
@ -335,29 +328,6 @@ class MatrixClientPegClass implements IMatrixClientPeg {
logger.log(`MatrixClientPeg: MatrixClient started`); logger.log(`MatrixClientPeg: MatrixClient started`);
} }
public getCredentials(): IMatrixClientCreds {
if (!this.matrixClient) {
throw new Error("createClient must be called first");
}
let copiedCredentials: IMatrixClientCreds | null = this.currentClientCreds;
if (this.currentClientCreds?.userId !== this.matrixClient?.credentials?.userId) {
// cached credentials belong to a different user - don't use them
copiedCredentials = null;
}
return {
// Copy the cached credentials before overriding what we can.
...(copiedCredentials ?? {}),
homeserverUrl: this.matrixClient.baseUrl,
identityServerUrl: this.matrixClient.idBaseUrl,
userId: this.matrixClient.getSafeUserId(),
deviceId: this.matrixClient.getDeviceId() ?? undefined,
accessToken: this.matrixClient.getAccessToken() ?? undefined,
guest: this.matrixClient.isGuest(),
};
}
public getHomeserverName(): string { public getHomeserverName(): string {
const matches = /^@[^:]+:(.+)$/.exec(this.safeGet().getSafeUserId()); const matches = /^@[^:]+:(.+)$/.exec(this.safeGet().getSafeUserId());
if (matches === null || matches.length < 1) { if (matches === null || matches.length < 1) {

View file

@ -25,20 +25,19 @@ import {
} from "matrix-js-sdk/src/interactive-auth"; } from "matrix-js-sdk/src/interactive-auth";
import { MatrixClient } from "matrix-js-sdk/src/client"; import { MatrixClient } from "matrix-js-sdk/src/client";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { UIAResponse } from "matrix-js-sdk/src/@types/uia";
import getEntryComponentForLoginType, { IStageComponent } from "../views/auth/InteractiveAuthEntryComponents"; import getEntryComponentForLoginType, { IStageComponent } from "../views/auth/InteractiveAuthEntryComponents";
import Spinner from "../views/elements/Spinner"; import Spinner from "../views/elements/Spinner";
export const ERROR_USER_CANCELLED = new Error("User cancelled auth session"); export const ERROR_USER_CANCELLED = new Error("User cancelled auth session");
type InteractiveAuthCallbackSuccess = ( type InteractiveAuthCallbackSuccess<T> = (
success: true, success: true,
response?: IAuthData, response: T,
extra?: { emailSid?: string; clientSecret?: string }, extra?: { emailSid?: string; clientSecret?: string },
) => void; ) => void;
type InteractiveAuthCallbackFailure = (success: false, response: IAuthData | Error) => void; type InteractiveAuthCallbackFailure = (success: false, response: IAuthData | Error) => void;
export type InteractiveAuthCallback = InteractiveAuthCallbackSuccess & InteractiveAuthCallbackFailure; export type InteractiveAuthCallback<T> = InteractiveAuthCallbackSuccess<T> & InteractiveAuthCallbackFailure;
export interface InteractiveAuthProps<T> { export interface InteractiveAuthProps<T> {
// matrix client to use for UI auth requests // matrix client to use for UI auth requests
@ -62,7 +61,7 @@ export interface InteractiveAuthProps<T> {
continueText?: string; continueText?: string;
continueKind?: string; continueKind?: string;
// callback // callback
makeRequest(auth: IAuthDict | null): Promise<UIAResponse<T>>; makeRequest(auth: IAuthDict | null): Promise<T>;
// callback called when the auth process has finished, // callback called when the auth process has finished,
// successfully or unsuccessfully. // successfully or unsuccessfully.
// @param {boolean} status True if the operation requiring // @param {boolean} status True if the operation requiring
@ -75,7 +74,7 @@ export interface InteractiveAuthProps<T> {
// the auth session. // the auth session.
// * clientSecret {string} The client secret used in auth // * clientSecret {string} The client secret used in auth
// sessions with the ID server. // sessions with the ID server.
onAuthFinished: InteractiveAuthCallback; onAuthFinished: InteractiveAuthCallback<T>;
// As js-sdk interactive-auth // As js-sdk interactive-auth
requestEmailToken?(email: string, secret: string, attempt: number, session: string): Promise<{ sid: string }>; requestEmailToken?(email: string, secret: string, attempt: number, session: string): Promise<{ sid: string }>;
// Called when the stage changes, or the stage's phase changes. First // Called when the stage changes, or the stage's phase changes. First
@ -94,7 +93,7 @@ interface IState {
} }
export default class InteractiveAuthComponent<T> extends React.Component<InteractiveAuthProps<T>, IState> { export default class InteractiveAuthComponent<T> extends React.Component<InteractiveAuthProps<T>, IState> {
private readonly authLogic: InteractiveAuth; private readonly authLogic: InteractiveAuth<T>;
private readonly intervalId: number | null = null; private readonly intervalId: number | null = null;
private readonly stageComponent = createRef<IStageComponent>(); private readonly stageComponent = createRef<IStageComponent>();
@ -108,7 +107,7 @@ export default class InteractiveAuthComponent<T> extends React.Component<Interac
submitButtonEnabled: false, submitButtonEnabled: false,
}; };
this.authLogic = new InteractiveAuth({ this.authLogic = new InteractiveAuth<T>({
authData: this.props.authData, authData: this.props.authData,
doRequest: this.requestCallback, doRequest: this.requestCallback,
busyChanged: this.onBusyChanged, busyChanged: this.onBusyChanged,
@ -211,7 +210,7 @@ export default class InteractiveAuthComponent<T> extends React.Component<Interac
); );
}; };
private requestCallback = (auth: IAuthDict | null, background: boolean): Promise<UIAResponse<T>> => { private requestCallback = (auth: IAuthDict | null, background: boolean): Promise<T> => {
// This wrapper just exists because the js-sdk passes a second // This wrapper just exists because the js-sdk passes a second
// 'busy' param for backwards compat. This throws the tests off // 'busy' param for backwards compat. This throws the tests off
// so discard it here. // so discard it here.

View file

@ -14,12 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { AuthType, createClient, IAuthDict, IAuthData, IInputs, MatrixError } from "matrix-js-sdk/src/matrix"; import { AuthType, createClient, IAuthData, IAuthDict, IInputs, MatrixError } from "matrix-js-sdk/src/matrix";
import React, { Fragment, ReactNode } from "react"; import React, { Fragment, ReactNode } from "react";
import { IRegisterRequestParams, IRequestTokenResponse, MatrixClient } from "matrix-js-sdk/src/client"; import { IRegisterRequestParams, IRequestTokenResponse, MatrixClient } from "matrix-js-sdk/src/client";
import classNames from "classnames"; import classNames from "classnames";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { ISSOFlow, SSOAction } from "matrix-js-sdk/src/@types/auth"; import { ISSOFlow, SSOAction } from "matrix-js-sdk/src/@types/auth";
import { RegisterResponse } from "matrix-js-sdk/src/@types/registration";
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
import { adminContactStrings, messageForResourceLimitError, resourceLimitStrings } from "../../../utils/ErrorUtils"; import { adminContactStrings, messageForResourceLimitError, resourceLimitStrings } from "../../../utils/ErrorUtils";
@ -305,7 +306,7 @@ export default class Registration extends React.Component<IProps, IState> {
); );
}; };
private onUIAuthFinished: InteractiveAuthCallback = async (success, response): Promise<void> => { private onUIAuthFinished: InteractiveAuthCallback<RegisterResponse> = async (success, response): Promise<void> => {
if (!this.state.matrixClient) throw new Error("Matrix client has not yet been loaded"); if (!this.state.matrixClient) throw new Error("Matrix client has not yet been loaded");
debuglog("Registration: ui authentication finished: ", { success, response }); debuglog("Registration: ui authentication finished: ", { success, response });
@ -329,8 +330,8 @@ export default class Registration extends React.Component<IProps, IState> {
<p>{errorDetail}</p> <p>{errorDetail}</p>
</div> </div>
); );
} else if ((response as IAuthData).required_stages?.includes(AuthType.Msisdn)) { } else if ((response as IAuthData).flows?.some((flow) => flow.stages.includes(AuthType.Msisdn))) {
const flows = (response as IAuthData).available_flows ?? []; const flows = (response as IAuthData).flows ?? [];
const msisdnAvailable = flows.some((flow) => flow.stages.includes(AuthType.Msisdn)); const msisdnAvailable = flows.some((flow) => flow.stages.includes(AuthType.Msisdn));
if (!msisdnAvailable) { if (!msisdnAvailable) {
errorText = _t("This server does not support authentication with a phone number."); errorText = _t("This server does not support authentication with a phone number.");
@ -349,15 +350,15 @@ export default class Registration extends React.Component<IProps, IState> {
return; return;
} }
const userId = (response as IAuthData).user_id; const userId = (response as RegisterResponse).user_id;
const accessToken = (response as IAuthData).access_token; const accessToken = (response as RegisterResponse).access_token;
if (!userId || !accessToken) throw new Error("Registration failed"); if (!userId || !accessToken) throw new Error("Registration failed");
MatrixClientPeg.setJustRegisteredUserId(userId); MatrixClientPeg.setJustRegisteredUserId(userId);
const newState: Partial<IState> = { const newState: Partial<IState> = {
doingUIAuth: false, doingUIAuth: false,
registeredUsername: (response as IAuthData).user_id, registeredUsername: userId,
differentLoggedInUserId: undefined, differentLoggedInUserId: undefined,
completedNoSignin: false, completedNoSignin: false,
// we're still busy until we get unmounted: don't show the registration form again // we're still busy until we get unmounted: don't show the registration form again
@ -370,10 +371,8 @@ export default class Registration extends React.Component<IProps, IState> {
// starting the registration process. This isn't perfect since it's possible // starting the registration process. This isn't perfect since it's possible
// the user had a separate guest session they didn't actually mean to replace. // the user had a separate guest session they didn't actually mean to replace.
const [sessionOwner, sessionIsGuest] = await Lifecycle.getStoredSessionOwner(); const [sessionOwner, sessionIsGuest] = await Lifecycle.getStoredSessionOwner();
if (sessionOwner && !sessionIsGuest && sessionOwner !== (response as IAuthData).user_id) { if (sessionOwner && !sessionIsGuest && sessionOwner !== userId) {
logger.log( logger.log(`Found a session for ${sessionOwner} but ${userId} has just registered.`);
`Found a session for ${sessionOwner} but ${(response as IAuthData).user_id} has just registered.`,
);
newState.differentLoggedInUserId = sessionOwner; newState.differentLoggedInUserId = sessionOwner;
} }
@ -390,7 +389,7 @@ export default class Registration extends React.Component<IProps, IState> {
// as the client that started registration may be gone by the time we've verified the email, and only the client // as the client that started registration may be gone by the time we've verified the email, and only the client
// that verified the email is guaranteed to exist, we'll always do the login in that client. // that verified the email is guaranteed to exist, we'll always do the login in that client.
const hasEmail = Boolean(this.state.formVals.email); const hasEmail = Boolean(this.state.formVals.email);
const hasAccessToken = Boolean((response as IAuthData).access_token); const hasAccessToken = Boolean(accessToken);
debuglog("Registration: ui auth finished:", { hasEmail, hasAccessToken }); debuglog("Registration: ui auth finished:", { hasEmail, hasAccessToken });
// dont log in if we found a session for a different user // dont log in if we found a session for a different user
if (!hasEmail && hasAccessToken && !newState.differentLoggedInUserId) { if (!hasEmail && hasAccessToken && !newState.differentLoggedInUserId) {
@ -399,7 +398,7 @@ export default class Registration extends React.Component<IProps, IState> {
await this.props.onLoggedIn( await this.props.onLoggedIn(
{ {
userId, userId,
deviceId: (response as IAuthData).device_id, deviceId: (response as RegisterResponse).device_id!,
homeserverUrl: this.state.matrixClient.getHomeserverUrl(), homeserverUrl: this.state.matrixClient.getHomeserverUrl(),
identityServerUrl: this.state.matrixClient.getIdentityServerUrl(), identityServerUrl: this.state.matrixClient.getIdentityServerUrl(),
accessToken, accessToken,
@ -461,7 +460,7 @@ export default class Registration extends React.Component<IProps, IState> {
}); });
}; };
private makeRegisterRequest = (auth: IAuthDict | null): Promise<IAuthData> => { private makeRegisterRequest = (auth: IAuthDict | null): Promise<RegisterResponse> => {
if (!this.state.matrixClient) throw new Error("Matrix client has not yet been loaded"); if (!this.state.matrixClient) throw new Error("Matrix client has not yet been loaded");
const registerParams: IRegisterRequestParams = { const registerParams: IRegisterRequestParams = {

View file

@ -18,6 +18,7 @@ limitations under the License.
import React from "react"; import React from "react";
import { AuthType, IAuthData } from "matrix-js-sdk/src/interactive-auth"; import { AuthType, IAuthData } from "matrix-js-sdk/src/interactive-auth";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { MatrixClientPeg } from "../../../MatrixClientPeg";
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
@ -109,7 +110,10 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
this.setState({ bodyText, continueText, continueKind }); this.setState({ bodyText, continueText, continueKind });
}; };
private onUIAuthFinished: InteractiveAuthCallback = (success, result) => { private onUIAuthFinished: InteractiveAuthCallback<Awaited<ReturnType<MatrixClient["deactivateAccount"]>>> = (
success,
result,
) => {
if (success) return; // great! makeRequest() will be called too. if (success) return; // great! makeRequest() will be called too.
if (result === ERROR_USER_CANCELLED) { if (result === ERROR_USER_CANCELLED) {

View file

@ -18,7 +18,8 @@ limitations under the License.
import React from "react"; import React from "react";
import { MatrixClient } from "matrix-js-sdk/src/client"; import { MatrixClient } from "matrix-js-sdk/src/client";
import { AuthType, IAuthData } from "matrix-js-sdk/src/interactive-auth"; import { AuthType } from "matrix-js-sdk/src/interactive-auth";
import { UIAResponse } from "matrix-js-sdk/src/@types/uia";
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
import AccessibleButton from "../elements/AccessibleButton"; import AccessibleButton from "../elements/AccessibleButton";
@ -70,7 +71,7 @@ export interface InteractiveAuthDialogProps<T = unknown>
// Default is defined in _getDefaultDialogAesthetics() // Default is defined in _getDefaultDialogAesthetics()
aestheticsForStagePhases?: DialogAesthetics; aestheticsForStagePhases?: DialogAesthetics;
onFinished(success?: boolean, result?: IAuthData | Error | null): void; onFinished(success?: boolean, result?: UIAResponse<T> | Error | null): void;
} }
interface IState { interface IState {
@ -116,7 +117,7 @@ export default class InteractiveAuthDialog<T> extends React.Component<Interactiv
}; };
} }
private onAuthFinished: InteractiveAuthCallback = (success, result): void => { private onAuthFinished: InteractiveAuthCallback<T> = (success, result): void => {
if (success) { if (success) {
this.props.onFinished(true, result); this.props.onFinished(true, result);
} else { } else {

View file

@ -760,7 +760,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
const lookup = await MatrixClientPeg.safeGet().lookupThreePid("email", term, token); const lookup = await MatrixClientPeg.safeGet().lookupThreePid("email", term, token);
if (term !== this.state.filterText) return; // abandon hope if (term !== this.state.filterText) return; // abandon hope
if (!lookup || !lookup.mxid) { if (!lookup || !("mxid" in lookup)) {
// We weren't able to find anyone - we're already suggesting the plain email // We weren't able to find anyone - we're already suggesting the plain email
// as an alternative, so do nothing. // as an alternative, so do nothing.
return; return;

View file

@ -25,7 +25,7 @@ import { RoomPreviewOpts, RoomViewLifecycle } from "@matrix-org/react-sdk-module
import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { MatrixClientPeg } from "../../../MatrixClientPeg";
import dis from "../../../dispatcher/dispatcher"; import dis from "../../../dispatcher/dispatcher";
import { _t } from "../../../languageHandler"; import { _t, UserFriendlyError } from "../../../languageHandler";
import SdkConfig from "../../../SdkConfig"; import SdkConfig from "../../../SdkConfig";
import IdentityAuthClient from "../../../IdentityAuthClient"; import IdentityAuthClient from "../../../IdentityAuthClient";
import InviteReason from "../elements/InviteReason"; import InviteReason from "../elements/InviteReason";
@ -153,6 +153,9 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
this.props.invitedEmail, this.props.invitedEmail,
identityAccessToken!, identityAccessToken!,
); );
if (!("mxid" in result)) {
throw new UserFriendlyError("Unable to find user by email");
}
this.setState({ invitedEmailMxid: result.mxid }); this.setState({ invitedEmailMxid: result.mxid });
} catch (err) { } catch (err) {
this.setState({ threePidFetchError: err as MatrixError }); this.setState({ threePidFetchError: err as MatrixError });

View file

@ -32,7 +32,7 @@ const makeDeleteRequest =
export const deleteDevicesWithInteractiveAuth = async ( export const deleteDevicesWithInteractiveAuth = async (
matrixClient: MatrixClient, matrixClient: MatrixClient,
deviceIds: string[], deviceIds: string[],
onFinished: InteractiveAuthCallback, onFinished: InteractiveAuthCallback<void>,
): Promise<void> => { ): Promise<void> => {
if (!deviceIds.length) { if (!deviceIds.length) {
return; return;

View file

@ -2076,6 +2076,7 @@
"Currently removing messages in %(count)s rooms|one": "Currently removing messages in %(count)s room", "Currently removing messages in %(count)s rooms|one": "Currently removing messages in %(count)s room",
"%(spaceName)s menu": "%(spaceName)s menu", "%(spaceName)s menu": "%(spaceName)s menu",
"Home options": "Home options", "Home options": "Home options",
"Unable to find user by email": "Unable to find user by email",
"Joining space…": "Joining space…", "Joining space…": "Joining space…",
"Joining room…": "Joining room…", "Joining room…": "Joining room…",
"Joining…": "Joining…", "Joining…": "Joining…",

View file

@ -418,7 +418,11 @@ describe("<MatrixChat />", () => {
// this is used to create a temporary client during login // this is used to create a temporary client during login
jest.spyOn(MatrixJs, "createClient").mockReturnValue(loginClient); jest.spyOn(MatrixJs, "createClient").mockReturnValue(loginClient);
loginClient.login.mockClear().mockResolvedValue({}); loginClient.login.mockClear().mockResolvedValue({
access_token: "TOKEN",
device_id: "IMADEVICE",
user_id: userId,
});
loginClient.loginFlows.mockClear().mockResolvedValue({ flows: [{ type: "m.login.password" }] }); loginClient.loginFlows.mockClear().mockResolvedValue({ flows: [{ type: "m.login.password" }] });
loginClient.getProfileInfo.mockResolvedValue({ loginClient.getProfileInfo.mockResolvedValue({

View file

@ -54,7 +54,11 @@ describe("Login", function () {
disable_custom_urls: true, disable_custom_urls: true,
oidc_static_client_ids: oidcStaticClientsConfig, oidc_static_client_ids: oidcStaticClientsConfig,
}); });
mockClient.login.mockClear().mockResolvedValue({}); mockClient.login.mockClear().mockResolvedValue({
access_token: "TOKEN",
device_id: "IAMADEVICE",
user_id: "@user:server",
});
mockClient.loginFlows.mockClear().mockResolvedValue({ flows: [{ type: "m.login.password" }] }); mockClient.loginFlows.mockClear().mockResolvedValue({ flows: [{ type: "m.login.password" }] });
mocked(createClient).mockImplementation((opts) => { mocked(createClient).mockImplementation((opts) => {
mockClient.idBaseUrl = opts.idBaseUrl; mockClient.idBaseUrl = opts.idBaseUrl;

View file

@ -19,6 +19,7 @@ import React from "react";
import { fireEvent, render, screen, act } from "@testing-library/react"; import { fireEvent, render, screen, act } from "@testing-library/react";
import userEvent from "@testing-library/user-event"; import userEvent from "@testing-library/user-event";
import { mocked } from "jest-mock"; import { mocked } from "jest-mock";
import { MatrixError } from "matrix-js-sdk/src/matrix";
import InteractiveAuthDialog from "../../../../src/components/views/dialogs/InteractiveAuthDialog"; import InteractiveAuthDialog from "../../../../src/components/views/dialogs/InteractiveAuthDialog";
import { clearAllModals, flushPromises, getMockClientWithEventEmitter, unmockClientPeg } from "../../../test-utils"; import { clearAllModals, flushPromises, getMockClientWithEventEmitter, unmockClientPeg } from "../../../test-utils";
@ -130,7 +131,7 @@ describe("InteractiveAuthDialog", function () {
const successfulResult = { test: 1 }; const successfulResult = { test: 1 };
const makeRequest = jest const makeRequest = jest
.fn() .fn()
.mockRejectedValueOnce({ httpStatus: 401, data: { flows: [{ stages: ["m.login.sso"] }] } }) .mockRejectedValueOnce(new MatrixError({ data: { flows: [{ stages: ["m.login.sso"] }] } }, 401))
.mockResolvedValue(successfulResult); .mockResolvedValue(successfulResult);
mockClient.credentials = { userId: "@user:id" }; mockClient.credentials = { userId: "@user:id" };

View file

@ -332,13 +332,15 @@ describe("<RoomPreviewBar />", () => {
{ medium: "not-email", address: "address 2" }, { medium: "not-email", address: "address 2" },
]; ];
const testJoinButton = (props: ComponentProps<typeof RoomPreviewBar>) => async () => { const testJoinButton =
(props: ComponentProps<typeof RoomPreviewBar>, expectSecondaryButton = false) =>
async () => {
const onJoinClick = jest.fn(); const onJoinClick = jest.fn();
const onRejectClick = jest.fn(); const onRejectClick = jest.fn();
const component = getComponent({ ...props, onJoinClick, onRejectClick }); const component = getComponent({ ...props, onJoinClick, onRejectClick });
await new Promise(setImmediate); await new Promise(setImmediate);
expect(getPrimaryActionButton(component)).toBeTruthy(); expect(getPrimaryActionButton(component)).toBeTruthy();
expect(getSecondaryActionButton(component)).toBeFalsy(); if (expectSecondaryButton) expect(getSecondaryActionButton(component)).toBeFalsy();
fireEvent.click(getPrimaryActionButton(component)!); fireEvent.click(getPrimaryActionButton(component)!);
expect(onJoinClick).toHaveBeenCalled(); expect(onJoinClick).toHaveBeenCalled();
}; };
@ -399,7 +401,7 @@ describe("<RoomPreviewBar />", () => {
}); });
it("renders email mismatch message when invite email mxid doesnt match", async () => { it("renders email mismatch message when invite email mxid doesnt match", async () => {
MatrixClientPeg.safeGet().lookupThreePid = jest.fn().mockReturnValue("not userid"); MatrixClientPeg.safeGet().lookupThreePid = jest.fn().mockReturnValue({ mxid: "not userid" });
const component = getComponent({ inviterName, invitedEmail }); const component = getComponent({ inviterName, invitedEmail });
await new Promise(setImmediate); await new Promise(setImmediate);
@ -413,12 +415,12 @@ describe("<RoomPreviewBar />", () => {
}); });
it("renders invite message when invite email mxid match", async () => { it("renders invite message when invite email mxid match", async () => {
MatrixClientPeg.safeGet().lookupThreePid = jest.fn().mockReturnValue(userId); MatrixClientPeg.safeGet().lookupThreePid = jest.fn().mockReturnValue({ mxid: userId });
const component = getComponent({ inviterName, invitedEmail }); const component = getComponent({ inviterName, invitedEmail });
await new Promise(setImmediate); await new Promise(setImmediate);
expect(getMessage(component)).toMatchSnapshot(); expect(getMessage(component)).toMatchSnapshot();
await testJoinButton({ inviterName, invitedEmail })(); await testJoinButton({ inviterName, invitedEmail }, false)();
}); });
}); });
}); });

View file

@ -116,10 +116,40 @@ exports[`<RoomPreviewBar /> with an invite with an invited email when client has
class="mx_RoomPreviewBar_message" class="mx_RoomPreviewBar_message"
> >
<h3> <h3>
This invite to RoomPreviewBar-test-room was sent to test@test.com Do you want to join RoomPreviewBar-test-room?
</h3> </h3>
<p> <p>
Share this email in Settings to receive invites directly in Element. <span
class="mx_BaseAvatar"
role="presentation"
>
<span
aria-hidden="true"
class="mx_BaseAvatar_initial"
style="font-size: 23.400000000000002px; width: 36px; line-height: 36px;"
>
R
</span>
<img
alt=""
aria-hidden="true"
class="mx_BaseAvatar_image"
data-testid="avatar-img"
loading="lazy"
src="data:image/png;base64,00"
style="width: 36px; height: 36px;"
/>
</span>
</p>
<p>
<span>
<span
class="mx_RoomPreviewBar_inviter"
>
@inviter:test.com
</span>
invited you
</span>
</p> </p>
</div> </div>
`; `;