Migrate to React 18 createRoot API (#28256)
* Migrate to React 18 createRoot API Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Discard changes to src/components/views/settings/devices/DeviceDetails.tsx * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Attempt to stabilise test Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * legacyRoot? Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Improve coverage Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update snapshots Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Improve coverage Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
48fd330dd9
commit
ca33d9165a
44 changed files with 719 additions and 731 deletions
|
@ -75,6 +75,7 @@ interface State {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ForgotPassword extends React.Component<Props, State> {
|
export default class ForgotPassword extends React.Component<Props, State> {
|
||||||
|
private unmounted = false;
|
||||||
private reset: PasswordReset;
|
private reset: PasswordReset;
|
||||||
private fieldPassword: Field | null = null;
|
private fieldPassword: Field | null = null;
|
||||||
private fieldPasswordConfirm: Field | null = null;
|
private fieldPasswordConfirm: Field | null = null;
|
||||||
|
@ -108,14 +109,20 @@ export default class ForgotPassword extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public componentWillUnmount(): void {
|
||||||
|
this.unmounted = true;
|
||||||
|
}
|
||||||
|
|
||||||
private async checkServerLiveliness(serverConfig: ValidatedServerConfig): Promise<void> {
|
private async checkServerLiveliness(serverConfig: ValidatedServerConfig): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(serverConfig.hsUrl, serverConfig.isUrl);
|
await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(serverConfig.hsUrl, serverConfig.isUrl);
|
||||||
|
if (this.unmounted) return;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
serverIsAlive: true,
|
serverIsAlive: true,
|
||||||
});
|
});
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
|
if (this.unmounted) return;
|
||||||
const { serverIsAlive, serverDeadError } = AutoDiscoveryUtils.authComponentStateForError(
|
const { serverIsAlive, serverDeadError } = AutoDiscoveryUtils.authComponentStateForError(
|
||||||
e,
|
e,
|
||||||
"forgot_password",
|
"forgot_password",
|
||||||
|
|
|
@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as ReactDOM from "react-dom";
|
import { createRoot } from "react-dom/client";
|
||||||
import React, { StrictMode } from "react";
|
import React, { StrictMode } from "react";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
|
||||||
|
@ -93,7 +93,9 @@ export async function loadApp(fragParams: {}): Promise<void> {
|
||||||
function setWindowMatrixChat(matrixChat: MatrixChat): void {
|
function setWindowMatrixChat(matrixChat: MatrixChat): void {
|
||||||
window.matrixChat = matrixChat;
|
window.matrixChat = matrixChat;
|
||||||
}
|
}
|
||||||
ReactDOM.render(await module.loadApp(fragParams, setWindowMatrixChat), document.getElementById("matrixchat"));
|
const app = await module.loadApp(fragParams, setWindowMatrixChat);
|
||||||
|
const root = createRoot(document.getElementById("matrixchat")!);
|
||||||
|
root.render(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function showError(title: string, messages?: string[]): Promise<void> {
|
export async function showError(title: string, messages?: string[]): Promise<void> {
|
||||||
|
@ -101,11 +103,11 @@ export async function showError(title: string, messages?: string[]): Promise<voi
|
||||||
/* webpackChunkName: "error-view" */
|
/* webpackChunkName: "error-view" */
|
||||||
"../async-components/structures/ErrorView"
|
"../async-components/structures/ErrorView"
|
||||||
);
|
);
|
||||||
ReactDOM.render(
|
const root = createRoot(document.getElementById("matrixchat")!);
|
||||||
|
root.render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<ErrorView title={title} messages={messages} />
|
<ErrorView title={title} messages={messages} />
|
||||||
</StrictMode>,
|
</StrictMode>,
|
||||||
document.getElementById("matrixchat"),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,11 +116,11 @@ export async function showIncompatibleBrowser(onAccept: () => void): Promise<voi
|
||||||
/* webpackChunkName: "error-view" */
|
/* webpackChunkName: "error-view" */
|
||||||
"../async-components/structures/ErrorView"
|
"../async-components/structures/ErrorView"
|
||||||
);
|
);
|
||||||
ReactDOM.render(
|
const root = createRoot(document.getElementById("matrixchat")!);
|
||||||
|
root.render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<UnsupportedBrowserView onAccept={onAccept} />
|
<UnsupportedBrowserView onAccept={onAccept} />
|
||||||
</StrictMode>,
|
</StrictMode>,
|
||||||
document.getElementById("matrixchat"),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ const wrapWithTooltipProvider = (Wrapper: RenderOptions["wrapper"]) => {
|
||||||
|
|
||||||
const customRender = (ui: ReactElement, options: RenderOptions = {}) => {
|
const customRender = (ui: ReactElement, options: RenderOptions = {}) => {
|
||||||
return render(ui, {
|
return render(ui, {
|
||||||
legacyRoot: true,
|
|
||||||
...options,
|
...options,
|
||||||
wrapper: wrapWithTooltipProvider(options?.wrapper) as RenderOptions["wrapper"],
|
wrapper: wrapWithTooltipProvider(options?.wrapper) as RenderOptions["wrapper"],
|
||||||
}) as ReturnType<typeof render>;
|
}) as ReturnType<typeof render>;
|
||||||
|
|
|
@ -197,7 +197,7 @@ export const clearAllModals = async (): Promise<void> => {
|
||||||
// Prevent modals from leaking and polluting other tests
|
// Prevent modals from leaking and polluting other tests
|
||||||
let keepClosingModals = true;
|
let keepClosingModals = true;
|
||||||
while (keepClosingModals) {
|
while (keepClosingModals) {
|
||||||
keepClosingModals = Modal.closeCurrentModal();
|
keepClosingModals = await act(() => Modal.closeCurrentModal());
|
||||||
|
|
||||||
// Then wait for the screen to update (probably React rerender and async/await).
|
// Then wait for the screen to update (probably React rerender and async/await).
|
||||||
// Important for tests using Jest fake timers to not get into an infinite loop
|
// Important for tests using Jest fake timers to not get into an infinite loop
|
||||||
|
|
|
@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { HTMLAttributes } from "react";
|
import React, { HTMLAttributes } from "react";
|
||||||
import { render } from "jest-matrix-react";
|
import { act, render } from "jest-matrix-react";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -79,15 +79,15 @@ describe("RovingTabIndex", () => {
|
||||||
checkTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
|
checkTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
|
||||||
|
|
||||||
// focus on 2nd button and test it is the only active one
|
// focus on 2nd button and test it is the only active one
|
||||||
container.querySelectorAll("button")[2].focus();
|
act(() => container.querySelectorAll("button")[2].focus());
|
||||||
checkTabIndexes(container.querySelectorAll("button"), [-1, -1, 0]);
|
checkTabIndexes(container.querySelectorAll("button"), [-1, -1, 0]);
|
||||||
|
|
||||||
// focus on 1st button and test it is the only active one
|
// focus on 1st button and test it is the only active one
|
||||||
container.querySelectorAll("button")[1].focus();
|
act(() => container.querySelectorAll("button")[1].focus());
|
||||||
checkTabIndexes(container.querySelectorAll("button"), [-1, 0, -1]);
|
checkTabIndexes(container.querySelectorAll("button"), [-1, 0, -1]);
|
||||||
|
|
||||||
// check that the active button does not change even on an explicit blur event
|
// check that the active button does not change even on an explicit blur event
|
||||||
container.querySelectorAll("button")[1].blur();
|
act(() => container.querySelectorAll("button")[1].blur());
|
||||||
checkTabIndexes(container.querySelectorAll("button"), [-1, 0, -1]);
|
checkTabIndexes(container.querySelectorAll("button"), [-1, 0, -1]);
|
||||||
|
|
||||||
// update the children, it should remain on the same button
|
// update the children, it should remain on the same button
|
||||||
|
@ -162,7 +162,7 @@ describe("RovingTabIndex", () => {
|
||||||
checkTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
|
checkTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
|
||||||
|
|
||||||
// focus on 2nd button and test it is the only active one
|
// focus on 2nd button and test it is the only active one
|
||||||
container.querySelectorAll("button")[2].focus();
|
act(() => container.querySelectorAll("button")[2].focus());
|
||||||
checkTabIndexes(container.querySelectorAll("button"), [-1, -1, 0]);
|
checkTabIndexes(container.querySelectorAll("button"), [-1, -1, 0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -390,7 +390,7 @@ describe("RovingTabIndex", () => {
|
||||||
</RovingTabIndexProvider>,
|
</RovingTabIndexProvider>,
|
||||||
);
|
);
|
||||||
|
|
||||||
container.querySelectorAll("button")[0].focus();
|
act(() => container.querySelectorAll("button")[0].focus());
|
||||||
checkTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
|
checkTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
|
||||||
|
|
||||||
await userEvent.keyboard("[ArrowDown]");
|
await userEvent.keyboard("[ArrowDown]");
|
||||||
|
@ -423,7 +423,7 @@ describe("RovingTabIndex", () => {
|
||||||
</RovingTabIndexProvider>,
|
</RovingTabIndexProvider>,
|
||||||
);
|
);
|
||||||
|
|
||||||
container.querySelectorAll("button")[0].focus();
|
act(() => container.querySelectorAll("button")[0].focus());
|
||||||
checkTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
|
checkTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
|
||||||
|
|
||||||
const button = container.querySelectorAll("button")[1];
|
const button = container.querySelectorAll("button")[1];
|
||||||
|
|
|
@ -11,7 +11,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
import "core-js/stable/structured-clone";
|
import "core-js/stable/structured-clone";
|
||||||
import "fake-indexeddb/auto";
|
import "fake-indexeddb/auto";
|
||||||
import React, { ComponentProps } from "react";
|
import React, { ComponentProps } from "react";
|
||||||
import { fireEvent, render, RenderResult, screen, waitFor, within } from "jest-matrix-react";
|
import { fireEvent, render, RenderResult, screen, waitFor, within, act } from "jest-matrix-react";
|
||||||
import fetchMock from "fetch-mock-jest";
|
import fetchMock from "fetch-mock-jest";
|
||||||
import { Mocked, mocked } from "jest-mock";
|
import { Mocked, mocked } from "jest-mock";
|
||||||
import { ClientEvent, MatrixClient, MatrixEvent, Room, SyncState } from "matrix-js-sdk/src/matrix";
|
import { ClientEvent, MatrixClient, MatrixEvent, Room, SyncState } from "matrix-js-sdk/src/matrix";
|
||||||
|
@ -163,7 +163,7 @@ describe("<MatrixChat />", () => {
|
||||||
let initPromise: Promise<void> | undefined;
|
let initPromise: Promise<void> | undefined;
|
||||||
let defaultProps: ComponentProps<typeof MatrixChat>;
|
let defaultProps: ComponentProps<typeof MatrixChat>;
|
||||||
const getComponent = (props: Partial<ComponentProps<typeof MatrixChat>> = {}) =>
|
const getComponent = (props: Partial<ComponentProps<typeof MatrixChat>> = {}) =>
|
||||||
render(<MatrixChat {...defaultProps} {...props} />);
|
render(<MatrixChat {...defaultProps} {...props} />, { legacyRoot: true });
|
||||||
|
|
||||||
// make test results readable
|
// make test results readable
|
||||||
filterConsole(
|
filterConsole(
|
||||||
|
@ -201,7 +201,7 @@ describe("<MatrixChat />", () => {
|
||||||
// we are logged in, but are still waiting for the /sync to complete
|
// we are logged in, but are still waiting for the /sync to complete
|
||||||
await screen.findByText("Syncing…");
|
await screen.findByText("Syncing…");
|
||||||
// initial sync
|
// initial sync
|
||||||
client.emit(ClientEvent.Sync, SyncState.Prepared, null);
|
await act(() => client.emit(ClientEvent.Sync, SyncState.Prepared, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
// let things settle
|
// let things settle
|
||||||
|
@ -263,7 +263,7 @@ describe("<MatrixChat />", () => {
|
||||||
|
|
||||||
// emit a loggedOut event so that all of the Store singletons forget about their references to the mock client
|
// emit a loggedOut event so that all of the Store singletons forget about their references to the mock client
|
||||||
// (must be sync otherwise the next test will start before it happens)
|
// (must be sync otherwise the next test will start before it happens)
|
||||||
defaultDispatcher.dispatch({ action: Action.OnLoggedOut }, true);
|
act(() => defaultDispatcher.dispatch({ action: Action.OnLoggedOut }, true));
|
||||||
|
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
});
|
});
|
||||||
|
@ -328,7 +328,7 @@ describe("<MatrixChat />", () => {
|
||||||
|
|
||||||
expect(within(dialog).getByText(errorMessage)).toBeInTheDocument();
|
expect(within(dialog).getByText(errorMessage)).toBeInTheDocument();
|
||||||
// just check we're back on welcome page
|
// just check we're back on welcome page
|
||||||
await expect(await screen.findByTestId("mx_welcome_screen")).toBeInTheDocument();
|
await expect(screen.findByTestId("mx_welcome_screen")).resolves.toBeInTheDocument();
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -956,9 +956,11 @@ describe("<MatrixChat />", () => {
|
||||||
await screen.findByText("Powered by Matrix");
|
await screen.findByText("Powered by Matrix");
|
||||||
|
|
||||||
// go to login page
|
// go to login page
|
||||||
defaultDispatcher.dispatch({
|
act(() =>
|
||||||
action: "start_login",
|
defaultDispatcher.dispatch({
|
||||||
});
|
action: "start_login",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
|
||||||
|
@ -1126,9 +1128,11 @@ describe("<MatrixChat />", () => {
|
||||||
|
|
||||||
await getComponentAndLogin();
|
await getComponentAndLogin();
|
||||||
|
|
||||||
bootstrapDeferred.resolve();
|
act(() => bootstrapDeferred.resolve());
|
||||||
|
|
||||||
await expect(await screen.findByRole("heading", { name: "You're in", level: 1 })).toBeInTheDocument();
|
await expect(
|
||||||
|
screen.findByRole("heading", { name: "You're in", level: 1 }),
|
||||||
|
).resolves.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1397,7 +1401,9 @@ describe("<MatrixChat />", () => {
|
||||||
|
|
||||||
function simulateSessionLockClaim() {
|
function simulateSessionLockClaim() {
|
||||||
localStorage.setItem("react_sdk_session_lock_claimant", "testtest");
|
localStorage.setItem("react_sdk_session_lock_claimant", "testtest");
|
||||||
window.dispatchEvent(new StorageEvent("storage", { key: "react_sdk_session_lock_claimant" }));
|
act(() =>
|
||||||
|
window.dispatchEvent(new StorageEvent("storage", { key: "react_sdk_session_lock_claimant" })),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
it("after a session is restored", async () => {
|
it("after a session is restored", async () => {
|
||||||
|
|
|
@ -81,9 +81,7 @@ describe("PipContainer", () => {
|
||||||
let voiceBroadcastPlaybacksStore: VoiceBroadcastPlaybacksStore;
|
let voiceBroadcastPlaybacksStore: VoiceBroadcastPlaybacksStore;
|
||||||
|
|
||||||
const actFlushPromises = async () => {
|
const actFlushPromises = async () => {
|
||||||
await act(async () => {
|
await flushPromises();
|
||||||
await flushPromises();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
@ -165,12 +163,12 @@ describe("PipContainer", () => {
|
||||||
if (!(call instanceof MockedCall)) throw new Error("Failed to create call");
|
if (!(call instanceof MockedCall)) throw new Error("Failed to create call");
|
||||||
|
|
||||||
const widget = new Widget(call.widget);
|
const widget = new Widget(call.widget);
|
||||||
WidgetStore.instance.addVirtualWidget(call.widget, room.roomId);
|
|
||||||
WidgetMessagingStore.instance.storeMessaging(widget, room.roomId, {
|
|
||||||
stop: () => {},
|
|
||||||
} as unknown as ClientWidgetApi);
|
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
|
WidgetStore.instance.addVirtualWidget(call.widget, room.roomId);
|
||||||
|
WidgetMessagingStore.instance.storeMessaging(widget, room.roomId, {
|
||||||
|
stop: () => {},
|
||||||
|
} as unknown as ClientWidgetApi);
|
||||||
|
|
||||||
await call.start();
|
await call.start();
|
||||||
ActiveWidgetStore.instance.setWidgetPersistence(widget.id, room.roomId, true);
|
ActiveWidgetStore.instance.setWidgetPersistence(widget.id, room.roomId, true);
|
||||||
});
|
});
|
||||||
|
@ -178,9 +176,11 @@ describe("PipContainer", () => {
|
||||||
await fn(call);
|
await fn(call);
|
||||||
|
|
||||||
cleanup();
|
cleanup();
|
||||||
call.destroy();
|
act(() => {
|
||||||
ActiveWidgetStore.instance.destroyPersistentWidget(widget.id, room.roomId);
|
call.destroy();
|
||||||
WidgetStore.instance.removeVirtualWidget(widget.id, room.roomId);
|
ActiveWidgetStore.instance.destroyPersistentWidget(widget.id, room.roomId);
|
||||||
|
WidgetStore.instance.removeVirtualWidget(widget.id, room.roomId);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const withWidget = async (fn: () => Promise<void>): Promise<void> => {
|
const withWidget = async (fn: () => Promise<void>): Promise<void> => {
|
||||||
|
|
|
@ -23,14 +23,22 @@ import {
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import { CryptoApi, UserVerificationStatus } from "matrix-js-sdk/src/crypto-api";
|
import { CryptoApi, UserVerificationStatus } from "matrix-js-sdk/src/crypto-api";
|
||||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||||
import { fireEvent, render, screen, RenderResult, waitForElementToBeRemoved, waitFor } from "jest-matrix-react";
|
import {
|
||||||
|
fireEvent,
|
||||||
|
render,
|
||||||
|
screen,
|
||||||
|
RenderResult,
|
||||||
|
waitForElementToBeRemoved,
|
||||||
|
waitFor,
|
||||||
|
act,
|
||||||
|
cleanup,
|
||||||
|
} from "jest-matrix-react";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
stubClient,
|
stubClient,
|
||||||
mockPlatformPeg,
|
mockPlatformPeg,
|
||||||
unmockPlatformPeg,
|
unmockPlatformPeg,
|
||||||
wrapInMatrixClientContext,
|
|
||||||
flushPromises,
|
flushPromises,
|
||||||
mkEvent,
|
mkEvent,
|
||||||
setupAsyncStoreWithClient,
|
setupAsyncStoreWithClient,
|
||||||
|
@ -45,7 +53,7 @@ import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||||
import { Action } from "../../../../src/dispatcher/actions";
|
import { Action } from "../../../../src/dispatcher/actions";
|
||||||
import defaultDispatcher from "../../../../src/dispatcher/dispatcher";
|
import defaultDispatcher from "../../../../src/dispatcher/dispatcher";
|
||||||
import { ViewRoomPayload } from "../../../../src/dispatcher/payloads/ViewRoomPayload";
|
import { ViewRoomPayload } from "../../../../src/dispatcher/payloads/ViewRoomPayload";
|
||||||
import { RoomView as _RoomView } from "../../../../src/components/structures/RoomView";
|
import { RoomView } from "../../../../src/components/structures/RoomView";
|
||||||
import ResizeNotifier from "../../../../src/utils/ResizeNotifier";
|
import ResizeNotifier from "../../../../src/utils/ResizeNotifier";
|
||||||
import SettingsStore from "../../../../src/settings/SettingsStore";
|
import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||||
import { SettingLevel } from "../../../../src/settings/SettingLevel";
|
import { SettingLevel } from "../../../../src/settings/SettingLevel";
|
||||||
|
@ -64,8 +72,7 @@ import WidgetStore from "../../../../src/stores/WidgetStore";
|
||||||
import { ViewRoomErrorPayload } from "../../../../src/dispatcher/payloads/ViewRoomErrorPayload";
|
import { ViewRoomErrorPayload } from "../../../../src/dispatcher/payloads/ViewRoomErrorPayload";
|
||||||
import { SearchScope } from "../../../../src/Searching";
|
import { SearchScope } from "../../../../src/Searching";
|
||||||
import { MEGOLM_ENCRYPTION_ALGORITHM } from "../../../../src/utils/crypto";
|
import { MEGOLM_ENCRYPTION_ALGORITHM } from "../../../../src/utils/crypto";
|
||||||
|
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||||
const RoomView = wrapInMatrixClientContext(_RoomView);
|
|
||||||
|
|
||||||
describe("RoomView", () => {
|
describe("RoomView", () => {
|
||||||
let cli: MockedObject<MatrixClient>;
|
let cli: MockedObject<MatrixClient>;
|
||||||
|
@ -106,9 +113,10 @@ describe("RoomView", () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
unmockPlatformPeg();
|
unmockPlatformPeg();
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
cleanup();
|
||||||
});
|
});
|
||||||
|
|
||||||
const mountRoomView = async (ref?: RefObject<_RoomView>): Promise<RenderResult> => {
|
const mountRoomView = async (ref?: RefObject<RoomView>): Promise<RenderResult> => {
|
||||||
if (stores.roomViewStore.getRoomId() !== room.roomId) {
|
if (stores.roomViewStore.getRoomId() !== room.roomId) {
|
||||||
const switchedRoom = new Promise<void>((resolve) => {
|
const switchedRoom = new Promise<void>((resolve) => {
|
||||||
const subFn = () => {
|
const subFn = () => {
|
||||||
|
@ -120,26 +128,30 @@ describe("RoomView", () => {
|
||||||
stores.roomViewStore.on(UPDATE_EVENT, subFn);
|
stores.roomViewStore.on(UPDATE_EVENT, subFn);
|
||||||
});
|
});
|
||||||
|
|
||||||
defaultDispatcher.dispatch<ViewRoomPayload>({
|
act(() =>
|
||||||
action: Action.ViewRoom,
|
defaultDispatcher.dispatch<ViewRoomPayload>({
|
||||||
room_id: room.roomId,
|
action: Action.ViewRoom,
|
||||||
metricsTrigger: undefined,
|
room_id: room.roomId,
|
||||||
});
|
metricsTrigger: undefined,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
await switchedRoom;
|
await switchedRoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
const roomView = render(
|
const roomView = render(
|
||||||
<SDKContext.Provider value={stores}>
|
<MatrixClientContext.Provider value={cli}>
|
||||||
<RoomView
|
<SDKContext.Provider value={stores}>
|
||||||
// threepidInvite should be optional on RoomView props
|
<RoomView
|
||||||
// it is treated as optional in RoomView
|
// threepidInvite should be optional on RoomView props
|
||||||
threepidInvite={undefined as any}
|
// it is treated as optional in RoomView
|
||||||
resizeNotifier={new ResizeNotifier()}
|
threepidInvite={undefined as any}
|
||||||
forceTimeline={false}
|
resizeNotifier={new ResizeNotifier()}
|
||||||
wrappedRef={ref as any}
|
forceTimeline={false}
|
||||||
/>
|
ref={ref}
|
||||||
</SDKContext.Provider>,
|
/>
|
||||||
|
</SDKContext.Provider>
|
||||||
|
</MatrixClientContext.Provider>,
|
||||||
);
|
);
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
return roomView;
|
return roomView;
|
||||||
|
@ -167,22 +179,24 @@ describe("RoomView", () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const roomView = render(
|
const roomView = render(
|
||||||
<SDKContext.Provider value={stores}>
|
<MatrixClientContext.Provider value={cli}>
|
||||||
<RoomView
|
<SDKContext.Provider value={stores}>
|
||||||
// threepidInvite should be optional on RoomView props
|
<RoomView
|
||||||
// it is treated as optional in RoomView
|
// threepidInvite should be optional on RoomView props
|
||||||
threepidInvite={undefined as any}
|
// it is treated as optional in RoomView
|
||||||
resizeNotifier={new ResizeNotifier()}
|
threepidInvite={undefined as any}
|
||||||
forceTimeline={false}
|
resizeNotifier={new ResizeNotifier()}
|
||||||
onRegistered={jest.fn()}
|
forceTimeline={false}
|
||||||
/>
|
onRegistered={jest.fn()}
|
||||||
</SDKContext.Provider>,
|
/>
|
||||||
|
</SDKContext.Provider>
|
||||||
|
</MatrixClientContext.Provider>,
|
||||||
);
|
);
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
return roomView;
|
return roomView;
|
||||||
};
|
};
|
||||||
const getRoomViewInstance = async (): Promise<_RoomView> => {
|
const getRoomViewInstance = async (): Promise<RoomView> => {
|
||||||
const ref = createRef<_RoomView>();
|
const ref = createRef<RoomView>();
|
||||||
await mountRoomView(ref);
|
await mountRoomView(ref);
|
||||||
return ref.current!;
|
return ref.current!;
|
||||||
};
|
};
|
||||||
|
@ -193,7 +207,7 @@ describe("RoomView", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when there is an old room", () => {
|
describe("when there is an old room", () => {
|
||||||
let instance: _RoomView;
|
let instance: RoomView;
|
||||||
let oldRoom: Room;
|
let oldRoom: Room;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
@ -217,11 +231,11 @@ describe("RoomView", () => {
|
||||||
|
|
||||||
describe("and feature_dynamic_room_predecessors is enabled", () => {
|
describe("and feature_dynamic_room_predecessors is enabled", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
instance.setState({ msc3946ProcessDynamicPredecessor: true });
|
act(() => instance.setState({ msc3946ProcessDynamicPredecessor: true }));
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
instance.setState({ msc3946ProcessDynamicPredecessor: false });
|
act(() => instance.setState({ msc3946ProcessDynamicPredecessor: false }));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should pass the setting to findPredecessor", async () => {
|
it("should pass the setting to findPredecessor", async () => {
|
||||||
|
@ -252,15 +266,17 @@ describe("RoomView", () => {
|
||||||
cli.isRoomEncrypted.mockReturnValue(true);
|
cli.isRoomEncrypted.mockReturnValue(true);
|
||||||
|
|
||||||
// and fake an encryption event into the room to prompt it to re-check
|
// and fake an encryption event into the room to prompt it to re-check
|
||||||
room.addLiveEvents([
|
await act(() =>
|
||||||
new MatrixEvent({
|
room.addLiveEvents([
|
||||||
type: "m.room.encryption",
|
new MatrixEvent({
|
||||||
sender: cli.getUserId()!,
|
type: "m.room.encryption",
|
||||||
content: {},
|
sender: cli.getUserId()!,
|
||||||
event_id: "someid",
|
content: {},
|
||||||
room_id: room.roomId,
|
event_id: "someid",
|
||||||
}),
|
room_id: room.roomId,
|
||||||
]);
|
}),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
// URL previews should now be disabled
|
// URL previews should now be disabled
|
||||||
expect(roomViewInstance.state.showUrlPreview).toBe(false);
|
expect(roomViewInstance.state.showUrlPreview).toBe(false);
|
||||||
|
@ -270,7 +286,7 @@ describe("RoomView", () => {
|
||||||
const roomViewInstance = await getRoomViewInstance();
|
const roomViewInstance = await getRoomViewInstance();
|
||||||
const oldTimeline = roomViewInstance.state.liveTimeline;
|
const oldTimeline = roomViewInstance.state.liveTimeline;
|
||||||
|
|
||||||
room.getUnfilteredTimelineSet().resetLiveTimeline();
|
act(() => room.getUnfilteredTimelineSet().resetLiveTimeline());
|
||||||
expect(roomViewInstance.state.liveTimeline).not.toEqual(oldTimeline);
|
expect(roomViewInstance.state.liveTimeline).not.toEqual(oldTimeline);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -287,7 +303,7 @@ describe("RoomView", () => {
|
||||||
await renderRoomView();
|
await renderRoomView();
|
||||||
expect(VoipUserMapper.sharedInstance().getVirtualRoomForRoom).toHaveBeenCalledWith(room.roomId);
|
expect(VoipUserMapper.sharedInstance().getVirtualRoomForRoom).toHaveBeenCalledWith(room.roomId);
|
||||||
|
|
||||||
cli.emit(ClientEvent.Room, room);
|
act(() => cli.emit(ClientEvent.Room, room));
|
||||||
|
|
||||||
// called again after room event
|
// called again after room event
|
||||||
expect(VoipUserMapper.sharedInstance().getVirtualRoomForRoom).toHaveBeenCalledTimes(2);
|
expect(VoipUserMapper.sharedInstance().getVirtualRoomForRoom).toHaveBeenCalledTimes(2);
|
||||||
|
@ -429,6 +445,194 @@ describe("RoomView", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should show error view if failed to look up room alias", async () => {
|
||||||
|
const { asFragment, findByText } = await renderRoomView(false);
|
||||||
|
|
||||||
|
act(() =>
|
||||||
|
defaultDispatcher.dispatch<ViewRoomErrorPayload>({
|
||||||
|
action: Action.ViewRoomError,
|
||||||
|
room_alias: "#addy:server",
|
||||||
|
room_id: null,
|
||||||
|
err: new MatrixError({ errcode: "M_NOT_FOUND" }),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
await emitPromise(stores.roomViewStore, UPDATE_EVENT);
|
||||||
|
|
||||||
|
await findByText("Are you sure you're at the right place?");
|
||||||
|
expect(asFragment()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("knock rooms", () => {
|
||||||
|
const client = createTestClient();
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting) => setting === "feature_ask_to_join");
|
||||||
|
jest.spyOn(room, "getJoinRule").mockReturnValue(JoinRule.Knock);
|
||||||
|
jest.spyOn(defaultDispatcher, "dispatch");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows to request to join", async () => {
|
||||||
|
jest.spyOn(MatrixClientPeg, "safeGet").mockReturnValue(client);
|
||||||
|
jest.spyOn(client, "knockRoom").mockResolvedValue({ room_id: room.roomId });
|
||||||
|
|
||||||
|
await mountRoomView();
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "Request access" }));
|
||||||
|
await untilDispatch(Action.SubmitAskToJoin, defaultDispatcher);
|
||||||
|
|
||||||
|
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({
|
||||||
|
action: "submit_ask_to_join",
|
||||||
|
roomId: room.roomId,
|
||||||
|
opts: { reason: undefined },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows to cancel a join request", async () => {
|
||||||
|
jest.spyOn(MatrixClientPeg, "safeGet").mockReturnValue(client);
|
||||||
|
jest.spyOn(client, "leave").mockResolvedValue({});
|
||||||
|
jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Knock);
|
||||||
|
|
||||||
|
await mountRoomView();
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "Cancel request" }));
|
||||||
|
await untilDispatch(Action.CancelAskToJoin, defaultDispatcher);
|
||||||
|
|
||||||
|
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({
|
||||||
|
action: "cancel_ask_to_join",
|
||||||
|
roomId: room.roomId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should close search results when edit is clicked", async () => {
|
||||||
|
room.getMyMembership = jest.fn().mockReturnValue(KnownMembership.Join);
|
||||||
|
|
||||||
|
const eventMapper = (obj: Partial<IEvent>) => new MatrixEvent(obj);
|
||||||
|
|
||||||
|
const roomViewRef = createRef<RoomView>();
|
||||||
|
const { container, getByText, findByLabelText } = await mountRoomView(roomViewRef);
|
||||||
|
await waitFor(() => expect(roomViewRef.current).toBeTruthy());
|
||||||
|
// @ts-ignore - triggering a search organically is a lot of work
|
||||||
|
act(() =>
|
||||||
|
roomViewRef.current!.setState({
|
||||||
|
search: {
|
||||||
|
searchId: 1,
|
||||||
|
roomId: room.roomId,
|
||||||
|
term: "search term",
|
||||||
|
scope: SearchScope.Room,
|
||||||
|
promise: Promise.resolve({
|
||||||
|
results: [
|
||||||
|
SearchResult.fromJson(
|
||||||
|
{
|
||||||
|
rank: 1,
|
||||||
|
result: {
|
||||||
|
content: {
|
||||||
|
body: "search term",
|
||||||
|
msgtype: "m.text",
|
||||||
|
},
|
||||||
|
type: "m.room.message",
|
||||||
|
event_id: "$eventId",
|
||||||
|
sender: cli.getSafeUserId(),
|
||||||
|
origin_server_ts: 123456789,
|
||||||
|
room_id: room.roomId,
|
||||||
|
},
|
||||||
|
context: {
|
||||||
|
events_before: [],
|
||||||
|
events_after: [],
|
||||||
|
profile_info: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
eventMapper,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
highlights: [],
|
||||||
|
count: 1,
|
||||||
|
}),
|
||||||
|
inProgress: false,
|
||||||
|
count: 1,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(container.querySelector(".mx_RoomView_searchResultsPanel")).toBeVisible();
|
||||||
|
});
|
||||||
|
const prom = waitForElementToBeRemoved(() => container.querySelector(".mx_RoomView_searchResultsPanel"));
|
||||||
|
|
||||||
|
await userEvent.hover(getByText("search term"));
|
||||||
|
await userEvent.click(await findByLabelText("Edit"));
|
||||||
|
|
||||||
|
await prom;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should switch rooms when edit is clicked on a search result for a different room", async () => {
|
||||||
|
const room2 = new Room(`!${roomCount++}:example.org`, cli, "@alice:example.org");
|
||||||
|
rooms.set(room2.roomId, room2);
|
||||||
|
|
||||||
|
room.getMyMembership = jest.fn().mockReturnValue(KnownMembership.Join);
|
||||||
|
|
||||||
|
const eventMapper = (obj: Partial<IEvent>) => new MatrixEvent(obj);
|
||||||
|
|
||||||
|
const roomViewRef = createRef<RoomView>();
|
||||||
|
const { container, getByText, findByLabelText } = await mountRoomView(roomViewRef);
|
||||||
|
await waitFor(() => expect(roomViewRef.current).toBeTruthy());
|
||||||
|
// @ts-ignore - triggering a search organically is a lot of work
|
||||||
|
act(() =>
|
||||||
|
roomViewRef.current!.setState({
|
||||||
|
search: {
|
||||||
|
searchId: 1,
|
||||||
|
roomId: room.roomId,
|
||||||
|
term: "search term",
|
||||||
|
scope: SearchScope.All,
|
||||||
|
promise: Promise.resolve({
|
||||||
|
results: [
|
||||||
|
SearchResult.fromJson(
|
||||||
|
{
|
||||||
|
rank: 1,
|
||||||
|
result: {
|
||||||
|
content: {
|
||||||
|
body: "search term",
|
||||||
|
msgtype: "m.text",
|
||||||
|
},
|
||||||
|
type: "m.room.message",
|
||||||
|
event_id: "$eventId",
|
||||||
|
sender: cli.getSafeUserId(),
|
||||||
|
origin_server_ts: 123456789,
|
||||||
|
room_id: room2.roomId,
|
||||||
|
},
|
||||||
|
context: {
|
||||||
|
events_before: [],
|
||||||
|
events_after: [],
|
||||||
|
profile_info: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
eventMapper,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
highlights: [],
|
||||||
|
count: 1,
|
||||||
|
}),
|
||||||
|
inProgress: false,
|
||||||
|
count: 1,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(container.querySelector(".mx_RoomView_searchResultsPanel")).toBeVisible();
|
||||||
|
});
|
||||||
|
const prom = untilDispatch(Action.ViewRoom, defaultDispatcher);
|
||||||
|
|
||||||
|
await userEvent.hover(getByText("search term"));
|
||||||
|
await userEvent.click(await findByLabelText("Edit"));
|
||||||
|
|
||||||
|
await expect(prom).resolves.toEqual(expect.objectContaining({ room_id: room2.roomId }));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fires Action.RoomLoaded", async () => {
|
||||||
|
jest.spyOn(defaultDispatcher, "dispatch");
|
||||||
|
await mountRoomView();
|
||||||
|
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({ action: Action.RoomLoaded });
|
||||||
|
});
|
||||||
|
|
||||||
describe("when there is a RoomView", () => {
|
describe("when there is a RoomView", () => {
|
||||||
const widget1Id = "widget1";
|
const widget1Id = "widget1";
|
||||||
const widget2Id = "widget2";
|
const widget2Id = "widget2";
|
||||||
|
@ -514,184 +718,4 @@ describe("RoomView", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should show error view if failed to look up room alias", async () => {
|
|
||||||
const { asFragment, findByText } = await renderRoomView(false);
|
|
||||||
|
|
||||||
defaultDispatcher.dispatch<ViewRoomErrorPayload>({
|
|
||||||
action: Action.ViewRoomError,
|
|
||||||
room_alias: "#addy:server",
|
|
||||||
room_id: null,
|
|
||||||
err: new MatrixError({ errcode: "M_NOT_FOUND" }),
|
|
||||||
});
|
|
||||||
await emitPromise(stores.roomViewStore, UPDATE_EVENT);
|
|
||||||
|
|
||||||
await findByText("Are you sure you're at the right place?");
|
|
||||||
expect(asFragment()).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("knock rooms", () => {
|
|
||||||
const client = createTestClient();
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((setting) => setting === "feature_ask_to_join");
|
|
||||||
jest.spyOn(room, "getJoinRule").mockReturnValue(JoinRule.Knock);
|
|
||||||
jest.spyOn(defaultDispatcher, "dispatch");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("allows to request to join", async () => {
|
|
||||||
jest.spyOn(MatrixClientPeg, "safeGet").mockReturnValue(client);
|
|
||||||
jest.spyOn(client, "knockRoom").mockResolvedValue({ room_id: room.roomId });
|
|
||||||
|
|
||||||
await mountRoomView();
|
|
||||||
fireEvent.click(screen.getByRole("button", { name: "Request access" }));
|
|
||||||
await untilDispatch(Action.SubmitAskToJoin, defaultDispatcher);
|
|
||||||
|
|
||||||
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({
|
|
||||||
action: "submit_ask_to_join",
|
|
||||||
roomId: room.roomId,
|
|
||||||
opts: { reason: undefined },
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("allows to cancel a join request", async () => {
|
|
||||||
jest.spyOn(MatrixClientPeg, "safeGet").mockReturnValue(client);
|
|
||||||
jest.spyOn(client, "leave").mockResolvedValue({});
|
|
||||||
jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Knock);
|
|
||||||
|
|
||||||
await mountRoomView();
|
|
||||||
fireEvent.click(screen.getByRole("button", { name: "Cancel request" }));
|
|
||||||
await untilDispatch(Action.CancelAskToJoin, defaultDispatcher);
|
|
||||||
|
|
||||||
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({
|
|
||||||
action: "cancel_ask_to_join",
|
|
||||||
roomId: room.roomId,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should close search results when edit is clicked", async () => {
|
|
||||||
room.getMyMembership = jest.fn().mockReturnValue(KnownMembership.Join);
|
|
||||||
|
|
||||||
const eventMapper = (obj: Partial<IEvent>) => new MatrixEvent(obj);
|
|
||||||
|
|
||||||
const roomViewRef = createRef<_RoomView>();
|
|
||||||
const { container, getByText, findByLabelText } = await mountRoomView(roomViewRef);
|
|
||||||
// @ts-ignore - triggering a search organically is a lot of work
|
|
||||||
roomViewRef.current!.setState({
|
|
||||||
search: {
|
|
||||||
searchId: 1,
|
|
||||||
roomId: room.roomId,
|
|
||||||
term: "search term",
|
|
||||||
scope: SearchScope.Room,
|
|
||||||
promise: Promise.resolve({
|
|
||||||
results: [
|
|
||||||
SearchResult.fromJson(
|
|
||||||
{
|
|
||||||
rank: 1,
|
|
||||||
result: {
|
|
||||||
content: {
|
|
||||||
body: "search term",
|
|
||||||
msgtype: "m.text",
|
|
||||||
},
|
|
||||||
type: "m.room.message",
|
|
||||||
event_id: "$eventId",
|
|
||||||
sender: cli.getSafeUserId(),
|
|
||||||
origin_server_ts: 123456789,
|
|
||||||
room_id: room.roomId,
|
|
||||||
},
|
|
||||||
context: {
|
|
||||||
events_before: [],
|
|
||||||
events_after: [],
|
|
||||||
profile_info: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
eventMapper,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
highlights: [],
|
|
||||||
count: 1,
|
|
||||||
}),
|
|
||||||
inProgress: false,
|
|
||||||
count: 1,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(container.querySelector(".mx_RoomView_searchResultsPanel")).toBeVisible();
|
|
||||||
});
|
|
||||||
const prom = waitForElementToBeRemoved(() => container.querySelector(".mx_RoomView_searchResultsPanel"));
|
|
||||||
|
|
||||||
await userEvent.hover(getByText("search term"));
|
|
||||||
await userEvent.click(await findByLabelText("Edit"));
|
|
||||||
|
|
||||||
await prom;
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should switch rooms when edit is clicked on a search result for a different room", async () => {
|
|
||||||
const room2 = new Room(`!${roomCount++}:example.org`, cli, "@alice:example.org");
|
|
||||||
rooms.set(room2.roomId, room2);
|
|
||||||
|
|
||||||
room.getMyMembership = jest.fn().mockReturnValue(KnownMembership.Join);
|
|
||||||
|
|
||||||
const eventMapper = (obj: Partial<IEvent>) => new MatrixEvent(obj);
|
|
||||||
|
|
||||||
const roomViewRef = createRef<_RoomView>();
|
|
||||||
const { container, getByText, findByLabelText } = await mountRoomView(roomViewRef);
|
|
||||||
// @ts-ignore - triggering a search organically is a lot of work
|
|
||||||
roomViewRef.current!.setState({
|
|
||||||
search: {
|
|
||||||
searchId: 1,
|
|
||||||
roomId: room.roomId,
|
|
||||||
term: "search term",
|
|
||||||
scope: SearchScope.All,
|
|
||||||
promise: Promise.resolve({
|
|
||||||
results: [
|
|
||||||
SearchResult.fromJson(
|
|
||||||
{
|
|
||||||
rank: 1,
|
|
||||||
result: {
|
|
||||||
content: {
|
|
||||||
body: "search term",
|
|
||||||
msgtype: "m.text",
|
|
||||||
},
|
|
||||||
type: "m.room.message",
|
|
||||||
event_id: "$eventId",
|
|
||||||
sender: cli.getSafeUserId(),
|
|
||||||
origin_server_ts: 123456789,
|
|
||||||
room_id: room2.roomId,
|
|
||||||
},
|
|
||||||
context: {
|
|
||||||
events_before: [],
|
|
||||||
events_after: [],
|
|
||||||
profile_info: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
eventMapper,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
highlights: [],
|
|
||||||
count: 1,
|
|
||||||
}),
|
|
||||||
inProgress: false,
|
|
||||||
count: 1,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(container.querySelector(".mx_RoomView_searchResultsPanel")).toBeVisible();
|
|
||||||
});
|
|
||||||
const prom = untilDispatch(Action.ViewRoom, defaultDispatcher);
|
|
||||||
|
|
||||||
await userEvent.hover(getByText("search term"));
|
|
||||||
await userEvent.click(await findByLabelText("Edit"));
|
|
||||||
|
|
||||||
await expect(prom).resolves.toEqual(expect.objectContaining({ room_id: room2.roomId }));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("fires Action.RoomLoaded", async () => {
|
|
||||||
jest.spyOn(defaultDispatcher, "dispatch");
|
|
||||||
await mountRoomView();
|
|
||||||
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith({ action: Action.RoomLoaded });
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -215,34 +215,33 @@ describe("ThreadPanel", () => {
|
||||||
myThreads!.addLiveEvent(mixedThread.rootEvent);
|
myThreads!.addLiveEvent(mixedThread.rootEvent);
|
||||||
myThreads!.addLiveEvent(ownThread.rootEvent);
|
myThreads!.addLiveEvent(ownThread.rootEvent);
|
||||||
|
|
||||||
let events: EventData[] = [];
|
|
||||||
const renderResult = render(<TestThreadPanel />);
|
const renderResult = render(<TestThreadPanel />);
|
||||||
await waitFor(() => expect(renderResult.container.querySelector(".mx_AutoHideScrollbar")).toBeFalsy());
|
await waitFor(() => expect(renderResult.container.querySelector(".mx_AutoHideScrollbar")).toBeFalsy());
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
events = findEvents(renderResult.container);
|
const events = findEvents(renderResult.container);
|
||||||
expect(findEvents(renderResult.container)).toHaveLength(3);
|
expect(events).toHaveLength(3);
|
||||||
|
expect(events[0]).toEqual(toEventData(otherThread.rootEvent));
|
||||||
|
expect(events[1]).toEqual(toEventData(mixedThread.rootEvent));
|
||||||
|
expect(events[2]).toEqual(toEventData(ownThread.rootEvent));
|
||||||
});
|
});
|
||||||
expect(events[0]).toEqual(toEventData(otherThread.rootEvent));
|
|
||||||
expect(events[1]).toEqual(toEventData(mixedThread.rootEvent));
|
|
||||||
expect(events[2]).toEqual(toEventData(ownThread.rootEvent));
|
|
||||||
await waitFor(() => expect(renderResult.container.querySelector(".mx_ThreadPanel_dropdown")).toBeTruthy());
|
await waitFor(() => expect(renderResult.container.querySelector(".mx_ThreadPanel_dropdown")).toBeTruthy());
|
||||||
toggleThreadFilter(renderResult.container, ThreadFilterType.My);
|
toggleThreadFilter(renderResult.container, ThreadFilterType.My);
|
||||||
await waitFor(() => expect(renderResult.container.querySelector(".mx_AutoHideScrollbar")).toBeFalsy());
|
await waitFor(() => expect(renderResult.container.querySelector(".mx_AutoHideScrollbar")).toBeFalsy());
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
events = findEvents(renderResult.container);
|
const events = findEvents(renderResult.container);
|
||||||
expect(findEvents(renderResult.container)).toHaveLength(2);
|
expect(events).toHaveLength(2);
|
||||||
|
expect(events[0]).toEqual(toEventData(mixedThread.rootEvent));
|
||||||
|
expect(events[1]).toEqual(toEventData(ownThread.rootEvent));
|
||||||
});
|
});
|
||||||
expect(events[0]).toEqual(toEventData(mixedThread.rootEvent));
|
|
||||||
expect(events[1]).toEqual(toEventData(ownThread.rootEvent));
|
|
||||||
toggleThreadFilter(renderResult.container, ThreadFilterType.All);
|
toggleThreadFilter(renderResult.container, ThreadFilterType.All);
|
||||||
await waitFor(() => expect(renderResult.container.querySelector(".mx_AutoHideScrollbar")).toBeFalsy());
|
await waitFor(() => expect(renderResult.container.querySelector(".mx_AutoHideScrollbar")).toBeFalsy());
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
events = findEvents(renderResult.container);
|
const events = findEvents(renderResult.container);
|
||||||
expect(findEvents(renderResult.container)).toHaveLength(3);
|
expect(events).toHaveLength(3);
|
||||||
|
expect(events[0]).toEqual(toEventData(otherThread.rootEvent));
|
||||||
|
expect(events[1]).toEqual(toEventData(mixedThread.rootEvent));
|
||||||
|
expect(events[2]).toEqual(toEventData(ownThread.rootEvent));
|
||||||
});
|
});
|
||||||
expect(events[0]).toEqual(toEventData(otherThread.rootEvent));
|
|
||||||
expect(events[1]).toEqual(toEventData(mixedThread.rootEvent));
|
|
||||||
expect(events[2]).toEqual(toEventData(ownThread.rootEvent));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("correctly filters Thread List with a single, unparticipated thread", async () => {
|
it("correctly filters Thread List with a single, unparticipated thread", async () => {
|
||||||
|
@ -261,28 +260,27 @@ describe("ThreadPanel", () => {
|
||||||
const [allThreads] = room.threadsTimelineSets;
|
const [allThreads] = room.threadsTimelineSets;
|
||||||
allThreads!.addLiveEvent(otherThread.rootEvent);
|
allThreads!.addLiveEvent(otherThread.rootEvent);
|
||||||
|
|
||||||
let events: EventData[] = [];
|
|
||||||
const renderResult = render(<TestThreadPanel />);
|
const renderResult = render(<TestThreadPanel />);
|
||||||
await waitFor(() => expect(renderResult.container.querySelector(".mx_AutoHideScrollbar")).toBeFalsy());
|
await waitFor(() => expect(renderResult.container.querySelector(".mx_AutoHideScrollbar")).toBeFalsy());
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
events = findEvents(renderResult.container);
|
const events = findEvents(renderResult.container);
|
||||||
expect(findEvents(renderResult.container)).toHaveLength(1);
|
expect(events).toHaveLength(1);
|
||||||
|
expect(events[0]).toEqual(toEventData(otherThread.rootEvent));
|
||||||
});
|
});
|
||||||
expect(events[0]).toEqual(toEventData(otherThread.rootEvent));
|
|
||||||
await waitFor(() => expect(renderResult.container.querySelector(".mx_ThreadPanel_dropdown")).toBeTruthy());
|
await waitFor(() => expect(renderResult.container.querySelector(".mx_ThreadPanel_dropdown")).toBeTruthy());
|
||||||
toggleThreadFilter(renderResult.container, ThreadFilterType.My);
|
toggleThreadFilter(renderResult.container, ThreadFilterType.My);
|
||||||
await waitFor(() => expect(renderResult.container.querySelector(".mx_AutoHideScrollbar")).toBeFalsy());
|
await waitFor(() => expect(renderResult.container.querySelector(".mx_AutoHideScrollbar")).toBeFalsy());
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
events = findEvents(renderResult.container);
|
const events = findEvents(renderResult.container);
|
||||||
expect(findEvents(renderResult.container)).toHaveLength(0);
|
expect(events).toHaveLength(0);
|
||||||
});
|
});
|
||||||
toggleThreadFilter(renderResult.container, ThreadFilterType.All);
|
toggleThreadFilter(renderResult.container, ThreadFilterType.All);
|
||||||
await waitFor(() => expect(renderResult.container.querySelector(".mx_AutoHideScrollbar")).toBeFalsy());
|
await waitFor(() => expect(renderResult.container.querySelector(".mx_AutoHideScrollbar")).toBeFalsy());
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
events = findEvents(renderResult.container);
|
const events = findEvents(renderResult.container);
|
||||||
expect(findEvents(renderResult.container)).toHaveLength(1);
|
expect(events).toHaveLength(1);
|
||||||
|
expect(events[0]).toEqual(toEventData(otherThread.rootEvent));
|
||||||
});
|
});
|
||||||
expect(events[0]).toEqual(toEventData(otherThread.rootEvent));
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { render, waitFor, screen } from "jest-matrix-react";
|
import { render, waitFor, screen, act } from "jest-matrix-react";
|
||||||
import {
|
import {
|
||||||
ReceiptType,
|
ReceiptType,
|
||||||
EventTimelineSet,
|
EventTimelineSet,
|
||||||
|
@ -205,8 +205,10 @@ describe("TimelinePanel", () => {
|
||||||
manageReadReceipts={true}
|
manageReadReceipts={true}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
/>,
|
/>,
|
||||||
|
{ legacyRoot: true },
|
||||||
);
|
);
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
await waitFor(() => expect(ref.current).toBeTruthy());
|
||||||
timelinePanel = ref.current!;
|
timelinePanel = ref.current!;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -255,14 +257,16 @@ describe("TimelinePanel", () => {
|
||||||
|
|
||||||
describe("and reading the timeline", () => {
|
describe("and reading the timeline", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await renderTimelinePanel();
|
await act(async () => {
|
||||||
timelineSet.addLiveEvent(ev1, {});
|
await renderTimelinePanel();
|
||||||
await flushPromises();
|
timelineSet.addLiveEvent(ev1, {});
|
||||||
|
await flushPromises();
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
await timelinePanel.sendReadReceipts();
|
await timelinePanel.sendReadReceipts();
|
||||||
// @ts-ignore Simulate user activity by calling updateReadMarker on the TimelinePanel.
|
// @ts-ignore Simulate user activity by calling updateReadMarker on the TimelinePanel.
|
||||||
await timelinePanel.updateReadMarker();
|
await timelinePanel.updateReadMarker();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should send a fully read marker and a public receipt", async () => {
|
it("should send a fully read marker and a public receipt", async () => {
|
||||||
|
@ -276,7 +280,7 @@ describe("TimelinePanel", () => {
|
||||||
client.setRoomReadMarkers.mockClear();
|
client.setRoomReadMarkers.mockClear();
|
||||||
|
|
||||||
// @ts-ignore Simulate user activity by calling updateReadMarker on the TimelinePanel.
|
// @ts-ignore Simulate user activity by calling updateReadMarker on the TimelinePanel.
|
||||||
await timelinePanel.updateReadMarker();
|
await act(() => timelinePanel.updateReadMarker());
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not send receipts again", () => {
|
it("should not send receipts again", () => {
|
||||||
|
@ -315,7 +319,7 @@ describe("TimelinePanel", () => {
|
||||||
|
|
||||||
it("should send a fully read marker and a private receipt", async () => {
|
it("should send a fully read marker and a private receipt", async () => {
|
||||||
await renderTimelinePanel();
|
await renderTimelinePanel();
|
||||||
timelineSet.addLiveEvent(ev1, {});
|
act(() => timelineSet.addLiveEvent(ev1, {}));
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -326,6 +330,7 @@ describe("TimelinePanel", () => {
|
||||||
// Expect the fully_read marker not to be send yet
|
// Expect the fully_read marker not to be send yet
|
||||||
expect(client.setRoomReadMarkers).not.toHaveBeenCalled();
|
expect(client.setRoomReadMarkers).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
await flushPromises();
|
||||||
client.sendReadReceipt.mockClear();
|
client.sendReadReceipt.mockClear();
|
||||||
|
|
||||||
// @ts-ignore simulate user activity
|
// @ts-ignore simulate user activity
|
||||||
|
@ -334,7 +339,7 @@ describe("TimelinePanel", () => {
|
||||||
// It should not send the receipt again.
|
// It should not send the receipt again.
|
||||||
expect(client.sendReadReceipt).not.toHaveBeenCalledWith(ev1, ReceiptType.ReadPrivate);
|
expect(client.sendReadReceipt).not.toHaveBeenCalledWith(ev1, ReceiptType.ReadPrivate);
|
||||||
// Expect the fully_read marker to be sent after user activity.
|
// Expect the fully_read marker to be sent after user activity.
|
||||||
expect(client.setRoomReadMarkers).toHaveBeenCalledWith(roomId, ev1.getId());
|
await waitFor(() => expect(client.setRoomReadMarkers).toHaveBeenCalledWith(roomId, ev1.getId()));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -361,11 +366,11 @@ describe("TimelinePanel", () => {
|
||||||
|
|
||||||
it("should send receipts but no fully_read when reading the thread timeline", async () => {
|
it("should send receipts but no fully_read when reading the thread timeline", async () => {
|
||||||
await renderTimelinePanel();
|
await renderTimelinePanel();
|
||||||
timelineSet.addLiveEvent(threadEv1, {});
|
act(() => timelineSet.addLiveEvent(threadEv1, {}));
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
await timelinePanel.sendReadReceipts();
|
await act(() => timelinePanel.sendReadReceipts());
|
||||||
|
|
||||||
// fully_read is not supported for threads per spec
|
// fully_read is not supported for threads per spec
|
||||||
expect(client.setRoomReadMarkers).not.toHaveBeenCalled();
|
expect(client.setRoomReadMarkers).not.toHaveBeenCalled();
|
||||||
|
@ -1021,7 +1026,7 @@ describe("TimelinePanel", () => {
|
||||||
await waitFor(() => expectEvents(container, [events[1]]));
|
await waitFor(() => expectEvents(container, [events[1]]));
|
||||||
});
|
});
|
||||||
|
|
||||||
defaultDispatcher.fire(Action.DumpDebugLogs);
|
act(() => defaultDispatcher.fire(Action.DumpDebugLogs));
|
||||||
|
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
expect(spy).toHaveBeenCalledWith(expect.stringContaining("TimelinePanel(Room): Debugging info for roomId")),
|
expect(spy).toHaveBeenCalledWith(expect.stringContaining("TimelinePanel(Room): Debugging info for roomId")),
|
||||||
|
|
|
@ -8,19 +8,13 @@ Please see LICENSE files in the repository root for full details.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { mocked } from "jest-mock";
|
import { mocked } from "jest-mock";
|
||||||
import { act, render, RenderResult, screen, waitFor } from "jest-matrix-react";
|
import { render, RenderResult, screen, waitFor } from "jest-matrix-react";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
import { MatrixClient, createClient } from "matrix-js-sdk/src/matrix";
|
import { MatrixClient, createClient } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import ForgotPassword from "../../../../../src/components/structures/auth/ForgotPassword";
|
import ForgotPassword from "../../../../../src/components/structures/auth/ForgotPassword";
|
||||||
import { ValidatedServerConfig } from "../../../../../src/utils/ValidatedServerConfig";
|
import { ValidatedServerConfig } from "../../../../../src/utils/ValidatedServerConfig";
|
||||||
import {
|
import { clearAllModals, filterConsole, stubClient, waitEnoughCyclesForModal } from "../../../../test-utils";
|
||||||
clearAllModals,
|
|
||||||
filterConsole,
|
|
||||||
flushPromisesWithFakeTimers,
|
|
||||||
stubClient,
|
|
||||||
waitEnoughCyclesForModal,
|
|
||||||
} from "../../../../test-utils";
|
|
||||||
import AutoDiscoveryUtils from "../../../../../src/utils/AutoDiscoveryUtils";
|
import AutoDiscoveryUtils from "../../../../../src/utils/AutoDiscoveryUtils";
|
||||||
|
|
||||||
jest.mock("matrix-js-sdk/src/matrix", () => ({
|
jest.mock("matrix-js-sdk/src/matrix", () => ({
|
||||||
|
@ -39,11 +33,7 @@ describe("<ForgotPassword>", () => {
|
||||||
let renderResult: RenderResult;
|
let renderResult: RenderResult;
|
||||||
|
|
||||||
const typeIntoField = async (label: string, value: string): Promise<void> => {
|
const typeIntoField = async (label: string, value: string): Promise<void> => {
|
||||||
await act(async () => {
|
await userEvent.type(screen.getByLabelText(label), value, { delay: null });
|
||||||
await userEvent.type(screen.getByLabelText(label), value, { delay: null });
|
|
||||||
// the message is shown after some time
|
|
||||||
jest.advanceTimersByTime(500);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const click = async (element: Element): Promise<void> => {
|
const click = async (element: Element): Promise<void> => {
|
||||||
|
@ -80,18 +70,11 @@ describe("<ForgotPassword>", () => {
|
||||||
await clearAllModals();
|
await clearAllModals();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
jest.useFakeTimers();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
jest.useRealTimers();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("when starting a password reset flow", () => {
|
describe("when starting a password reset flow", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
renderResult = render(
|
renderResult = render(
|
||||||
<ForgotPassword serverConfig={serverConfig} onComplete={onComplete} onLoginClick={onLoginClick} />,
|
<ForgotPassword serverConfig={serverConfig} onComplete={onComplete} onLoginClick={onLoginClick} />,
|
||||||
|
{ legacyRoot: true },
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -128,8 +111,10 @@ describe("<ForgotPassword>", () => {
|
||||||
await typeIntoField("Email address", "not en email");
|
await typeIntoField("Email address", "not en email");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should show a message about the wrong format", () => {
|
it("should show a message about the wrong format", async () => {
|
||||||
expect(screen.getByText("The email address doesn't appear to be valid.")).toBeInTheDocument();
|
await expect(
|
||||||
|
screen.findByText("The email address doesn't appear to be valid."),
|
||||||
|
).resolves.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -142,8 +127,8 @@ describe("<ForgotPassword>", () => {
|
||||||
await click(screen.getByText("Send email"));
|
await click(screen.getByText("Send email"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should show an email not found message", () => {
|
it("should show an email not found message", async () => {
|
||||||
expect(screen.getByText("This email address was not found")).toBeInTheDocument();
|
await expect(screen.findByText("This email address was not found")).resolves.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -156,13 +141,12 @@ describe("<ForgotPassword>", () => {
|
||||||
await click(screen.getByText("Send email"));
|
await click(screen.getByText("Send email"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should show an info about that", () => {
|
it("should show an info about that", async () => {
|
||||||
expect(
|
await expect(
|
||||||
screen.getByText(
|
screen.findByText(
|
||||||
"Cannot reach homeserver: " +
|
"Cannot reach homeserver: Ensure you have a stable internet connection, or get in touch with the server admin",
|
||||||
"Ensure you have a stable internet connection, or get in touch with the server admin",
|
|
||||||
),
|
),
|
||||||
).toBeInTheDocument();
|
).resolves.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -178,8 +162,8 @@ describe("<ForgotPassword>", () => {
|
||||||
await click(screen.getByText("Send email"));
|
await click(screen.getByText("Send email"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should show the server error", () => {
|
it("should show the server error", async () => {
|
||||||
expect(screen.queryByText("server down")).toBeInTheDocument();
|
await expect(screen.findByText("server down")).resolves.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -215,8 +199,6 @@ describe("<ForgotPassword>", () => {
|
||||||
describe("and clicking »Resend«", () => {
|
describe("and clicking »Resend«", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await click(screen.getByText("Resend"));
|
await click(screen.getByText("Resend"));
|
||||||
// the message is shown after some time
|
|
||||||
jest.advanceTimersByTime(500);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should should resend the mail and show the tooltip", () => {
|
it("should should resend the mail and show the tooltip", () => {
|
||||||
|
@ -246,8 +228,10 @@ describe("<ForgotPassword>", () => {
|
||||||
await typeIntoField("Confirm new password", testPassword + "asd");
|
await typeIntoField("Confirm new password", testPassword + "asd");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should show an info about that", () => {
|
it("should show an info about that", async () => {
|
||||||
expect(screen.getByText("New passwords must match each other.")).toBeInTheDocument();
|
await expect(
|
||||||
|
screen.findByText("New passwords must match each other."),
|
||||||
|
).resolves.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -284,7 +268,7 @@ describe("<ForgotPassword>", () => {
|
||||||
await click(screen.getByText("Reset password"));
|
await click(screen.getByText("Reset password"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should send the new password (once)", () => {
|
it("should send the new password (once)", async () => {
|
||||||
expect(client.setPassword).toHaveBeenCalledWith(
|
expect(client.setPassword).toHaveBeenCalledWith(
|
||||||
{
|
{
|
||||||
type: "m.login.email.identity",
|
type: "m.login.email.identity",
|
||||||
|
@ -297,19 +281,15 @@ describe("<ForgotPassword>", () => {
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
// be sure that the next attempt to set the password would have been sent
|
|
||||||
jest.advanceTimersByTime(3000);
|
|
||||||
// it should not retry to set the password
|
// it should not retry to set the password
|
||||||
expect(client.setPassword).toHaveBeenCalledTimes(1);
|
await waitFor(() => expect(client.setPassword).toHaveBeenCalledTimes(1));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("and submitting it", () => {
|
describe("and submitting it", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await click(screen.getByText("Reset password"));
|
await click(screen.getByText("Reset password"));
|
||||||
await waitEnoughCyclesForModal({
|
await waitEnoughCyclesForModal();
|
||||||
useFakeTimers: true,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should send the new password and show the click validation link dialog", async () => {
|
it("should send the new password and show the click validation link dialog", async () => {
|
||||||
|
@ -367,23 +347,22 @@ describe("<ForgotPassword>", () => {
|
||||||
expect(screen.queryByText("Enter your email to reset password")).toBeInTheDocument();
|
expect(screen.queryByText("Enter your email to reset password")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("and validating the link from the mail", () => {
|
describe("and validating the link from the mail", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
mocked(client.setPassword).mockResolvedValue({});
|
mocked(client.setPassword).mockResolvedValue({});
|
||||||
// be sure the next set password attempt was sent
|
await click(screen.getByText("Reset password"));
|
||||||
jest.advanceTimersByTime(3000);
|
// flush promises for the modal to disappear
|
||||||
// quad flush promises for the modal to disappear
|
await waitEnoughCyclesForModal();
|
||||||
await flushPromisesWithFakeTimers();
|
await waitEnoughCyclesForModal();
|
||||||
await flushPromisesWithFakeTimers();
|
});
|
||||||
await flushPromisesWithFakeTimers();
|
|
||||||
await flushPromisesWithFakeTimers();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should display the confirm reset view and now show the dialog", () => {
|
it("should display the confirm reset view and now show the dialog", async () => {
|
||||||
expect(screen.queryByText("Your password has been reset.")).toBeInTheDocument();
|
await expect(
|
||||||
expect(screen.queryByText("Verify your email to continue")).not.toBeInTheDocument();
|
screen.findByText("Your password has been reset."),
|
||||||
});
|
).resolves.toBeInTheDocument();
|
||||||
|
expect(screen.queryByText("Verify your email to continue")).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -391,9 +370,6 @@ describe("<ForgotPassword>", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await click(screen.getByText("Sign out of all devices"));
|
await click(screen.getByText("Sign out of all devices"));
|
||||||
await click(screen.getByText("Reset password"));
|
await click(screen.getByText("Reset password"));
|
||||||
await waitEnoughCyclesForModal({
|
|
||||||
useFakeTimers: true,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should show the sign out warning dialog", async () => {
|
it("should show the sign out warning dialog", async () => {
|
||||||
|
|
|
@ -239,7 +239,7 @@ describe("Spotlight Dialog", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should call getVisibleRooms with MSC3946 dynamic room predecessors", async () => {
|
it("should call getVisibleRooms with MSC3946 dynamic room predecessors", async () => {
|
||||||
render(<SpotlightDialog onFinished={() => null} />, { legacyRoot: false });
|
render(<SpotlightDialog onFinished={() => null} />);
|
||||||
jest.advanceTimersByTime(200);
|
jest.advanceTimersByTime(200);
|
||||||
await flushPromisesWithFakeTimers();
|
await flushPromisesWithFakeTimers();
|
||||||
expect(mockedClient.getVisibleRooms).toHaveBeenCalledWith(true);
|
expect(mockedClient.getVisibleRooms).toHaveBeenCalledWith(true);
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { mocked, MockedObject } from "jest-mock";
|
||||||
import { MatrixClient, MatrixError } from "matrix-js-sdk/src/matrix";
|
import { MatrixClient, MatrixError } from "matrix-js-sdk/src/matrix";
|
||||||
import { sleep } from "matrix-js-sdk/src/utils";
|
import { sleep } from "matrix-js-sdk/src/utils";
|
||||||
|
|
||||||
import { filterConsole, stubClient } from "../../../../../test-utils";
|
import { filterConsole, flushPromises, stubClient } from "../../../../../test-utils";
|
||||||
import CreateSecretStorageDialog from "../../../../../../src/async-components/views/dialogs/security/CreateSecretStorageDialog";
|
import CreateSecretStorageDialog from "../../../../../../src/async-components/views/dialogs/security/CreateSecretStorageDialog";
|
||||||
|
|
||||||
describe("CreateSecretStorageDialog", () => {
|
describe("CreateSecretStorageDialog", () => {
|
||||||
|
@ -125,6 +125,7 @@ describe("CreateSecretStorageDialog", () => {
|
||||||
resetFunctionCallLog.push("resetKeyBackup");
|
resetFunctionCallLog.push("resetKeyBackup");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await flushPromises();
|
||||||
result.getByRole("button", { name: "Continue" }).click();
|
result.getByRole("button", { name: "Continue" }).click();
|
||||||
|
|
||||||
await result.findByText("Your keys are now being backed up from this device.");
|
await result.findByText("Your keys are now being backed up from this device.");
|
||||||
|
|
|
@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { screen, fireEvent, render, waitFor } from "jest-matrix-react";
|
import { screen, fireEvent, render, waitFor, act } from "jest-matrix-react";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
import { Crypto, IMegolmSessionData } from "matrix-js-sdk/src/matrix";
|
import { Crypto, IMegolmSessionData } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
|
@ -23,12 +23,12 @@ describe("ExportE2eKeysDialog", () => {
|
||||||
expect(asFragment()).toMatchSnapshot();
|
expect(asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should have disabled submit button initially", () => {
|
it("should have disabled submit button initially", async () => {
|
||||||
const cli = createTestClient();
|
const cli = createTestClient();
|
||||||
const onFinished = jest.fn();
|
const onFinished = jest.fn();
|
||||||
const { container } = render(<ExportE2eKeysDialog matrixClient={cli} onFinished={onFinished} />);
|
const { container } = render(<ExportE2eKeysDialog matrixClient={cli} onFinished={onFinished} />);
|
||||||
fireEvent.click(container.querySelector("[type=submit]")!);
|
await act(() => fireEvent.click(container.querySelector("[type=submit]")!));
|
||||||
expect(screen.getByText("Enter passphrase")).toBeInTheDocument();
|
expect(screen.getByLabelText("Enter passphrase")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should complain about weak passphrases", async () => {
|
it("should complain about weak passphrases", async () => {
|
||||||
|
@ -38,7 +38,7 @@ describe("ExportE2eKeysDialog", () => {
|
||||||
const { container } = render(<ExportE2eKeysDialog matrixClient={cli} onFinished={onFinished} />);
|
const { container } = render(<ExportE2eKeysDialog matrixClient={cli} onFinished={onFinished} />);
|
||||||
const input = screen.getByLabelText("Enter passphrase");
|
const input = screen.getByLabelText("Enter passphrase");
|
||||||
await userEvent.type(input, "password");
|
await userEvent.type(input, "password");
|
||||||
fireEvent.click(container.querySelector("[type=submit]")!);
|
await act(() => fireEvent.click(container.querySelector("[type=submit]")!));
|
||||||
await expect(screen.findByText("This is a top-10 common password")).resolves.toBeInTheDocument();
|
await expect(screen.findByText("This is a top-10 common password")).resolves.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ describe("ExportE2eKeysDialog", () => {
|
||||||
const { container } = render(<ExportE2eKeysDialog matrixClient={cli} onFinished={onFinished} />);
|
const { container } = render(<ExportE2eKeysDialog matrixClient={cli} onFinished={onFinished} />);
|
||||||
await userEvent.type(screen.getByLabelText("Enter passphrase"), "ThisIsAMoreSecurePW123$$");
|
await userEvent.type(screen.getByLabelText("Enter passphrase"), "ThisIsAMoreSecurePW123$$");
|
||||||
await userEvent.type(screen.getByLabelText("Confirm passphrase"), "ThisIsAMoreSecurePW124$$");
|
await userEvent.type(screen.getByLabelText("Confirm passphrase"), "ThisIsAMoreSecurePW124$$");
|
||||||
fireEvent.click(container.querySelector("[type=submit]")!);
|
await act(() => fireEvent.click(container.querySelector("[type=submit]")!));
|
||||||
await expect(screen.findByText("Passphrases must match")).resolves.toBeInTheDocument();
|
await expect(screen.findByText("Passphrases must match")).resolves.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ describe("ExportE2eKeysDialog", () => {
|
||||||
const { container } = render(<ExportE2eKeysDialog matrixClient={cli} onFinished={jest.fn()} />);
|
const { container } = render(<ExportE2eKeysDialog matrixClient={cli} onFinished={jest.fn()} />);
|
||||||
await userEvent.type(screen.getByLabelText("Enter passphrase"), passphrase);
|
await userEvent.type(screen.getByLabelText("Enter passphrase"), passphrase);
|
||||||
await userEvent.type(screen.getByLabelText("Confirm passphrase"), passphrase);
|
await userEvent.type(screen.getByLabelText("Confirm passphrase"), passphrase);
|
||||||
fireEvent.click(container.querySelector("[type=submit]")!);
|
await act(() => fireEvent.click(container.querySelector("[type=submit]")!));
|
||||||
|
|
||||||
// Then it exports keys and encrypts them
|
// Then it exports keys and encrypts them
|
||||||
await waitFor(() => expect(exportRoomKeysAsJson).toHaveBeenCalled());
|
await waitFor(() => expect(exportRoomKeysAsJson).toHaveBeenCalled());
|
||||||
|
|
|
@ -10,7 +10,7 @@ import React from "react";
|
||||||
import { Room, MatrixClient } from "matrix-js-sdk/src/matrix";
|
import { Room, MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||||
import { ClientWidgetApi, IWidget, MatrixWidgetType } from "matrix-widget-api";
|
import { ClientWidgetApi, IWidget, MatrixWidgetType } from "matrix-widget-api";
|
||||||
import { Optional } from "matrix-events-sdk";
|
import { Optional } from "matrix-events-sdk";
|
||||||
import { act, render, RenderResult } from "jest-matrix-react";
|
import { act, render, RenderResult, waitForElementToBeRemoved, waitFor } from "jest-matrix-react";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
import {
|
import {
|
||||||
ApprovalOpts,
|
ApprovalOpts,
|
||||||
|
@ -29,7 +29,6 @@ import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext
|
||||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||||
import { RightPanelPhases } from "../../../../../src/stores/right-panel/RightPanelStorePhases";
|
import { RightPanelPhases } from "../../../../../src/stores/right-panel/RightPanelStorePhases";
|
||||||
import RightPanelStore from "../../../../../src/stores/right-panel/RightPanelStore";
|
import RightPanelStore from "../../../../../src/stores/right-panel/RightPanelStore";
|
||||||
import { UPDATE_EVENT } from "../../../../../src/stores/AsyncStore";
|
|
||||||
import WidgetStore, { IApp } from "../../../../../src/stores/WidgetStore";
|
import WidgetStore, { IApp } from "../../../../../src/stores/WidgetStore";
|
||||||
import ActiveWidgetStore from "../../../../../src/stores/ActiveWidgetStore";
|
import ActiveWidgetStore from "../../../../../src/stores/ActiveWidgetStore";
|
||||||
import AppTile from "../../../../../src/components/views/elements/AppTile";
|
import AppTile from "../../../../../src/components/views/elements/AppTile";
|
||||||
|
@ -59,16 +58,6 @@ describe("AppTile", () => {
|
||||||
let app1: IApp;
|
let app1: IApp;
|
||||||
let app2: IApp;
|
let app2: IApp;
|
||||||
|
|
||||||
const waitForRps = (roomId: string) =>
|
|
||||||
new Promise<void>((resolve) => {
|
|
||||||
const update = () => {
|
|
||||||
if (RightPanelStore.instance.currentCardForRoom(roomId).phase !== RightPanelPhases.Widget) return;
|
|
||||||
RightPanelStore.instance.off(UPDATE_EVENT, update);
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
RightPanelStore.instance.on(UPDATE_EVENT, update);
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
stubClient();
|
stubClient();
|
||||||
cli = MatrixClientPeg.safeGet();
|
cli = MatrixClientPeg.safeGet();
|
||||||
|
@ -160,29 +149,28 @@ describe("AppTile", () => {
|
||||||
/>
|
/>
|
||||||
</MatrixClientContext.Provider>,
|
</MatrixClientContext.Provider>,
|
||||||
);
|
);
|
||||||
// Wait for RPS room 1 updates to fire
|
act(() =>
|
||||||
const rpsUpdated = waitForRps("r1");
|
dis.dispatch({
|
||||||
dis.dispatch({
|
action: Action.ViewRoom,
|
||||||
action: Action.ViewRoom,
|
room_id: "r1",
|
||||||
room_id: "r1",
|
}),
|
||||||
});
|
);
|
||||||
await rpsUpdated;
|
|
||||||
|
|
||||||
expect(renderResult.getByText("Example 1")).toBeInTheDocument();
|
await expect(renderResult.findByText("Example 1")).resolves.toBeInTheDocument();
|
||||||
expect(ActiveWidgetStore.instance.isLive("1", "r1")).toBe(true);
|
expect(ActiveWidgetStore.instance.isLive("1", "r1")).toBe(true);
|
||||||
|
|
||||||
const { container, asFragment } = renderResult;
|
const { asFragment } = renderResult;
|
||||||
expect(container.getElementsByClassName("mx_Spinner").length).toBeTruthy();
|
|
||||||
expect(asFragment()).toMatchSnapshot();
|
expect(asFragment()).toMatchSnapshot();
|
||||||
|
|
||||||
// We want to verify that as we change to room 2, we should close the
|
// We want to verify that as we change to room 2, we should close the
|
||||||
// right panel and destroy the widget.
|
// right panel and destroy the widget.
|
||||||
|
|
||||||
// Switch to room 2
|
// Switch to room 2
|
||||||
dis.dispatch({
|
act(() =>
|
||||||
action: Action.ViewRoom,
|
dis.dispatch({
|
||||||
room_id: "r2",
|
action: Action.ViewRoom,
|
||||||
});
|
room_id: "r2",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
renderResult.rerender(
|
renderResult.rerender(
|
||||||
<MatrixClientContext.Provider value={cli}>
|
<MatrixClientContext.Provider value={cli}>
|
||||||
|
@ -233,16 +221,17 @@ describe("AppTile", () => {
|
||||||
/>
|
/>
|
||||||
</MatrixClientContext.Provider>,
|
</MatrixClientContext.Provider>,
|
||||||
);
|
);
|
||||||
// Wait for RPS room 1 updates to fire
|
act(() =>
|
||||||
const rpsUpdated1 = waitForRps("r1");
|
dis.dispatch({
|
||||||
dis.dispatch({
|
action: Action.ViewRoom,
|
||||||
action: Action.ViewRoom,
|
room_id: "r1",
|
||||||
room_id: "r1",
|
}),
|
||||||
});
|
);
|
||||||
await rpsUpdated1;
|
|
||||||
|
|
||||||
expect(ActiveWidgetStore.instance.isLive("1", "r1")).toBe(true);
|
await waitFor(() => {
|
||||||
expect(ActiveWidgetStore.instance.isLive("1", "r2")).toBe(false);
|
expect(ActiveWidgetStore.instance.isLive("1", "r1")).toBe(true);
|
||||||
|
expect(ActiveWidgetStore.instance.isLive("1", "r2")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((name, roomId) => {
|
jest.spyOn(SettingsStore, "getValue").mockImplementation((name, roomId) => {
|
||||||
if (name === "RightPanel.phases") {
|
if (name === "RightPanel.phases") {
|
||||||
|
@ -263,13 +252,13 @@ describe("AppTile", () => {
|
||||||
}
|
}
|
||||||
return realGetValue(name, roomId);
|
return realGetValue(name, roomId);
|
||||||
});
|
});
|
||||||
// Wait for RPS room 2 updates to fire
|
|
||||||
const rpsUpdated2 = waitForRps("r2");
|
|
||||||
// Switch to room 2
|
// Switch to room 2
|
||||||
dis.dispatch({
|
act(() =>
|
||||||
action: Action.ViewRoom,
|
dis.dispatch({
|
||||||
room_id: "r2",
|
action: Action.ViewRoom,
|
||||||
});
|
room_id: "r2",
|
||||||
|
}),
|
||||||
|
);
|
||||||
renderResult.rerender(
|
renderResult.rerender(
|
||||||
<MatrixClientContext.Provider value={cli}>
|
<MatrixClientContext.Provider value={cli}>
|
||||||
<RightPanel
|
<RightPanel
|
||||||
|
@ -279,10 +268,11 @@ describe("AppTile", () => {
|
||||||
/>
|
/>
|
||||||
</MatrixClientContext.Provider>,
|
</MatrixClientContext.Provider>,
|
||||||
);
|
);
|
||||||
await rpsUpdated2;
|
|
||||||
|
|
||||||
expect(ActiveWidgetStore.instance.isLive("1", "r1")).toBe(false);
|
await waitFor(() => {
|
||||||
expect(ActiveWidgetStore.instance.isLive("1", "r2")).toBe(true);
|
expect(ActiveWidgetStore.instance.isLive("1", "r1")).toBe(false);
|
||||||
|
expect(ActiveWidgetStore.instance.isLive("1", "r2")).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("preserves non-persisted widget on container move", async () => {
|
it("preserves non-persisted widget on container move", async () => {
|
||||||
|
@ -345,7 +335,7 @@ describe("AppTile", () => {
|
||||||
let renderResult: RenderResult;
|
let renderResult: RenderResult;
|
||||||
let moveToContainerSpy: jest.SpyInstance<void, [room: Room, widget: IWidget, toContainer: Container]>;
|
let moveToContainerSpy: jest.SpyInstance<void, [room: Room, widget: IWidget, toContainer: Container]>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
renderResult = render(
|
renderResult = render(
|
||||||
<MatrixClientContext.Provider value={cli}>
|
<MatrixClientContext.Provider value={cli}>
|
||||||
<AppTile key={app1.id} app={app1} room={r1} />
|
<AppTile key={app1.id} app={app1} room={r1} />
|
||||||
|
@ -353,12 +343,12 @@ describe("AppTile", () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
moveToContainerSpy = jest.spyOn(WidgetLayoutStore.instance, "moveToContainer");
|
moveToContainerSpy = jest.spyOn(WidgetLayoutStore.instance, "moveToContainer");
|
||||||
|
await waitForElementToBeRemoved(() => renderResult.queryByRole("progressbar"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render", () => {
|
it("should render", () => {
|
||||||
const { container, asFragment } = renderResult;
|
const { asFragment } = renderResult;
|
||||||
|
|
||||||
expect(container.querySelector(".mx_Spinner")).toBeFalsy(); // Assert that the spinner is gone
|
|
||||||
expect(asFragment()).toMatchSnapshot(); // Take a snapshot of the pinned widget
|
expect(asFragment()).toMatchSnapshot(); // Take a snapshot of the pinned widget
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -459,18 +449,19 @@ describe("AppTile", () => {
|
||||||
describe("for a persistent app", () => {
|
describe("for a persistent app", () => {
|
||||||
let renderResult: RenderResult;
|
let renderResult: RenderResult;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
renderResult = render(
|
renderResult = render(
|
||||||
<MatrixClientContext.Provider value={cli}>
|
<MatrixClientContext.Provider value={cli}>
|
||||||
<AppTile key={app1.id} app={app1} fullWidth={true} room={r1} miniMode={true} showMenubar={false} />
|
<AppTile key={app1.id} app={app1} fullWidth={true} room={r1} miniMode={true} showMenubar={false} />
|
||||||
</MatrixClientContext.Provider>,
|
</MatrixClientContext.Provider>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await waitForElementToBeRemoved(() => renderResult.queryByRole("progressbar"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render", () => {
|
it("should render", async () => {
|
||||||
const { container, asFragment } = renderResult;
|
const { asFragment } = renderResult;
|
||||||
|
|
||||||
expect(container.querySelector(".mx_Spinner")).toBeFalsy();
|
|
||||||
expect(asFragment()).toMatchSnapshot();
|
expect(asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { act, render, RenderResult, screen } from "jest-matrix-react";
|
import { render, RenderResult, screen } from "jest-matrix-react";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
import { mocked, Mocked } from "jest-mock";
|
import { mocked, Mocked } from "jest-mock";
|
||||||
import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
||||||
|
@ -214,9 +214,7 @@ describe("<Pill>", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// wait for profile query via API
|
// wait for profile query via API
|
||||||
await act(async () => {
|
await flushPromises();
|
||||||
await flushPromises();
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(renderResult.asFragment()).toMatchSnapshot();
|
expect(renderResult.asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
@ -228,9 +226,7 @@ describe("<Pill>", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// wait for profile query via API
|
// wait for profile query via API
|
||||||
await act(async () => {
|
await flushPromises();
|
||||||
await flushPromises();
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(renderResult.asFragment()).toMatchSnapshot();
|
expect(renderResult.asFragment()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
|
@ -60,29 +60,9 @@ exports[`AppTile destroys non-persisted right panel widget on room change 1`] =
|
||||||
id="1"
|
id="1"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_AppTileBody mx_AppTileBody--large"
|
class="mx_AppTile_persistedWrapper"
|
||||||
>
|
>
|
||||||
<div
|
<div />
|
||||||
class="mx_AppTileBody_fadeInSpinner"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_Spinner"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mx_Spinner_Msg"
|
|
||||||
>
|
|
||||||
Loading…
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
aria-label="Loading…"
|
|
||||||
class="mx_Spinner_icon"
|
|
||||||
data-testid="spinner"
|
|
||||||
role="progressbar"
|
|
||||||
style="width: 32px; height: 32px;"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { createRef } from "react";
|
import React, { createRef } from "react";
|
||||||
import { render, waitFor } from "jest-matrix-react";
|
import { render, waitFor, act } from "jest-matrix-react";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
|
|
||||||
import EmojiPicker from "../../../../../src/components/views/emojipicker/EmojiPicker";
|
import EmojiPicker from "../../../../../src/components/views/emojipicker/EmojiPicker";
|
||||||
|
@ -27,12 +27,12 @@ describe("EmojiPicker", function () {
|
||||||
|
|
||||||
// Apply a filter and assert that the HTML has changed
|
// Apply a filter and assert that the HTML has changed
|
||||||
//@ts-ignore private access
|
//@ts-ignore private access
|
||||||
ref.current!.onChangeFilter("test");
|
act(() => ref.current!.onChangeFilter("test"));
|
||||||
expect(beforeHtml).not.toEqual(container.innerHTML);
|
expect(beforeHtml).not.toEqual(container.innerHTML);
|
||||||
|
|
||||||
// Clear the filter and assert that the HTML matches what it was before filtering
|
// Clear the filter and assert that the HTML matches what it was before filtering
|
||||||
//@ts-ignore private access
|
//@ts-ignore private access
|
||||||
ref.current!.onChangeFilter("");
|
act(() => ref.current!.onChangeFilter(""));
|
||||||
await waitFor(() => expect(beforeHtml).toEqual(container.innerHTML));
|
await waitFor(() => expect(beforeHtml).toEqual(container.innerHTML));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ describe("EmojiPicker", function () {
|
||||||
const ep = new EmojiPicker({ onChoose: (str: string) => false, onFinished: jest.fn() });
|
const ep = new EmojiPicker({ onChoose: (str: string) => false, onFinished: jest.fn() });
|
||||||
|
|
||||||
//@ts-ignore private access
|
//@ts-ignore private access
|
||||||
ep.onChangeFilter("heart");
|
act(() => ep.onChangeFilter("heart"));
|
||||||
|
|
||||||
//@ts-ignore private access
|
//@ts-ignore private access
|
||||||
expect(ep.memoizedDataByCategory["people"][0].shortcodes[0]).toEqual("heart");
|
expect(ep.memoizedDataByCategory["people"][0].shortcodes[0]).toEqual("heart");
|
||||||
|
|
|
@ -139,7 +139,7 @@ describe("<LocationShareMenu />", () => {
|
||||||
const [, onGeolocateCallback] = mocked(mockGeolocate.on).mock.calls.find(([event]) => event === "geolocate")!;
|
const [, onGeolocateCallback] = mocked(mockGeolocate.on).mock.calls.find(([event]) => event === "geolocate")!;
|
||||||
|
|
||||||
// set the location
|
// set the location
|
||||||
onGeolocateCallback(position);
|
act(() => onGeolocateCallback(position));
|
||||||
};
|
};
|
||||||
|
|
||||||
const setLocationClick = () => {
|
const setLocationClick = () => {
|
||||||
|
@ -151,7 +151,7 @@ describe("<LocationShareMenu />", () => {
|
||||||
lngLat: { lng: position.coords.longitude, lat: position.coords.latitude },
|
lngLat: { lng: position.coords.longitude, lat: position.coords.latitude },
|
||||||
} as unknown as maplibregl.MapMouseEvent;
|
} as unknown as maplibregl.MapMouseEvent;
|
||||||
// set the location
|
// set the location
|
||||||
onMapClickCallback(event);
|
act(() => onMapClickCallback(event));
|
||||||
};
|
};
|
||||||
|
|
||||||
const shareTypeLabels: Record<LocationShareType, string> = {
|
const shareTypeLabels: Record<LocationShareType, string> = {
|
||||||
|
|
|
@ -48,6 +48,7 @@ describe("DateSeparator", () => {
|
||||||
<MatrixClientContext.Provider value={mockClient}>
|
<MatrixClientContext.Provider value={mockClient}>
|
||||||
<DateSeparator {...defaultProps} {...props} />
|
<DateSeparator {...defaultProps} {...props} />
|
||||||
</MatrixClientContext.Provider>,
|
</MatrixClientContext.Provider>,
|
||||||
|
{ legacyRoot: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
type TestCase = [string, number, string];
|
type TestCase = [string, number, string];
|
||||||
|
@ -264,10 +265,12 @@ describe("DateSeparator", () => {
|
||||||
fireEvent.click(jumpToLastWeekButton);
|
fireEvent.click(jumpToLastWeekButton);
|
||||||
|
|
||||||
// Expect error to be shown. We have to wait for the UI to transition.
|
// Expect error to be shown. We have to wait for the UI to transition.
|
||||||
expect(await screen.findByTestId("jump-to-date-error-content")).toBeInTheDocument();
|
await expect(screen.findByTestId("jump-to-date-error-content")).resolves.toBeInTheDocument();
|
||||||
|
|
||||||
// Expect an option to submit debug logs to be shown when a non-network error occurs
|
// Expect an option to submit debug logs to be shown when a non-network error occurs
|
||||||
expect(await screen.findByTestId("jump-to-date-error-submit-debug-logs-button")).toBeInTheDocument();
|
await expect(
|
||||||
|
screen.findByTestId("jump-to-date-error-submit-debug-logs-button"),
|
||||||
|
).resolves.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
[
|
[
|
||||||
|
@ -280,19 +283,20 @@ describe("DateSeparator", () => {
|
||||||
),
|
),
|
||||||
].forEach((fakeError) => {
|
].forEach((fakeError) => {
|
||||||
it(`should show error dialog without submit debug logs option when networking error (${fakeError.name}) occurs`, async () => {
|
it(`should show error dialog without submit debug logs option when networking error (${fakeError.name}) occurs`, async () => {
|
||||||
|
// Try to jump to "last week" but we want a network error to occur
|
||||||
|
mockClient.timestampToEvent.mockRejectedValue(fakeError);
|
||||||
|
|
||||||
// Render the component
|
// Render the component
|
||||||
getComponent();
|
getComponent();
|
||||||
|
|
||||||
// Open the jump to date context menu
|
// Open the jump to date context menu
|
||||||
fireEvent.click(screen.getByTestId("jump-to-date-separator-button"));
|
fireEvent.click(screen.getByTestId("jump-to-date-separator-button"));
|
||||||
|
|
||||||
// Try to jump to "last week" but we want a network error to occur
|
|
||||||
mockClient.timestampToEvent.mockRejectedValue(fakeError);
|
|
||||||
const jumpToLastWeekButton = await screen.findByTestId("jump-to-date-last-week");
|
const jumpToLastWeekButton = await screen.findByTestId("jump-to-date-last-week");
|
||||||
fireEvent.click(jumpToLastWeekButton);
|
fireEvent.click(jumpToLastWeekButton);
|
||||||
|
|
||||||
// Expect error to be shown. We have to wait for the UI to transition.
|
// Expect error to be shown. We have to wait for the UI to transition.
|
||||||
expect(await screen.findByTestId("jump-to-date-error-content")).toBeInTheDocument();
|
await expect(screen.findByTestId("jump-to-date-error-content")).resolves.toBeInTheDocument();
|
||||||
|
|
||||||
// The submit debug logs option should *NOT* be shown for network errors.
|
// The submit debug logs option should *NOT* be shown for network errors.
|
||||||
//
|
//
|
||||||
|
|
|
@ -27,9 +27,9 @@ const renderEncryptionEvent = (client: MatrixClient, event: MatrixEvent) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkTexts = (title: string, subTitle: string) => {
|
const checkTexts = async (title: string, subTitle: string) => {
|
||||||
screen.getByText(title);
|
await screen.findByText(title);
|
||||||
screen.getByText(subTitle);
|
await screen.findByText(subTitle);
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("EncryptionEvent", () => {
|
describe("EncryptionEvent", () => {
|
||||||
|
@ -120,9 +120,9 @@ describe("EncryptionEvent", () => {
|
||||||
renderEncryptionEvent(client, event);
|
renderEncryptionEvent(client, event);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should show the expected texts", () => {
|
it("should show the expected texts", async () => {
|
||||||
expect(client.getCrypto()!.isEncryptionEnabledInRoom).toHaveBeenCalledWith(roomId);
|
expect(client.getCrypto()!.isEncryptionEnabledInRoom).toHaveBeenCalledWith(roomId);
|
||||||
checkTexts("Encryption enabled", "Messages in this chat will be end-to-end encrypted.");
|
await checkTexts("Encryption enabled", "Messages in this chat will be end-to-end encrypted.");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { fireEvent, render, RenderResult, waitFor } from "jest-matrix-react";
|
import { act, fireEvent, render, RenderResult, waitForElementToBeRemoved, waitFor } from "jest-matrix-react";
|
||||||
import {
|
import {
|
||||||
MatrixEvent,
|
MatrixEvent,
|
||||||
Relations,
|
Relations,
|
||||||
|
@ -226,7 +226,7 @@ describe("MPollBody", () => {
|
||||||
clickOption(renderResult, "pizza");
|
clickOption(renderResult, "pizza");
|
||||||
|
|
||||||
// When a new vote from me comes in
|
// When a new vote from me comes in
|
||||||
await room.processPollEvents([responseEvent("@me:example.com", "wings", 101)]);
|
await act(() => room.processPollEvents([responseEvent("@me:example.com", "wings", 101)]));
|
||||||
|
|
||||||
// Then the new vote is counted, not the old one
|
// Then the new vote is counted, not the old one
|
||||||
expect(votesCount(renderResult, "pizza")).toBe("0 votes");
|
expect(votesCount(renderResult, "pizza")).toBe("0 votes");
|
||||||
|
@ -255,7 +255,7 @@ describe("MPollBody", () => {
|
||||||
clickOption(renderResult, "pizza");
|
clickOption(renderResult, "pizza");
|
||||||
|
|
||||||
// When a new vote from someone else comes in
|
// When a new vote from someone else comes in
|
||||||
await room.processPollEvents([responseEvent("@xx:example.com", "wings", 101)]);
|
await act(() => room.processPollEvents([responseEvent("@xx:example.com", "wings", 101)]));
|
||||||
|
|
||||||
// Then my vote is still for pizza
|
// Then my vote is still for pizza
|
||||||
// NOTE: the new event does not affect the counts for other people -
|
// NOTE: the new event does not affect the counts for other people -
|
||||||
|
@ -596,11 +596,13 @@ describe("MPollBody", () => {
|
||||||
];
|
];
|
||||||
const renderResult = await newMPollBody(votes, ends);
|
const renderResult = await newMPollBody(votes, ends);
|
||||||
|
|
||||||
expect(endedVotesCount(renderResult, "pizza")).toBe("2 votes");
|
await waitFor(() => {
|
||||||
expect(endedVotesCount(renderResult, "poutine")).toBe("0 votes");
|
expect(endedVotesCount(renderResult, "pizza")).toBe("2 votes");
|
||||||
expect(endedVotesCount(renderResult, "italian")).toBe("0 votes");
|
expect(endedVotesCount(renderResult, "poutine")).toBe("0 votes");
|
||||||
expect(endedVotesCount(renderResult, "wings")).toBe('<div class="mx_PollOption_winnerIcon"></div>3 votes');
|
expect(endedVotesCount(renderResult, "italian")).toBe("0 votes");
|
||||||
expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Final result based on 5 votes");
|
expect(endedVotesCount(renderResult, "wings")).toBe('<div class="mx_PollOption_winnerIcon"></div>3 votes');
|
||||||
|
expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Final result based on 5 votes");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("ignores votes that arrived after the first end poll event", async () => {
|
it("ignores votes that arrived after the first end poll event", async () => {
|
||||||
|
@ -890,12 +892,14 @@ async function newMPollBody(
|
||||||
room_id: "#myroom:example.com",
|
room_id: "#myroom:example.com",
|
||||||
content: newPollStart(answers, undefined, disclosed),
|
content: newPollStart(answers, undefined, disclosed),
|
||||||
});
|
});
|
||||||
const result = newMPollBodyFromEvent(mxEvent, relationEvents, endEvents);
|
const prom = newMPollBodyFromEvent(mxEvent, relationEvents, endEvents);
|
||||||
// flush promises from loading relations
|
|
||||||
if (waitForResponsesLoad) {
|
if (waitForResponsesLoad) {
|
||||||
await flushPromises();
|
const result = await prom;
|
||||||
|
if (result.queryByTestId("spinner")) {
|
||||||
|
await waitForElementToBeRemoved(() => result.getByTestId("spinner"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return prom;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMPollBodyPropsFromEvent(mxEvent: MatrixEvent): IBodyProps {
|
function getMPollBodyPropsFromEvent(mxEvent: MatrixEvent): IBodyProps {
|
||||||
|
|
|
@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { render, waitFor } from "jest-matrix-react";
|
import { render, waitFor, waitForElementToBeRemoved } from "jest-matrix-react";
|
||||||
import { EventTimeline, MatrixEvent, Room, M_TEXT } from "matrix-js-sdk/src/matrix";
|
import { EventTimeline, MatrixEvent, Room, M_TEXT } from "matrix-js-sdk/src/matrix";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
|
||||||
|
@ -127,6 +127,7 @@ describe("<MPollEndBody />", () => {
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
|
|
||||||
await waitFor(() => expect(getByRole("progressbar")).toBeInTheDocument());
|
await waitFor(() => expect(getByRole("progressbar")).toBeInTheDocument());
|
||||||
|
await waitForElementToBeRemoved(() => getByRole("progressbar"));
|
||||||
|
|
||||||
expect(mockClient.fetchRoomEvent).toHaveBeenCalledWith(roomId, pollStartEvent.getId());
|
expect(mockClient.fetchRoomEvent).toHaveBeenCalledWith(roomId, pollStartEvent.getId());
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { act, fireEvent, render } from "jest-matrix-react";
|
import { fireEvent, render } from "jest-matrix-react";
|
||||||
import { Filter, EventTimeline, Room, MatrixEvent, M_POLL_START } from "matrix-js-sdk/src/matrix";
|
import { Filter, EventTimeline, Room, MatrixEvent, M_POLL_START } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import { PollHistory } from "../../../../../../src/components/views/polls/pollHistory/PollHistory";
|
import { PollHistory } from "../../../../../../src/components/views/polls/pollHistory/PollHistory";
|
||||||
|
@ -110,7 +110,7 @@ describe("<PollHistory />", () => {
|
||||||
expect(getByText("Loading polls")).toBeInTheDocument();
|
expect(getByText("Loading polls")).toBeInTheDocument();
|
||||||
|
|
||||||
// flush filter creation request
|
// flush filter creation request
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
expect(liveTimeline.getPaginationToken).toHaveBeenCalledWith(EventTimeline.BACKWARDS);
|
expect(liveTimeline.getPaginationToken).toHaveBeenCalledWith(EventTimeline.BACKWARDS);
|
||||||
expect(mockClient.paginateEventTimeline).toHaveBeenCalledWith(liveTimeline, { backwards: true });
|
expect(mockClient.paginateEventTimeline).toHaveBeenCalledWith(liveTimeline, { backwards: true });
|
||||||
|
@ -140,7 +140,7 @@ describe("<PollHistory />", () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
// flush filter creation request
|
// flush filter creation request
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
// once per page
|
// once per page
|
||||||
expect(mockClient.paginateEventTimeline).toHaveBeenCalledTimes(3);
|
expect(mockClient.paginateEventTimeline).toHaveBeenCalledTimes(3);
|
||||||
|
|
||||||
|
@ -175,7 +175,7 @@ describe("<PollHistory />", () => {
|
||||||
|
|
||||||
it("renders a no polls message when there are no active polls in the room", async () => {
|
it("renders a no polls message when there are no active polls in the room", async () => {
|
||||||
const { getByText } = getComponent();
|
const { getByText } = getComponent();
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
expect(getByText("There are no active polls in this room")).toBeTruthy();
|
expect(getByText("There are no active polls in this room")).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
@ -199,7 +199,7 @@ describe("<PollHistory />", () => {
|
||||||
.mockReturnValueOnce("test-pagination-token-3");
|
.mockReturnValueOnce("test-pagination-token-3");
|
||||||
|
|
||||||
const { getByText } = getComponent();
|
const { getByText } = getComponent();
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
expect(mockClient.paginateEventTimeline).toHaveBeenCalledTimes(1);
|
expect(mockClient.paginateEventTimeline).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ describe("<PollHistory />", () => {
|
||||||
// load more polls button still in UI, with loader
|
// load more polls button still in UI, with loader
|
||||||
expect(getByText("Load more polls")).toMatchSnapshot();
|
expect(getByText("Load more polls")).toMatchSnapshot();
|
||||||
|
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
// no more spinner
|
// no more spinner
|
||||||
expect(getByText("Load more polls")).toMatchSnapshot();
|
expect(getByText("Load more polls")).toMatchSnapshot();
|
||||||
|
|
|
@ -91,7 +91,7 @@ exports[`<PollHistory /> renders a list of active polls when there are polls in
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
aria-labelledby=":ra:"
|
aria-labelledby=":rc:"
|
||||||
class="mx_PollListItem_content"
|
class="mx_PollListItem_content"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
|
@ -116,7 +116,7 @@ exports[`<PollHistory /> renders a list of active polls when there are polls in
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
aria-labelledby=":rg:"
|
aria-labelledby=":rh:"
|
||||||
class="mx_PollListItem_content"
|
class="mx_PollListItem_content"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
|
|
|
@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { fireEvent, render, screen, waitFor, cleanup, act, within } from "jest-matrix-react";
|
import { fireEvent, render, screen, cleanup, act, within } from "jest-matrix-react";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
import { Mocked, mocked } from "jest-mock";
|
import { Mocked, mocked } from "jest-mock";
|
||||||
import { Room, User, MatrixClient, RoomMember, MatrixEvent, EventType, Device } from "matrix-js-sdk/src/matrix";
|
import { Room, User, MatrixClient, RoomMember, MatrixEvent, EventType, Device } from "matrix-js-sdk/src/matrix";
|
||||||
|
@ -199,6 +199,7 @@ describe("<UserInfo />", () => {
|
||||||
|
|
||||||
return render(<UserInfo {...defaultProps} {...props} />, {
|
return render(<UserInfo {...defaultProps} {...props} />, {
|
||||||
wrapper: Wrapper,
|
wrapper: Wrapper,
|
||||||
|
legacyRoot: true,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -439,7 +440,7 @@ describe("<UserInfo />", () => {
|
||||||
|
|
||||||
it("renders a device list which can be expanded", async () => {
|
it("renders a device list which can be expanded", async () => {
|
||||||
renderComponent();
|
renderComponent();
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
// check the button exists with the expected text
|
// check the button exists with the expected text
|
||||||
const devicesButton = screen.getByRole("button", { name: "1 session" });
|
const devicesButton = screen.getByRole("button", { name: "1 session" });
|
||||||
|
@ -459,9 +460,9 @@ describe("<UserInfo />", () => {
|
||||||
verificationRequest,
|
verificationRequest,
|
||||||
room: mockRoom,
|
room: mockRoom,
|
||||||
});
|
});
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
await waitFor(() => expect(screen.getByRole("button", { name: "Verify" })).toBeInTheDocument());
|
await expect(screen.findByRole("button", { name: "Verify" })).resolves.toBeInTheDocument();
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -490,7 +491,7 @@ describe("<UserInfo />", () => {
|
||||||
mockCrypto.getUserDeviceInfo.mockResolvedValue(userDeviceMap);
|
mockCrypto.getUserDeviceInfo.mockResolvedValue(userDeviceMap);
|
||||||
|
|
||||||
renderComponent({ room: mockRoom });
|
renderComponent({ room: mockRoom });
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
// check the button exists with the expected text (the dehydrated device shouldn't be counted)
|
// check the button exists with the expected text (the dehydrated device shouldn't be counted)
|
||||||
const devicesButton = screen.getByRole("button", { name: "1 session" });
|
const devicesButton = screen.getByRole("button", { name: "1 session" });
|
||||||
|
@ -538,7 +539,7 @@ describe("<UserInfo />", () => {
|
||||||
} as DeviceVerificationStatus);
|
} as DeviceVerificationStatus);
|
||||||
|
|
||||||
renderComponent({ room: mockRoom });
|
renderComponent({ room: mockRoom });
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
// check the button exists with the expected text (the dehydrated device shouldn't be counted)
|
// check the button exists with the expected text (the dehydrated device shouldn't be counted)
|
||||||
const devicesButton = screen.getByRole("button", { name: "1 verified session" });
|
const devicesButton = screen.getByRole("button", { name: "1 verified session" });
|
||||||
|
@ -583,7 +584,7 @@ describe("<UserInfo />", () => {
|
||||||
mockCrypto.getUserVerificationStatus.mockResolvedValue(new UserVerificationStatus(true, true, true));
|
mockCrypto.getUserVerificationStatus.mockResolvedValue(new UserVerificationStatus(true, true, true));
|
||||||
|
|
||||||
renderComponent({ room: mockRoom });
|
renderComponent({ room: mockRoom });
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
// the dehydrated device should be shown as an unverified device, which means
|
// the dehydrated device should be shown as an unverified device, which means
|
||||||
// there should now be a button with the device id ...
|
// there should now be a button with the device id ...
|
||||||
|
@ -618,7 +619,7 @@ describe("<UserInfo />", () => {
|
||||||
mockCrypto.getUserDeviceInfo.mockResolvedValue(userDeviceMap);
|
mockCrypto.getUserDeviceInfo.mockResolvedValue(userDeviceMap);
|
||||||
|
|
||||||
renderComponent({ room: mockRoom });
|
renderComponent({ room: mockRoom });
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
// check the button exists with the expected text (the dehydrated device shouldn't be counted)
|
// check the button exists with the expected text (the dehydrated device shouldn't be counted)
|
||||||
const devicesButton = screen.getByRole("button", { name: "2 sessions" });
|
const devicesButton = screen.getByRole("button", { name: "2 sessions" });
|
||||||
|
@ -653,7 +654,7 @@ describe("<UserInfo />", () => {
|
||||||
room: mockRoom,
|
room: mockRoom,
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitFor(() => expect(screen.getByRole("button", { name: "Deactivate user" })).toBeInTheDocument());
|
await expect(screen.findByRole("button", { name: "Deactivate user" })).resolves.toBeInTheDocument();
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -666,7 +667,7 @@ describe("<UserInfo />", () => {
|
||||||
it("renders unverified user info", async () => {
|
it("renders unverified user info", async () => {
|
||||||
mockCrypto.getUserVerificationStatus.mockResolvedValue(new UserVerificationStatus(false, false, false));
|
mockCrypto.getUserVerificationStatus.mockResolvedValue(new UserVerificationStatus(false, false, false));
|
||||||
renderComponent({ room: mockRoom });
|
renderComponent({ room: mockRoom });
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
const userHeading = screen.getByRole("heading", { name: /@user:example.com/ });
|
const userHeading = screen.getByRole("heading", { name: /@user:example.com/ });
|
||||||
|
|
||||||
|
@ -677,7 +678,7 @@ describe("<UserInfo />", () => {
|
||||||
it("renders verified user info", async () => {
|
it("renders verified user info", async () => {
|
||||||
mockCrypto.getUserVerificationStatus.mockResolvedValue(new UserVerificationStatus(true, false, false));
|
mockCrypto.getUserVerificationStatus.mockResolvedValue(new UserVerificationStatus(true, false, false));
|
||||||
renderComponent({ room: mockRoom });
|
renderComponent({ room: mockRoom });
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
const userHeading = screen.getByRole("heading", { name: /@user:example.com/ });
|
const userHeading = screen.getByRole("heading", { name: /@user:example.com/ });
|
||||||
|
|
||||||
|
@ -768,7 +769,7 @@ describe("<DeviceItem />", () => {
|
||||||
|
|
||||||
it("with unverified user and device, displays button without a label", async () => {
|
it("with unverified user and device, displays button without a label", async () => {
|
||||||
renderComponent();
|
renderComponent();
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
expect(screen.getByRole("button", { name: device.displayName! })).toBeInTheDocument();
|
expect(screen.getByRole("button", { name: device.displayName! })).toBeInTheDocument();
|
||||||
expect(screen.queryByText(/trusted/i)).not.toBeInTheDocument();
|
expect(screen.queryByText(/trusted/i)).not.toBeInTheDocument();
|
||||||
|
@ -776,7 +777,7 @@ describe("<DeviceItem />", () => {
|
||||||
|
|
||||||
it("with verified user only, displays button with a 'Not trusted' label", async () => {
|
it("with verified user only, displays button with a 'Not trusted' label", async () => {
|
||||||
renderComponent({ isUserVerified: true });
|
renderComponent({ isUserVerified: true });
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
const button = screen.getByRole("button", { name: device.displayName });
|
const button = screen.getByRole("button", { name: device.displayName });
|
||||||
expect(button).toHaveTextContent(`${device.displayName}Not trusted`);
|
expect(button).toHaveTextContent(`${device.displayName}Not trusted`);
|
||||||
|
@ -785,7 +786,7 @@ describe("<DeviceItem />", () => {
|
||||||
it("with verified device only, displays no button without a label", async () => {
|
it("with verified device only, displays no button without a label", async () => {
|
||||||
setMockDeviceTrust(true);
|
setMockDeviceTrust(true);
|
||||||
renderComponent();
|
renderComponent();
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
expect(screen.getByText(device.displayName!)).toBeInTheDocument();
|
expect(screen.getByText(device.displayName!)).toBeInTheDocument();
|
||||||
expect(screen.queryByText(/trusted/)).not.toBeInTheDocument();
|
expect(screen.queryByText(/trusted/)).not.toBeInTheDocument();
|
||||||
|
@ -798,7 +799,7 @@ describe("<DeviceItem />", () => {
|
||||||
mockClient.getSafeUserId.mockReturnValueOnce(defaultUserId);
|
mockClient.getSafeUserId.mockReturnValueOnce(defaultUserId);
|
||||||
mockClient.getUserId.mockReturnValueOnce(defaultUserId);
|
mockClient.getUserId.mockReturnValueOnce(defaultUserId);
|
||||||
renderComponent();
|
renderComponent();
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
// set trust to be false for isVerified, true for isCrossSigningVerified
|
// set trust to be false for isVerified, true for isCrossSigningVerified
|
||||||
deferred.resolve({
|
deferred.resolve({
|
||||||
|
@ -814,7 +815,7 @@ describe("<DeviceItem />", () => {
|
||||||
it("with verified user and device, displays no button and a 'Trusted' label", async () => {
|
it("with verified user and device, displays no button and a 'Trusted' label", async () => {
|
||||||
setMockDeviceTrust(true);
|
setMockDeviceTrust(true);
|
||||||
renderComponent({ isUserVerified: true });
|
renderComponent({ isUserVerified: true });
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
expect(screen.queryByRole("button")).not.toBeInTheDocument();
|
expect(screen.queryByRole("button")).not.toBeInTheDocument();
|
||||||
expect(screen.getByText(device.displayName!)).toBeInTheDocument();
|
expect(screen.getByText(device.displayName!)).toBeInTheDocument();
|
||||||
|
@ -824,7 +825,7 @@ describe("<DeviceItem />", () => {
|
||||||
it("does not call verifyDevice if client.getUser returns null", async () => {
|
it("does not call verifyDevice if client.getUser returns null", async () => {
|
||||||
mockClient.getUser.mockReturnValueOnce(null);
|
mockClient.getUser.mockReturnValueOnce(null);
|
||||||
renderComponent();
|
renderComponent();
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
const button = screen.getByRole("button", { name: device.displayName! });
|
const button = screen.getByRole("button", { name: device.displayName! });
|
||||||
expect(button).toBeInTheDocument();
|
expect(button).toBeInTheDocument();
|
||||||
|
@ -839,7 +840,7 @@ describe("<DeviceItem />", () => {
|
||||||
// even more mocking
|
// even more mocking
|
||||||
mockClient.isGuest.mockReturnValueOnce(true);
|
mockClient.isGuest.mockReturnValueOnce(true);
|
||||||
renderComponent();
|
renderComponent();
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
const button = screen.getByRole("button", { name: device.displayName! });
|
const button = screen.getByRole("button", { name: device.displayName! });
|
||||||
expect(button).toBeInTheDocument();
|
expect(button).toBeInTheDocument();
|
||||||
|
@ -851,7 +852,7 @@ describe("<DeviceItem />", () => {
|
||||||
|
|
||||||
it("with display name", async () => {
|
it("with display name", async () => {
|
||||||
const { container } = renderComponent();
|
const { container } = renderComponent();
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
@ -859,7 +860,7 @@ describe("<DeviceItem />", () => {
|
||||||
it("without display name", async () => {
|
it("without display name", async () => {
|
||||||
const device = { deviceId: "deviceId" } as Device;
|
const device = { deviceId: "deviceId" } as Device;
|
||||||
const { container } = renderComponent({ device, userId: defaultUserId });
|
const { container } = renderComponent({ device, userId: defaultUserId });
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
@ -867,7 +868,7 @@ describe("<DeviceItem />", () => {
|
||||||
it("ambiguous display name", async () => {
|
it("ambiguous display name", async () => {
|
||||||
const device = { deviceId: "deviceId", ambiguous: true, displayName: "my display name" };
|
const device = { deviceId: "deviceId", ambiguous: true, displayName: "my display name" };
|
||||||
const { container } = renderComponent({ device, userId: defaultUserId });
|
const { container } = renderComponent({ device, userId: defaultUserId });
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
@ -1033,9 +1034,7 @@ describe("<UserOptionsSection />", () => {
|
||||||
expect(inviteSpy).toHaveBeenCalledWith([member.userId]);
|
expect(inviteSpy).toHaveBeenCalledWith([member.userId]);
|
||||||
|
|
||||||
// check that the test error message is displayed
|
// check that the test error message is displayed
|
||||||
await waitFor(() => {
|
await expect(screen.findByText(mockErrorMessage.message)).resolves.toBeInTheDocument();
|
||||||
expect(screen.getByText(mockErrorMessage.message)).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("if calling .invite throws something strange, show default error message", async () => {
|
it("if calling .invite throws something strange, show default error message", async () => {
|
||||||
|
@ -1048,9 +1047,7 @@ describe("<UserOptionsSection />", () => {
|
||||||
await userEvent.click(inviteButton);
|
await userEvent.click(inviteButton);
|
||||||
|
|
||||||
// check that the default test error message is displayed
|
// check that the default test error message is displayed
|
||||||
await waitFor(() => {
|
await expect(screen.findByText(/operation failed/i)).resolves.toBeInTheDocument();
|
||||||
expect(screen.getByText(/operation failed/i)).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
|
|
|
@ -260,7 +260,7 @@ describe("EventTile", () => {
|
||||||
} as EventEncryptionInfo);
|
} as EventEncryptionInfo);
|
||||||
|
|
||||||
const { container } = getComponent();
|
const { container } = getComponent();
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
const eventTiles = container.getElementsByClassName("mx_EventTile");
|
const eventTiles = container.getElementsByClassName("mx_EventTile");
|
||||||
expect(eventTiles).toHaveLength(1);
|
expect(eventTiles).toHaveLength(1);
|
||||||
|
@ -285,7 +285,7 @@ describe("EventTile", () => {
|
||||||
} as EventEncryptionInfo);
|
} as EventEncryptionInfo);
|
||||||
|
|
||||||
const { container } = getComponent();
|
const { container } = getComponent();
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
const eventTiles = container.getElementsByClassName("mx_EventTile");
|
const eventTiles = container.getElementsByClassName("mx_EventTile");
|
||||||
expect(eventTiles).toHaveLength(1);
|
expect(eventTiles).toHaveLength(1);
|
||||||
|
@ -314,7 +314,7 @@ describe("EventTile", () => {
|
||||||
} as EventEncryptionInfo);
|
} as EventEncryptionInfo);
|
||||||
|
|
||||||
const { container } = getComponent();
|
const { container } = getComponent();
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
const e2eIcons = container.getElementsByClassName("mx_EventTile_e2eIcon");
|
const e2eIcons = container.getElementsByClassName("mx_EventTile_e2eIcon");
|
||||||
expect(e2eIcons).toHaveLength(1);
|
expect(e2eIcons).toHaveLength(1);
|
||||||
|
@ -346,7 +346,7 @@ describe("EventTile", () => {
|
||||||
await mxEvent.attemptDecryption(mockCrypto);
|
await mxEvent.attemptDecryption(mockCrypto);
|
||||||
|
|
||||||
const { container } = getComponent();
|
const { container } = getComponent();
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
const eventTiles = container.getElementsByClassName("mx_EventTile");
|
const eventTiles = container.getElementsByClassName("mx_EventTile");
|
||||||
expect(eventTiles).toHaveLength(1);
|
expect(eventTiles).toHaveLength(1);
|
||||||
|
@ -400,7 +400,7 @@ describe("EventTile", () => {
|
||||||
const roomContext = getRoomContext(room, {});
|
const roomContext = getRoomContext(room, {});
|
||||||
const { container, rerender } = render(<WrappedEventTile roomContext={roomContext} />);
|
const { container, rerender } = render(<WrappedEventTile roomContext={roomContext} />);
|
||||||
|
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
const eventTiles = container.getElementsByClassName("mx_EventTile");
|
const eventTiles = container.getElementsByClassName("mx_EventTile");
|
||||||
expect(eventTiles).toHaveLength(1);
|
expect(eventTiles).toHaveLength(1);
|
||||||
|
@ -451,7 +451,7 @@ describe("EventTile", () => {
|
||||||
|
|
||||||
const roomContext = getRoomContext(room, {});
|
const roomContext = getRoomContext(room, {});
|
||||||
const { container, rerender } = render(<WrappedEventTile roomContext={roomContext} />);
|
const { container, rerender } = render(<WrappedEventTile roomContext={roomContext} />);
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
const eventTiles = container.getElementsByClassName("mx_EventTile");
|
const eventTiles = container.getElementsByClassName("mx_EventTile");
|
||||||
expect(eventTiles).toHaveLength(1);
|
expect(eventTiles).toHaveLength(1);
|
||||||
|
|
|
@ -8,7 +8,16 @@ Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { act, fireEvent, render, RenderResult, screen, waitFor, waitForElementToBeRemoved } from "jest-matrix-react";
|
import {
|
||||||
|
act,
|
||||||
|
fireEvent,
|
||||||
|
render,
|
||||||
|
RenderResult,
|
||||||
|
screen,
|
||||||
|
waitFor,
|
||||||
|
waitForElementToBeRemoved,
|
||||||
|
cleanup,
|
||||||
|
} from "jest-matrix-react";
|
||||||
import { Room, MatrixClient, RoomState, RoomMember, User, MatrixEvent } from "matrix-js-sdk/src/matrix";
|
import { Room, MatrixClient, RoomState, RoomMember, User, MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||||
import { mocked, MockedObject } from "jest-mock";
|
import { mocked, MockedObject } from "jest-mock";
|
||||||
|
@ -361,6 +370,7 @@ describe("MemberList", () => {
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.restoreAllMocks();
|
jest.restoreAllMocks();
|
||||||
|
cleanup();
|
||||||
});
|
});
|
||||||
|
|
||||||
const renderComponent = () => {
|
const renderComponent = () => {
|
||||||
|
@ -397,21 +407,22 @@ describe("MemberList", () => {
|
||||||
jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Join);
|
jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Join);
|
||||||
jest.spyOn(room, "canInvite").mockReturnValue(false);
|
jest.spyOn(room, "canInvite").mockReturnValue(false);
|
||||||
|
|
||||||
renderComponent();
|
const { findByLabelText } = renderComponent();
|
||||||
await flushPromises();
|
|
||||||
|
|
||||||
// button rendered but disabled
|
// button rendered but disabled
|
||||||
expect(screen.getByText("Invite to this room")).toHaveAttribute("aria-disabled", "true");
|
await expect(findByLabelText("You do not have permission to invite users")).resolves.toHaveAttribute(
|
||||||
|
"aria-disabled",
|
||||||
|
"true",
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders enabled invite button when current user is a member and has rights to invite", async () => {
|
it("renders enabled invite button when current user is a member and has rights to invite", async () => {
|
||||||
jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Join);
|
jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Join);
|
||||||
jest.spyOn(room, "canInvite").mockReturnValue(true);
|
jest.spyOn(room, "canInvite").mockReturnValue(true);
|
||||||
|
|
||||||
renderComponent();
|
const { findByText } = renderComponent();
|
||||||
await flushPromises();
|
|
||||||
|
|
||||||
expect(screen.getByText("Invite to this room")).not.toBeDisabled();
|
await expect(findByText("Invite to this room")).resolves.not.toBeDisabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("opens room inviter on button click", async () => {
|
it("opens room inviter on button click", async () => {
|
||||||
|
|
|
@ -42,17 +42,13 @@ import { mkVoiceBroadcastInfoStateEvent } from "../../../voice-broadcast/utils/t
|
||||||
import { SdkContextClass } from "../../../../../src/contexts/SDKContext";
|
import { SdkContextClass } from "../../../../../src/contexts/SDKContext";
|
||||||
|
|
||||||
const openStickerPicker = async (): Promise<void> => {
|
const openStickerPicker = async (): Promise<void> => {
|
||||||
await act(async () => {
|
await userEvent.click(screen.getByLabelText("More options"));
|
||||||
await userEvent.click(screen.getByLabelText("More options"));
|
await userEvent.click(screen.getByLabelText("Sticker"));
|
||||||
await userEvent.click(screen.getByLabelText("Sticker"));
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const startVoiceMessage = async (): Promise<void> => {
|
const startVoiceMessage = async (): Promise<void> => {
|
||||||
await act(async () => {
|
await userEvent.click(screen.getByLabelText("More options"));
|
||||||
await userEvent.click(screen.getByLabelText("More options"));
|
await userEvent.click(screen.getByLabelText("Voice Message"));
|
||||||
await userEvent.click(screen.getByLabelText("Voice Message"));
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const setCurrentBroadcastRecording = (room: Room, state: VoiceBroadcastInfoState): void => {
|
const setCurrentBroadcastRecording = (room: Room, state: VoiceBroadcastInfoState): void => {
|
||||||
|
@ -61,7 +57,7 @@ const setCurrentBroadcastRecording = (room: Room, state: VoiceBroadcastInfoState
|
||||||
MatrixClientPeg.safeGet(),
|
MatrixClientPeg.safeGet(),
|
||||||
state,
|
state,
|
||||||
);
|
);
|
||||||
SdkContextClass.instance.voiceBroadcastRecordingsStore.setCurrent(recording);
|
act(() => SdkContextClass.instance.voiceBroadcastRecordingsStore.setCurrent(recording));
|
||||||
};
|
};
|
||||||
|
|
||||||
const expectVoiceMessageRecordingTriggered = (): void => {
|
const expectVoiceMessageRecordingTriggered = (): void => {
|
||||||
|
@ -97,6 +93,45 @@ describe("MessageComposer", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("wysiwyg correctly persists state to and from localStorage", async () => {
|
||||||
|
const room = mkStubRoom("!roomId:server", "Room 1", cli);
|
||||||
|
const messageText = "Test Text";
|
||||||
|
await SettingsStore.setValue("feature_wysiwyg_composer", null, SettingLevel.DEVICE, true);
|
||||||
|
const { renderResult, rawComponent } = wrapAndRender({ room });
|
||||||
|
const { unmount } = renderResult;
|
||||||
|
|
||||||
|
await flushPromises();
|
||||||
|
|
||||||
|
const key = `mx_wysiwyg_state_${room.roomId}`;
|
||||||
|
|
||||||
|
await userEvent.click(screen.getByRole("textbox"));
|
||||||
|
fireEvent.input(screen.getByRole("textbox"), {
|
||||||
|
data: messageText,
|
||||||
|
inputType: "insertText",
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => expect(screen.getByRole("textbox")).toHaveTextContent(messageText));
|
||||||
|
|
||||||
|
// Wait for event dispatch to happen
|
||||||
|
await flushPromises();
|
||||||
|
|
||||||
|
// assert there is state persisted
|
||||||
|
expect(localStorage.getItem(key)).toBeNull();
|
||||||
|
|
||||||
|
// ensure the right state was persisted to localStorage
|
||||||
|
unmount();
|
||||||
|
|
||||||
|
// assert the persisted state
|
||||||
|
expect(JSON.parse(localStorage.getItem(key)!)).toStrictEqual({
|
||||||
|
content: messageText,
|
||||||
|
isRichText: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ensure the correct state is re-loaded
|
||||||
|
render(rawComponent);
|
||||||
|
await waitFor(() => expect(screen.getByRole("textbox")).toHaveTextContent(messageText));
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
describe("for a Room", () => {
|
describe("for a Room", () => {
|
||||||
const room = mkStubRoom("!roomId:server", "Room 1", cli);
|
const room = mkStubRoom("!roomId:server", "Room 1", cli);
|
||||||
|
|
||||||
|
@ -185,14 +220,12 @@ describe("MessageComposer", () => {
|
||||||
[true, false].forEach((value: boolean) => {
|
[true, false].forEach((value: boolean) => {
|
||||||
describe(`when ${setting} = ${value}`, () => {
|
describe(`when ${setting} = ${value}`, () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
SettingsStore.setValue(setting, null, SettingLevel.DEVICE, value);
|
await act(() => SettingsStore.setValue(setting, null, SettingLevel.DEVICE, value));
|
||||||
wrapAndRender({ room });
|
wrapAndRender({ room });
|
||||||
await act(async () => {
|
await userEvent.click(screen.getByLabelText("More options"));
|
||||||
await userEvent.click(screen.getByLabelText("More options"));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should${value || "not"} display the button`, () => {
|
it(`should${value ? "" : " not"} display the button`, () => {
|
||||||
if (value) {
|
if (value) {
|
||||||
// eslint-disable-next-line jest/no-conditional-expect
|
// eslint-disable-next-line jest/no-conditional-expect
|
||||||
expect(screen.getByLabelText(buttonLabel)).toBeInTheDocument();
|
expect(screen.getByLabelText(buttonLabel)).toBeInTheDocument();
|
||||||
|
@ -205,15 +238,17 @@ describe("MessageComposer", () => {
|
||||||
describe(`and setting ${setting} to ${!value}`, () => {
|
describe(`and setting ${setting} to ${!value}`, () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
// simulate settings update
|
// simulate settings update
|
||||||
await SettingsStore.setValue(setting, null, SettingLevel.DEVICE, !value);
|
await act(async () => {
|
||||||
dis.dispatch(
|
await SettingsStore.setValue(setting, null, SettingLevel.DEVICE, !value);
|
||||||
{
|
dis.dispatch(
|
||||||
action: Action.SettingUpdated,
|
{
|
||||||
settingName: setting,
|
action: Action.SettingUpdated,
|
||||||
newValue: !value,
|
settingName: setting,
|
||||||
},
|
newValue: !value,
|
||||||
true,
|
},
|
||||||
);
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should${!value || "not"} display the button`, () => {
|
it(`should${!value || "not"} display the button`, () => {
|
||||||
|
@ -273,7 +308,7 @@ describe("MessageComposer", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
wrapAndRender({ room }, true, true);
|
wrapAndRender({ room }, true, true);
|
||||||
await openStickerPicker();
|
await openStickerPicker();
|
||||||
resizeCallback(UI_EVENTS.Resize, {});
|
act(() => resizeCallback(UI_EVENTS.Resize, {}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should close the menu", () => {
|
it("should close the menu", () => {
|
||||||
|
@ -295,7 +330,7 @@ describe("MessageComposer", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
wrapAndRender({ room }, true, false);
|
wrapAndRender({ room }, true, false);
|
||||||
await openStickerPicker();
|
await openStickerPicker();
|
||||||
resizeCallback(UI_EVENTS.Resize, {});
|
act(() => resizeCallback(UI_EVENTS.Resize, {}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should close the menu", () => {
|
it("should close the menu", () => {
|
||||||
|
@ -443,51 +478,6 @@ describe("MessageComposer", () => {
|
||||||
expect(screen.queryByLabelText("Sticker")).not.toBeInTheDocument();
|
expect(screen.queryByLabelText("Sticker")).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("wysiwyg correctly persists state to and from localStorage", async () => {
|
|
||||||
const room = mkStubRoom("!roomId:server", "Room 1", cli);
|
|
||||||
const messageText = "Test Text";
|
|
||||||
await SettingsStore.setValue("feature_wysiwyg_composer", null, SettingLevel.DEVICE, true);
|
|
||||||
const { renderResult, rawComponent } = wrapAndRender({ room });
|
|
||||||
const { unmount, rerender } = renderResult;
|
|
||||||
|
|
||||||
await act(async () => {
|
|
||||||
await flushPromises();
|
|
||||||
});
|
|
||||||
|
|
||||||
const key = `mx_wysiwyg_state_${room.roomId}`;
|
|
||||||
|
|
||||||
await act(async () => {
|
|
||||||
await userEvent.click(screen.getByRole("textbox"));
|
|
||||||
});
|
|
||||||
fireEvent.input(screen.getByRole("textbox"), {
|
|
||||||
data: messageText,
|
|
||||||
inputType: "insertText",
|
|
||||||
});
|
|
||||||
|
|
||||||
await waitFor(() => expect(screen.getByRole("textbox")).toHaveTextContent(messageText));
|
|
||||||
|
|
||||||
// Wait for event dispatch to happen
|
|
||||||
await act(async () => {
|
|
||||||
await flushPromises();
|
|
||||||
});
|
|
||||||
|
|
||||||
// assert there is state persisted
|
|
||||||
expect(localStorage.getItem(key)).toBeNull();
|
|
||||||
|
|
||||||
// ensure the right state was persisted to localStorage
|
|
||||||
unmount();
|
|
||||||
|
|
||||||
// assert the persisted state
|
|
||||||
expect(JSON.parse(localStorage.getItem(key)!)).toStrictEqual({
|
|
||||||
content: messageText,
|
|
||||||
isRichText: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// ensure the correct state is re-loaded
|
|
||||||
rerender(rawComponent);
|
|
||||||
await waitFor(() => expect(screen.getByRole("textbox")).toHaveTextContent(messageText));
|
|
||||||
}, 10000);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function wrapAndRender(
|
function wrapAndRender(
|
||||||
|
@ -529,7 +519,7 @@ function wrapAndRender(
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
rawComponent: getRawComponent(props, roomContext, mockClient),
|
rawComponent: getRawComponent(props, roomContext, mockClient),
|
||||||
renderResult: render(getRawComponent(props, roomContext, mockClient)),
|
renderResult: render(getRawComponent(props, roomContext, mockClient), { legacyRoot: true }),
|
||||||
roomContext,
|
roomContext,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -385,7 +385,7 @@ describe("<SendMessageComposer/>", () => {
|
||||||
|
|
||||||
it("correctly persists state to and from localStorage", () => {
|
it("correctly persists state to and from localStorage", () => {
|
||||||
const props = { replyToEvent: mockEvent };
|
const props = { replyToEvent: mockEvent };
|
||||||
const { container, unmount, rerender } = getComponent(props);
|
let { container, unmount } = getComponent(props);
|
||||||
|
|
||||||
addTextToComposer(container, "Test Text");
|
addTextToComposer(container, "Test Text");
|
||||||
|
|
||||||
|
@ -402,7 +402,7 @@ describe("<SendMessageComposer/>", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// ensure the correct model is re-loaded
|
// ensure the correct model is re-loaded
|
||||||
rerender(getRawComponent(props));
|
({ container, unmount } = getComponent(props));
|
||||||
expect(container.textContent).toBe("Test Text");
|
expect(container.textContent).toBe("Test Text");
|
||||||
expect(spyDispatcher).toHaveBeenCalledWith({
|
expect(spyDispatcher).toHaveBeenCalledWith({
|
||||||
action: "reply_to_event",
|
action: "reply_to_event",
|
||||||
|
@ -413,7 +413,7 @@ describe("<SendMessageComposer/>", () => {
|
||||||
// now try with localStorage wiped out
|
// now try with localStorage wiped out
|
||||||
unmount();
|
unmount();
|
||||||
localStorage.removeItem(key);
|
localStorage.removeItem(key);
|
||||||
rerender(getRawComponent(props));
|
({ container } = getComponent(props));
|
||||||
expect(container.textContent).toBe("");
|
expect(container.textContent).toBe("");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
|
|
||||||
import "@testing-library/jest-dom";
|
import "@testing-library/jest-dom";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { act, fireEvent, render, screen, waitFor } from "jest-matrix-react";
|
import { fireEvent, render, screen, waitFor } from "jest-matrix-react";
|
||||||
|
|
||||||
import MatrixClientContext from "../../../../../../src/contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../../../../src/contexts/MatrixClientContext";
|
||||||
import RoomContext from "../../../../../../src/contexts/RoomContext";
|
import RoomContext from "../../../../../../src/contexts/RoomContext";
|
||||||
|
@ -253,9 +253,7 @@ describe("EditWysiwygComposer", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait for event dispatch to happen
|
// Wait for event dispatch to happen
|
||||||
await act(async () => {
|
await flushPromises();
|
||||||
await flushPromises();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Then we don't get it because we are disabled
|
// Then we don't get it because we are disabled
|
||||||
expect(screen.getByRole("textbox")).not.toHaveFocus();
|
expect(screen.getByRole("textbox")).not.toHaveFocus();
|
||||||
|
|
|
@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { render, screen, waitFor } from "jest-matrix-react";
|
import { render, screen, waitFor, cleanup } from "jest-matrix-react";
|
||||||
import { MatrixClient, MatrixError, ThreepidMedium } from "matrix-js-sdk/src/matrix";
|
import { MatrixClient, MatrixError, ThreepidMedium } from "matrix-js-sdk/src/matrix";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
|
@ -48,54 +48,13 @@ describe("AddRemoveThreepids", () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.restoreAllMocks();
|
jest.restoreAllMocks();
|
||||||
clearAllModals();
|
clearAllModals();
|
||||||
|
cleanup();
|
||||||
});
|
});
|
||||||
|
|
||||||
const clientProviderWrapper: React.FC = ({ children }: React.PropsWithChildren) => (
|
const clientProviderWrapper: React.FC = ({ children }: React.PropsWithChildren) => (
|
||||||
<MatrixClientContext.Provider value={client}>{children}</MatrixClientContext.Provider>
|
<MatrixClientContext.Provider value={client}>{children}</MatrixClientContext.Provider>
|
||||||
);
|
);
|
||||||
|
|
||||||
it("should render a loader while loading", async () => {
|
|
||||||
render(
|
|
||||||
<AddRemoveThreepids
|
|
||||||
mode="hs"
|
|
||||||
medium={ThreepidMedium.Email}
|
|
||||||
threepids={[]}
|
|
||||||
isLoading={true}
|
|
||||||
onChange={() => {}}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(screen.getByLabelText("Loading…")).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render email addresses", async () => {
|
|
||||||
const { container } = render(
|
|
||||||
<AddRemoveThreepids
|
|
||||||
mode="hs"
|
|
||||||
medium={ThreepidMedium.Email}
|
|
||||||
threepids={[EMAIL1]}
|
|
||||||
isLoading={false}
|
|
||||||
onChange={() => {}}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(container).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should render phone numbers", async () => {
|
|
||||||
const { container } = render(
|
|
||||||
<AddRemoveThreepids
|
|
||||||
mode="hs"
|
|
||||||
medium={ThreepidMedium.Phone}
|
|
||||||
threepids={[PHONE1]}
|
|
||||||
isLoading={false}
|
|
||||||
onChange={() => {}}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(container).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should handle no email addresses", async () => {
|
it("should handle no email addresses", async () => {
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<AddRemoveThreepids
|
<AddRemoveThreepids
|
||||||
|
@ -107,6 +66,7 @@ describe("AddRemoveThreepids", () => {
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await expect(screen.findByText("Email Address")).resolves.toBeVisible();
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -127,7 +87,7 @@ describe("AddRemoveThreepids", () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const input = screen.getByRole("textbox", { name: "Email Address" });
|
const input = await screen.findByRole("textbox", { name: "Email Address" });
|
||||||
await userEvent.type(input, EMAIL1.address);
|
await userEvent.type(input, EMAIL1.address);
|
||||||
const addButton = screen.getByRole("button", { name: "Add" });
|
const addButton = screen.getByRole("button", { name: "Add" });
|
||||||
await userEvent.click(addButton);
|
await userEvent.click(addButton);
|
||||||
|
@ -166,7 +126,7 @@ describe("AddRemoveThreepids", () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const input = screen.getByRole("textbox", { name: "Email Address" });
|
const input = await screen.findByRole("textbox", { name: "Email Address" });
|
||||||
await userEvent.type(input, EMAIL1.address);
|
await userEvent.type(input, EMAIL1.address);
|
||||||
const addButton = screen.getByRole("button", { name: "Add" });
|
const addButton = screen.getByRole("button", { name: "Add" });
|
||||||
await userEvent.click(addButton);
|
await userEvent.click(addButton);
|
||||||
|
@ -210,7 +170,7 @@ describe("AddRemoveThreepids", () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const countryDropdown = screen.getByRole("button", { name: /Country Dropdown/ });
|
const countryDropdown = await screen.findByRole("button", { name: /Country Dropdown/ });
|
||||||
await userEvent.click(countryDropdown);
|
await userEvent.click(countryDropdown);
|
||||||
const gbOption = screen.getByRole("option", { name: "🇬🇧 United Kingdom (+44)" });
|
const gbOption = screen.getByRole("option", { name: "🇬🇧 United Kingdom (+44)" });
|
||||||
await userEvent.click(gbOption);
|
await userEvent.click(gbOption);
|
||||||
|
@ -270,7 +230,7 @@ describe("AddRemoveThreepids", () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const removeButton = screen.getByRole("button", { name: /Remove/ });
|
const removeButton = await screen.findByRole("button", { name: /Remove/ });
|
||||||
await userEvent.click(removeButton);
|
await userEvent.click(removeButton);
|
||||||
|
|
||||||
expect(screen.getByText(`Remove ${EMAIL1.address}?`)).toBeVisible();
|
expect(screen.getByText(`Remove ${EMAIL1.address}?`)).toBeVisible();
|
||||||
|
@ -297,7 +257,7 @@ describe("AddRemoveThreepids", () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const removeButton = screen.getByRole("button", { name: /Remove/ });
|
const removeButton = await screen.findByRole("button", { name: /Remove/ });
|
||||||
await userEvent.click(removeButton);
|
await userEvent.click(removeButton);
|
||||||
|
|
||||||
expect(screen.getByText(`Remove ${EMAIL1.address}?`)).toBeVisible();
|
expect(screen.getByText(`Remove ${EMAIL1.address}?`)).toBeVisible();
|
||||||
|
@ -326,7 +286,7 @@ describe("AddRemoveThreepids", () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const removeButton = screen.getByRole("button", { name: /Remove/ });
|
const removeButton = await screen.findByRole("button", { name: /Remove/ });
|
||||||
await userEvent.click(removeButton);
|
await userEvent.click(removeButton);
|
||||||
|
|
||||||
expect(screen.getByText(`Remove ${PHONE1.address}?`)).toBeVisible();
|
expect(screen.getByText(`Remove ${PHONE1.address}?`)).toBeVisible();
|
||||||
|
@ -357,7 +317,7 @@ describe("AddRemoveThreepids", () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.getByText(EMAIL1.address)).toBeVisible();
|
await expect(screen.findByText(EMAIL1.address)).resolves.toBeVisible();
|
||||||
const shareButton = screen.getByRole("button", { name: /Share/ });
|
const shareButton = screen.getByRole("button", { name: /Share/ });
|
||||||
await userEvent.click(shareButton);
|
await userEvent.click(shareButton);
|
||||||
|
|
||||||
|
@ -408,7 +368,7 @@ describe("AddRemoveThreepids", () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.getByText(PHONE1.address)).toBeVisible();
|
await expect(screen.findByText(PHONE1.address)).resolves.toBeVisible();
|
||||||
const shareButton = screen.getByRole("button", { name: /Share/ });
|
const shareButton = screen.getByRole("button", { name: /Share/ });
|
||||||
await userEvent.click(shareButton);
|
await userEvent.click(shareButton);
|
||||||
|
|
||||||
|
@ -452,7 +412,7 @@ describe("AddRemoveThreepids", () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.getByText(EMAIL1.address)).toBeVisible();
|
await expect(screen.findByText(EMAIL1.address)).resolves.toBeVisible();
|
||||||
const revokeButton = screen.getByRole("button", { name: /Revoke/ });
|
const revokeButton = screen.getByRole("button", { name: /Revoke/ });
|
||||||
await userEvent.click(revokeButton);
|
await userEvent.click(revokeButton);
|
||||||
|
|
||||||
|
@ -475,7 +435,7 @@ describe("AddRemoveThreepids", () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.getByText(PHONE1.address)).toBeVisible();
|
await expect(screen.findByText(PHONE1.address)).resolves.toBeVisible();
|
||||||
const revokeButton = screen.getByRole("button", { name: /Revoke/ });
|
const revokeButton = screen.getByRole("button", { name: /Revoke/ });
|
||||||
await userEvent.click(revokeButton);
|
await userEvent.click(revokeButton);
|
||||||
|
|
||||||
|
@ -596,4 +556,48 @@ describe("AddRemoveThreepids", () => {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should render a loader while loading", async () => {
|
||||||
|
render(
|
||||||
|
<AddRemoveThreepids
|
||||||
|
mode="hs"
|
||||||
|
medium={ThreepidMedium.Email}
|
||||||
|
threepids={[]}
|
||||||
|
isLoading={true}
|
||||||
|
onChange={() => {}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByLabelText("Loading…")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render email addresses", async () => {
|
||||||
|
const { container } = render(
|
||||||
|
<AddRemoveThreepids
|
||||||
|
mode="hs"
|
||||||
|
medium={ThreepidMedium.Email}
|
||||||
|
threepids={[EMAIL1]}
|
||||||
|
isLoading={false}
|
||||||
|
onChange={() => {}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(screen.findByText(EMAIL1.address)).resolves.toBeVisible();
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render phone numbers", async () => {
|
||||||
|
const { container } = render(
|
||||||
|
<AddRemoveThreepids
|
||||||
|
mode="hs"
|
||||||
|
medium={ThreepidMedium.Phone}
|
||||||
|
threepids={[PHONE1]}
|
||||||
|
isLoading={false}
|
||||||
|
onChange={() => {}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(screen.findByText(PHONE1.address)).resolves.toBeVisible();
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,14 +11,14 @@ exports[`AddRemoveThreepids should handle no email addresses 1`] = `
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
autocomplete="email"
|
autocomplete="email"
|
||||||
id="mx_Field_3"
|
id="mx_Field_1"
|
||||||
label="Email Address"
|
label="Email Address"
|
||||||
placeholder="Email Address"
|
placeholder="Email Address"
|
||||||
type="text"
|
type="text"
|
||||||
value=""
|
value=""
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
for="mx_Field_3"
|
for="mx_Field_1"
|
||||||
>
|
>
|
||||||
Email Address
|
Email Address
|
||||||
</label>
|
</label>
|
||||||
|
@ -61,14 +61,14 @@ exports[`AddRemoveThreepids should render email addresses 1`] = `
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
autocomplete="email"
|
autocomplete="email"
|
||||||
id="mx_Field_1"
|
id="mx_Field_14"
|
||||||
label="Email Address"
|
label="Email Address"
|
||||||
placeholder="Email Address"
|
placeholder="Email Address"
|
||||||
type="text"
|
type="text"
|
||||||
value=""
|
value=""
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
for="mx_Field_1"
|
for="mx_Field_14"
|
||||||
>
|
>
|
||||||
Email Address
|
Email Address
|
||||||
</label>
|
</label>
|
||||||
|
@ -148,14 +148,14 @@ exports[`AddRemoveThreepids should render phone numbers 1`] = `
|
||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
autocomplete="tel-national"
|
autocomplete="tel-national"
|
||||||
id="mx_Field_2"
|
id="mx_Field_15"
|
||||||
label="Phone Number"
|
label="Phone Number"
|
||||||
placeholder="Phone Number"
|
placeholder="Phone Number"
|
||||||
type="text"
|
type="text"
|
||||||
value=""
|
value=""
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
for="mx_Field_2"
|
for="mx_Field_15"
|
||||||
>
|
>
|
||||||
Phone Number
|
Phone Number
|
||||||
</label>
|
</label>
|
||||||
|
|
|
@ -79,9 +79,7 @@ describe("<LoginWithQR />", () => {
|
||||||
|
|
||||||
describe("MSC4108", () => {
|
describe("MSC4108", () => {
|
||||||
const getComponent = (props: { client: MatrixClient; onFinished?: () => void }) => (
|
const getComponent = (props: { client: MatrixClient; onFinished?: () => void }) => (
|
||||||
<React.StrictMode>
|
<LoginWithQR {...defaultProps} {...props} />
|
||||||
<LoginWithQR {...defaultProps} {...props} />
|
|
||||||
</React.StrictMode>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
test("render QR then back", async () => {
|
test("render QR then back", async () => {
|
||||||
|
|
|
@ -277,9 +277,7 @@ describe("<SessionManagerTab />", () => {
|
||||||
mockClient.getDevices.mockRejectedValue({ httpStatus: 404 });
|
mockClient.getDevices.mockRejectedValue({ httpStatus: 404 });
|
||||||
const { container } = render(getComponent());
|
const { container } = render(getComponent());
|
||||||
|
|
||||||
await act(async () => {
|
await flushPromises();
|
||||||
await flushPromises();
|
|
||||||
});
|
|
||||||
expect(container.getElementsByClassName("mx_Spinner").length).toBeFalsy();
|
expect(container.getElementsByClassName("mx_Spinner").length).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -302,9 +300,7 @@ describe("<SessionManagerTab />", () => {
|
||||||
|
|
||||||
const { getByTestId } = render(getComponent());
|
const { getByTestId } = render(getComponent());
|
||||||
|
|
||||||
await act(async () => {
|
await flushPromises();
|
||||||
await flushPromises();
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(mockCrypto.getDeviceVerificationStatus).toHaveBeenCalledTimes(3);
|
expect(mockCrypto.getDeviceVerificationStatus).toHaveBeenCalledTimes(3);
|
||||||
expect(
|
expect(
|
||||||
|
@ -337,9 +333,7 @@ describe("<SessionManagerTab />", () => {
|
||||||
|
|
||||||
const { getByTestId } = render(getComponent());
|
const { getByTestId } = render(getComponent());
|
||||||
|
|
||||||
await act(async () => {
|
await flushPromises();
|
||||||
await flushPromises();
|
|
||||||
});
|
|
||||||
|
|
||||||
// twice for each device
|
// twice for each device
|
||||||
expect(mockClient.getAccountData).toHaveBeenCalledTimes(4);
|
expect(mockClient.getAccountData).toHaveBeenCalledTimes(4);
|
||||||
|
@ -356,9 +350,7 @@ describe("<SessionManagerTab />", () => {
|
||||||
|
|
||||||
const { getByTestId, queryByTestId } = render(getComponent());
|
const { getByTestId, queryByTestId } = render(getComponent());
|
||||||
|
|
||||||
await act(async () => {
|
await flushPromises();
|
||||||
await flushPromises();
|
|
||||||
});
|
|
||||||
|
|
||||||
toggleDeviceDetails(getByTestId, alicesDevice.device_id);
|
toggleDeviceDetails(getByTestId, alicesDevice.device_id);
|
||||||
// application metadata section not rendered
|
// application metadata section not rendered
|
||||||
|
@ -369,9 +361,7 @@ describe("<SessionManagerTab />", () => {
|
||||||
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice] });
|
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice] });
|
||||||
const { queryByTestId } = render(getComponent());
|
const { queryByTestId } = render(getComponent());
|
||||||
|
|
||||||
await act(async () => {
|
await flushPromises();
|
||||||
await flushPromises();
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(queryByTestId("other-sessions-section")).toBeFalsy();
|
expect(queryByTestId("other-sessions-section")).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
@ -382,9 +372,7 @@ describe("<SessionManagerTab />", () => {
|
||||||
});
|
});
|
||||||
const { getByTestId } = render(getComponent());
|
const { getByTestId } = render(getComponent());
|
||||||
|
|
||||||
await act(async () => {
|
await flushPromises();
|
||||||
await flushPromises();
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(getByTestId("other-sessions-section")).toBeTruthy();
|
expect(getByTestId("other-sessions-section")).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
@ -395,9 +383,7 @@ describe("<SessionManagerTab />", () => {
|
||||||
});
|
});
|
||||||
const { getByTestId, container } = render(getComponent());
|
const { getByTestId, container } = render(getComponent());
|
||||||
|
|
||||||
await act(async () => {
|
await flushPromises();
|
||||||
await flushPromises();
|
|
||||||
});
|
|
||||||
|
|
||||||
fireEvent.click(getByTestId("unverified-devices-cta"));
|
fireEvent.click(getByTestId("unverified-devices-cta"));
|
||||||
|
|
||||||
|
@ -908,7 +894,8 @@ describe("<SessionManagerTab />", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
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({});
|
const deferredDeleteMultipleDevices = defer<{}>();
|
||||||
|
mockClient.deleteMultipleDevices.mockReturnValue(deferredDeleteMultipleDevices.promise);
|
||||||
mockClient.getDevices.mockResolvedValue({
|
mockClient.getDevices.mockResolvedValue({
|
||||||
devices: [alicesDevice, alicesMobileDevice, alicesOlderMobileDevice],
|
devices: [alicesDevice, alicesMobileDevice, alicesOlderMobileDevice],
|
||||||
});
|
});
|
||||||
|
@ -933,6 +920,7 @@ describe("<SessionManagerTab />", () => {
|
||||||
fireEvent.click(signOutButton);
|
fireEvent.click(signOutButton);
|
||||||
await confirmSignout(getByTestId);
|
await confirmSignout(getByTestId);
|
||||||
await prom;
|
await prom;
|
||||||
|
deferredDeleteMultipleDevices.resolve({});
|
||||||
|
|
||||||
// delete called
|
// delete called
|
||||||
expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith(
|
expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith(
|
||||||
|
@ -991,7 +979,7 @@ describe("<SessionManagerTab />", () => {
|
||||||
|
|
||||||
const { getByTestId, getByLabelText } = render(getComponent());
|
const { getByTestId, getByLabelText } = render(getComponent());
|
||||||
|
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
// reset mock count after initial load
|
// reset mock count after initial load
|
||||||
mockClient.getDevices.mockClear();
|
mockClient.getDevices.mockClear();
|
||||||
|
@ -1025,7 +1013,7 @@ describe("<SessionManagerTab />", () => {
|
||||||
fireEvent.submit(getByLabelText("Password"));
|
fireEvent.submit(getByLabelText("Password"));
|
||||||
});
|
});
|
||||||
|
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
// called again with auth
|
// called again with auth
|
||||||
expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith([alicesMobileDevice.device_id], {
|
expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith([alicesMobileDevice.device_id], {
|
||||||
|
@ -1551,7 +1539,7 @@ describe("<SessionManagerTab />", () => {
|
||||||
});
|
});
|
||||||
const { getByTestId, container } = render(getComponent());
|
const { getByTestId, container } = render(getComponent());
|
||||||
|
|
||||||
await act(flushPromises);
|
await flushPromises();
|
||||||
|
|
||||||
// filter for inactive sessions
|
// filter for inactive sessions
|
||||||
await setFilter(container, DeviceSecurityVariation.Inactive);
|
await setFilter(container, DeviceSecurityVariation.Inactive);
|
||||||
|
@ -1577,9 +1565,7 @@ describe("<SessionManagerTab />", () => {
|
||||||
it("lets you change the pusher state", async () => {
|
it("lets you change the pusher state", async () => {
|
||||||
const { getByTestId } = render(getComponent());
|
const { getByTestId } = render(getComponent());
|
||||||
|
|
||||||
await act(async () => {
|
await flushPromises();
|
||||||
await flushPromises();
|
|
||||||
});
|
|
||||||
|
|
||||||
toggleDeviceDetails(getByTestId, alicesMobileDevice.device_id);
|
toggleDeviceDetails(getByTestId, alicesMobileDevice.device_id);
|
||||||
|
|
||||||
|
@ -1598,9 +1584,7 @@ describe("<SessionManagerTab />", () => {
|
||||||
it("lets you change the local notification settings state", async () => {
|
it("lets you change the local notification settings state", async () => {
|
||||||
const { getByTestId } = render(getComponent());
|
const { getByTestId } = render(getComponent());
|
||||||
|
|
||||||
await act(async () => {
|
await flushPromises();
|
||||||
await flushPromises();
|
|
||||||
});
|
|
||||||
|
|
||||||
toggleDeviceDetails(getByTestId, alicesDevice.device_id);
|
toggleDeviceDetails(getByTestId, alicesDevice.device_id);
|
||||||
|
|
||||||
|
@ -1621,9 +1605,7 @@ describe("<SessionManagerTab />", () => {
|
||||||
it("updates the UI when another session changes the local notifications", async () => {
|
it("updates the UI when another session changes the local notifications", async () => {
|
||||||
const { getByTestId } = render(getComponent());
|
const { getByTestId } = render(getComponent());
|
||||||
|
|
||||||
await act(async () => {
|
await flushPromises();
|
||||||
await flushPromises();
|
|
||||||
});
|
|
||||||
|
|
||||||
toggleDeviceDetails(getByTestId, alicesDevice.device_id);
|
toggleDeviceDetails(getByTestId, alicesDevice.device_id);
|
||||||
|
|
||||||
|
|
|
@ -42,14 +42,14 @@ exports[`<AccountUserSettingsTab /> 3pids should display 3pid email addresses an
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
autocomplete="email"
|
autocomplete="email"
|
||||||
id="mx_Field_9"
|
id="mx_Field_3"
|
||||||
label="Email Address"
|
label="Email Address"
|
||||||
placeholder="Email Address"
|
placeholder="Email Address"
|
||||||
type="text"
|
type="text"
|
||||||
value=""
|
value=""
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
for="mx_Field_9"
|
for="mx_Field_3"
|
||||||
>
|
>
|
||||||
Email Address
|
Email Address
|
||||||
</label>
|
</label>
|
||||||
|
@ -145,14 +145,14 @@ exports[`<AccountUserSettingsTab /> 3pids should display 3pid email addresses an
|
||||||
</span>
|
</span>
|
||||||
<input
|
<input
|
||||||
autocomplete="tel-national"
|
autocomplete="tel-national"
|
||||||
id="mx_Field_10"
|
id="mx_Field_4"
|
||||||
label="Phone Number"
|
label="Phone Number"
|
||||||
placeholder="Phone Number"
|
placeholder="Phone Number"
|
||||||
type="text"
|
type="text"
|
||||||
value=""
|
value=""
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
for="mx_Field_10"
|
for="mx_Field_4"
|
||||||
>
|
>
|
||||||
Phone Number
|
Phone Number
|
||||||
</label>
|
</label>
|
||||||
|
|
|
@ -388,7 +388,7 @@ exports[`<SessionManagerTab /> goes to filtered list from security recommendatio
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-label="Select all"
|
aria-label="Select all"
|
||||||
aria-labelledby=":r4e:"
|
aria-labelledby=":r3s:"
|
||||||
data-testid="device-select-all-checkbox"
|
data-testid="device-select-all-checkbox"
|
||||||
id="device-select-all-checkbox"
|
id="device-select-all-checkbox"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
|
|
|
@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
|
|
||||||
import React, { ComponentProps } from "react";
|
import React, { ComponentProps } from "react";
|
||||||
import { mocked, Mocked } from "jest-mock";
|
import { mocked, Mocked } from "jest-mock";
|
||||||
import { act, render, RenderResult } from "jest-matrix-react";
|
import { render, RenderResult } from "jest-matrix-react";
|
||||||
import { TypedEventEmitter, IMyDevice, MatrixClient, Device } from "matrix-js-sdk/src/matrix";
|
import { TypedEventEmitter, IMyDevice, MatrixClient, Device } from "matrix-js-sdk/src/matrix";
|
||||||
import { VerificationRequest, VerificationRequestEvent } from "matrix-js-sdk/src/crypto-api";
|
import { VerificationRequest, VerificationRequestEvent } from "matrix-js-sdk/src/crypto-api";
|
||||||
|
|
||||||
|
@ -63,9 +63,7 @@ describe("VerificationRequestToast", () => {
|
||||||
otherDeviceId,
|
otherDeviceId,
|
||||||
});
|
});
|
||||||
const result = renderComponent({ request });
|
const result = renderComponent({ request });
|
||||||
await act(async () => {
|
await flushPromises();
|
||||||
await flushPromises();
|
|
||||||
});
|
|
||||||
expect(result.container).toMatchSnapshot();
|
expect(result.container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -76,9 +74,7 @@ describe("VerificationRequestToast", () => {
|
||||||
otherUserId,
|
otherUserId,
|
||||||
});
|
});
|
||||||
const result = renderComponent({ request });
|
const result = renderComponent({ request });
|
||||||
await act(async () => {
|
await flushPromises();
|
||||||
await flushPromises();
|
|
||||||
});
|
|
||||||
expect(result.container).toMatchSnapshot();
|
expect(result.container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -89,9 +85,7 @@ describe("VerificationRequestToast", () => {
|
||||||
otherUserId,
|
otherUserId,
|
||||||
});
|
});
|
||||||
renderComponent({ request, toastKey: "testKey" });
|
renderComponent({ request, toastKey: "testKey" });
|
||||||
await act(async () => {
|
await flushPromises();
|
||||||
await flushPromises();
|
|
||||||
});
|
|
||||||
|
|
||||||
const dismiss = jest.spyOn(ToastStore.sharedInstance(), "dismissToast");
|
const dismiss = jest.spyOn(ToastStore.sharedInstance(), "dismissToast");
|
||||||
Object.defineProperty(request, "accepting", { value: true });
|
Object.defineProperty(request, "accepting", { value: true });
|
||||||
|
|
|
@ -65,7 +65,8 @@ describe("UnverifiedSessionToast", () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
it("should render as expected", () => {
|
it("should render as expected", async () => {
|
||||||
|
await expect(screen.findByText("New login. Was this you?")).resolves.toBeInTheDocument();
|
||||||
expect(renderResult.baseElement).toMatchSnapshot();
|
expect(renderResult.baseElement).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ describe("requestMediaPermissions", () => {
|
||||||
const itShouldLogTheErrorAndShowTheNoMediaPermissionsModal = () => {
|
const itShouldLogTheErrorAndShowTheNoMediaPermissionsModal = () => {
|
||||||
it("should log the error and show the »No media permissions« modal", async () => {
|
it("should log the error and show the »No media permissions« modal", async () => {
|
||||||
expect(logger.log).toHaveBeenCalledWith("Failed to list userMedia devices", error);
|
expect(logger.log).toHaveBeenCalledWith("Failed to list userMedia devices", error);
|
||||||
await screen.findByText("No media permissions");
|
await expect(screen.findByText("No media permissions")).resolves.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,7 @@ exports[`showIncompatibleBrowser should match snapshot 1`] = `
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex mx_ErrorView_buttons"
|
class="mx_Flex mx_ErrorView_buttons"
|
||||||
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-4x);"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="_button_i91xf_17 _has-icon_i91xf_66"
|
class="_button_i91xf_17 _has-icon_i91xf_66"
|
||||||
|
@ -152,6 +153,7 @@ exports[`showIncompatibleBrowser should match snapshot 1`] = `
|
||||||
</h2>
|
</h2>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex"
|
class="mx_Flex"
|
||||||
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-4x);"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="_button_i91xf_17 _has-icon_i91xf_66"
|
class="_button_i91xf_17 _has-icon_i91xf_66"
|
||||||
|
@ -221,6 +223,7 @@ exports[`showIncompatibleBrowser should match snapshot 1`] = `
|
||||||
</h2>
|
</h2>
|
||||||
<div
|
<div
|
||||||
class="mx_Flex"
|
class="mx_Flex"
|
||||||
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-6x);"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="https://apps.apple.com/app/vector/id1083446067"
|
href="https://apps.apple.com/app/vector/id1083446067"
|
||||||
|
|
|
@ -5,7 +5,12 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { showError, showIncompatibleBrowser } from "../../../src/vector/init.tsx";
|
import fetchMock from "fetch-mock-jest";
|
||||||
|
import { waitFor, screen } from "jest-matrix-react";
|
||||||
|
|
||||||
|
import { loadApp, showError, showIncompatibleBrowser } from "../../../src/vector/init.tsx";
|
||||||
|
import SdkConfig from "../../../src/SdkConfig.ts";
|
||||||
|
import MatrixChat from "../../../src/components/structures/MatrixChat.tsx";
|
||||||
|
|
||||||
function setUpMatrixChatDiv() {
|
function setUpMatrixChatDiv() {
|
||||||
document.getElementById("matrixchat")?.remove();
|
document.getElementById("matrixchat")?.remove();
|
||||||
|
@ -19,6 +24,7 @@ describe("showIncompatibleBrowser", () => {
|
||||||
|
|
||||||
it("should match snapshot", async () => {
|
it("should match snapshot", async () => {
|
||||||
await showIncompatibleBrowser(jest.fn());
|
await showIncompatibleBrowser(jest.fn());
|
||||||
|
await screen.findByText("Element does not support this browser");
|
||||||
expect(document.getElementById("matrixchat")).toMatchSnapshot();
|
expect(document.getElementById("matrixchat")).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -28,6 +34,19 @@ describe("showError", () => {
|
||||||
|
|
||||||
it("should match snapshot", async () => {
|
it("should match snapshot", async () => {
|
||||||
await showError("Error title", ["msg1", "msg2"]);
|
await showError("Error title", ["msg1", "msg2"]);
|
||||||
|
await screen.findByText("Error title");
|
||||||
expect(document.getElementById("matrixchat")).toMatchSnapshot();
|
expect(document.getElementById("matrixchat")).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("loadApp", () => {
|
||||||
|
beforeEach(setUpMatrixChatDiv);
|
||||||
|
|
||||||
|
it("should set window.matrixChat to the MatrixChat instance", async () => {
|
||||||
|
fetchMock.get("https://matrix.org/_matrix/client/versions", { versions: ["v1.6"] });
|
||||||
|
SdkConfig.put({ default_server_config: { "m.homeserver": { base_url: "https://matrix.org" } } });
|
||||||
|
|
||||||
|
await loadApp({});
|
||||||
|
await waitFor(() => expect(window.matrixChat).toBeInstanceOf(MatrixChat));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -90,9 +90,7 @@ describe("VoiceBroadcastPreRecordingPip", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
renderResult = render(<VoiceBroadcastPreRecordingPip voiceBroadcastPreRecording={preRecording} />);
|
renderResult = render(<VoiceBroadcastPreRecordingPip voiceBroadcastPreRecording={preRecording} />);
|
||||||
|
|
||||||
await act(async () => {
|
await flushPromises();
|
||||||
flushPromises();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should match the snapshot", () => {
|
it("should match the snapshot", () => {
|
||||||
|
|
|
@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
//
|
//
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { act, render, RenderResult, screen } from "jest-matrix-react";
|
import { render, RenderResult, screen } from "jest-matrix-react";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
import { ClientEvent, MatrixClient, MatrixEvent, SyncState } from "matrix-js-sdk/src/matrix";
|
import { ClientEvent, MatrixClient, MatrixEvent, SyncState } from "matrix-js-sdk/src/matrix";
|
||||||
import { sleep } from "matrix-js-sdk/src/utils";
|
import { sleep } from "matrix-js-sdk/src/utils";
|
||||||
|
@ -61,9 +61,7 @@ describe("VoiceBroadcastRecordingPip", () => {
|
||||||
jest.spyOn(recording, "pause");
|
jest.spyOn(recording, "pause");
|
||||||
jest.spyOn(recording, "resume");
|
jest.spyOn(recording, "resume");
|
||||||
renderResult = render(<VoiceBroadcastRecordingPip recording={recording} />);
|
renderResult = render(<VoiceBroadcastRecordingPip recording={recording} />);
|
||||||
await act(async () => {
|
await flushPromises();
|
||||||
flushPromises();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const itShouldShowTheBroadcastRoom = () => {
|
const itShouldShowTheBroadcastRoom = () => {
|
||||||
|
@ -152,8 +150,9 @@ describe("VoiceBroadcastRecordingPip", () => {
|
||||||
describe("and clicking the stop button", () => {
|
describe("and clicking the stop button", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await userEvent.click(screen.getByLabelText("Stop Recording"));
|
await userEvent.click(screen.getByLabelText("Stop Recording"));
|
||||||
|
await screen.findByText("Stop live broadcasting?");
|
||||||
// modal rendering has some weird sleeps
|
// modal rendering has some weird sleeps
|
||||||
await sleep(100);
|
await sleep(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should display the confirm end dialog", () => {
|
it("should display the confirm end dialog", () => {
|
||||||
|
|
Loading…
Reference in a new issue