Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Make tests more resilient for React 18 upgrade #12861

Merged
merged 6 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion test/components/structures/MatrixChat-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1380,7 +1380,6 @@ describe("<MatrixChat />", () => {

it("while we are checking the sync store", async () => {
const rendered = getComponent({});
await flushPromises();
expect(rendered.getByTestId("spinner")).toBeInTheDocument();

// now a third session starts
Expand Down
6 changes: 3 additions & 3 deletions test/components/structures/UserMenu-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ describe("<UserMenu>", () => {

const spy = jest.spyOn(defaultDispatcher, "dispatch");
screen.getByRole("button", { name: /User menu/i }).click();
screen.getByRole("menuitem", { name: /Sign out/i }).click();
(await screen.findByRole("menuitem", { name: /Sign out/i })).click();
await waitFor(() => {
expect(spy).toHaveBeenCalledWith({ action: "logout" });
});
Expand All @@ -152,7 +152,7 @@ describe("<UserMenu>", () => {

const spy = jest.spyOn(defaultDispatcher, "dispatch");
screen.getByRole("button", { name: /User menu/i }).click();
screen.getByRole("menuitem", { name: /Sign out/i }).click();
await waitFor(() => screen.getByRole("menuitem", { name: /Sign out/i }).click());
await waitFor(() => {
expect(spy).toHaveBeenCalledWith({ action: "logout" });
});
Expand All @@ -178,7 +178,7 @@ describe("<UserMenu>", () => {

const spy = jest.spyOn(Modal, "createDialog");
screen.getByRole("button", { name: /User menu/i }).click();
screen.getByRole("menuitem", { name: /Sign out/i }).click();
await waitFor(() => screen.getByRole("menuitem", { name: /Sign out/i }).click());

await waitFor(() => {
expect(spy).toHaveBeenCalledWith(LogoutDialog);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,11 @@ describe("AccessSecretStorageDialog", () => {
expect(screen.getByPlaceholderText("Security Phrase")).toHaveValue(securityKey);
await submitDialog();

expect(
screen.getByText(
await expect(
screen.findByText(
"👎 Unable to access secret storage. Please verify that you entered the correct Security Phrase.",
),
).toBeInTheDocument();
).resolves.toBeInTheDocument();

expect(screen.getByPlaceholderText("Security Phrase")).toHaveFocus();
});
Expand Down
2 changes: 1 addition & 1 deletion test/components/views/dialogs/InviteDialog-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ describe("InviteDialog", () => {

describe("when clicking »Start DM anyway«", () => {
beforeEach(async () => {
await userEvent.click(screen.getByRole("button", { name: "Start DM anyway", exact: true }));
await userEvent.click(screen.getByRole("button", { name: "Start DM anyway" }));
});

it("should start the DM", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ limitations under the License.
*/

import React from "react";
import { render, RenderResult } from "@testing-library/react";
import { render, RenderResult, waitForElementToBeRemoved } from "@testing-library/react";
import { EventType, MatrixEvent } from "matrix-js-sdk/src/matrix";

import type { MatrixClient } from "matrix-js-sdk/src/matrix";
Expand All @@ -39,6 +39,7 @@ describe("<MessageEditHistory />", () => {

async function renderComponent(): Promise<RenderResult> {
const result = render(<MessageEditHistoryDialog mxEvent={event} onFinished={jest.fn()} />);
await waitForElementToBeRemoved(() => result.queryByRole("progressbar"));
await flushPromises();
return result;
}
Expand Down
8 changes: 5 additions & 3 deletions test/components/views/dialogs/RoomSettingsDialog-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ limitations under the License.
*/

import React from "react";
import { fireEvent, render, screen } from "@testing-library/react";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import {
EventTimeline,
EventType,
Expand Down Expand Up @@ -129,7 +129,7 @@ describe("<RoomSettingsDialog />", () => {
expect(screen.getByTestId("settings-tab-ROOM_PEOPLE_TAB")).toBeInTheDocument();
});

it("re-renders on room join rule changes", () => {
it("re-renders on room join rule changes", async () => {
jest.spyOn(SettingsStore, "getValue").mockImplementation(
(setting) => setting === "feature_ask_to_join",
);
Expand All @@ -142,7 +142,9 @@ describe("<RoomSettingsDialog />", () => {
room.getLiveTimeline().getState(EventTimeline.FORWARDS)!,
null,
);
expect(screen.queryByTestId("settings-tab-ROOM_PEOPLE_TAB")).not.toBeInTheDocument();
await waitFor(() =>
expect(screen.queryByTestId("settings-tab-ROOM_PEOPLE_TAB")).not.toBeInTheDocument(),
);
});
});

Expand Down
38 changes: 14 additions & 24 deletions test/components/views/dialogs/UserSettingsDialog-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,9 @@ jest.mock("../../../../src/settings/SettingsStore", () => ({
settingIsOveriddenAtConfigLevel: jest.fn(),
}));

jest.mock("../../../../src/SdkConfig", () => ({
get: jest.fn(),
}));

describe("<UserSettingsDialog />", () => {
const userId = "@alice:server.org";
const mockSettingsStore = mocked(SettingsStore);
const mockSdkConfig = mocked(SdkConfig);
let mockClient!: MockedObject<MatrixClient>;

let sdkContext: SdkContextClass;
Expand All @@ -89,7 +84,8 @@ describe("<UserSettingsDialog />", () => {
mockSettingsStore.getValue.mockReturnValue(false);
mockSettingsStore.getValueAt.mockReturnValue(false);
mockSettingsStore.getFeatureSettingNames.mockReturnValue([]);
mockSdkConfig.get.mockReturnValue({ brand: "Test" });
SdkConfig.reset();
SdkConfig.put({ brand: "Test" });
});

const getActiveTabLabel = (container: Element) =>
Expand All @@ -115,6 +111,9 @@ describe("<UserSettingsDialog />", () => {
});

it("renders tabs correctly", () => {
SdkConfig.add({
show_labs_settings: true,
});
const { container } = render(getComponent());
expect(container.querySelectorAll(".mx_TabbedView_tabLabel")).toMatchSnapshot();
});
Expand Down Expand Up @@ -181,26 +180,16 @@ describe("<UserSettingsDialog />", () => {
expect(screen.getByRole("heading", { level: 1 })).toHaveTextContent("Settings: Voice & Video");
});

it("renders with secutity tab selected", () => {
it("renders with security tab selected", () => {
const { container } = render(getComponent({ initialTabId: UserTab.Security }));

expect(getActiveTabLabel(container)).toEqual("Security & Privacy");
expect(screen.getByRole("heading", { level: 1 })).toHaveTextContent("Settings: Security & Privacy");
});

it("renders with labs tab selected", () => {
// @ts-ignore I give up trying to get the types right here
// why do we have functions that return different things depending on what they're passed?
mockSdkConfig.get.mockImplementation((x) => {
const mockConfig = { show_labs_settings: true, brand: "Test" };
switch (x) {
case "show_labs_settings":
case "brand":
// @ts-ignore
return mockConfig[x];
default:
return mockConfig;
}
SdkConfig.add({
show_labs_settings: true,
});
const { container } = render(getComponent({ initialTabId: UserTab.Labs }));

Expand All @@ -223,8 +212,9 @@ describe("<UserSettingsDialog />", () => {
});

it("renders labs tab when show_labs_settings is enabled in config", () => {
// @ts-ignore simplified test stub
mockSdkConfig.get.mockImplementation((configName) => configName === "show_labs_settings");
SdkConfig.add({
show_labs_settings: true,
});
const { getByTestId } = render(getComponent());
expect(getByTestId(`settings-tab-${UserTab.Labs}`)).toBeTruthy();
});
Expand All @@ -238,7 +228,7 @@ describe("<UserSettingsDialog />", () => {
expect(getByTestId(`settings-tab-${UserTab.Labs}`)).toBeTruthy();
});

it("watches settings", () => {
it("watches settings", async () => {
const watchSettingCallbacks: Record<string, CallbackFn> = {};

mockSettingsStore.watchSetting.mockImplementation((settingName, roomId, callback) => {
Expand All @@ -247,7 +237,7 @@ describe("<UserSettingsDialog />", () => {
});
mockSettingsStore.getValue.mockReturnValue(false);

const { queryByTestId, unmount } = render(getComponent());
const { queryByTestId, findByTestId, unmount } = render(getComponent());
expect(queryByTestId(`settings-tab-${UserTab.Mjolnir}`)).toBeFalsy();

expect(mockSettingsStore.watchSetting).toHaveBeenCalledWith("feature_mjolnir", null, expect.anything());
Expand All @@ -257,7 +247,7 @@ describe("<UserSettingsDialog />", () => {
watchSettingCallbacks["feature_mjolnir"]("feature_mjolnir", "", SettingLevel.ACCOUNT, true, true);

// tab is rendered now
expect(queryByTestId(`settings-tab-${UserTab.Mjolnir}`)).toBeTruthy();
await expect(findByTestId(`settings-tab-${UserTab.Mjolnir}`)).resolves.toBeTruthy();

unmount();

Expand Down
20 changes: 7 additions & 13 deletions test/components/views/elements/Field-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ limitations under the License.
*/

import React from "react";
import { act, fireEvent, render, screen } from "@testing-library/react";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";

import Field from "../../../../src/components/views/elements/Field";

Expand Down Expand Up @@ -63,12 +63,10 @@ describe("Field", () => {
);

// When invalid
await act(async () => {
fireEvent.focus(screen.getByRole("textbox"));
});
fireEvent.focus(screen.getByRole("textbox"));

// Expect 'alert' role
expect(screen.queryByRole("alert")).toBeInTheDocument();
await waitFor(() => expect(screen.getByRole("alert")).toBeInTheDocument());

// Close the feedback is Escape is pressed
fireEvent.keyDown(screen.getByRole("textbox"), { key: "Escape" });
Expand All @@ -85,12 +83,10 @@ describe("Field", () => {
);

// When valid
await act(async () => {
fireEvent.focus(screen.getByRole("textbox"));
});
fireEvent.focus(screen.getByRole("textbox"));

// Expect 'status' role
expect(screen.queryByRole("status")).toBeInTheDocument();
await waitFor(() => expect(screen.queryByRole("status")).toBeInTheDocument());

// Close the feedback is Escape is pressed
fireEvent.keyDown(screen.getByRole("textbox"), { key: "Escape" });
Expand All @@ -108,12 +104,10 @@ describe("Field", () => {
);

// When valid or invalid and 'tooltipContent' set
await act(async () => {
fireEvent.focus(screen.getByRole("textbox"));
});
fireEvent.focus(screen.getByRole("textbox"));

// Expect 'tooltip' role
expect(screen.queryByRole("tooltip")).toBeInTheDocument();
await waitFor(() => expect(screen.queryByRole("tooltip")).toBeInTheDocument());

// Close the feedback is Escape is pressed
fireEvent.keyDown(screen.getByRole("textbox"), { key: "Escape" });
Expand Down
4 changes: 2 additions & 2 deletions test/components/views/elements/SearchWarning-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ describe("<SearchWarning />", () => {
});

it("renders with a logo by default", () => {
const { asFragment, queryByRole } = render(
const { asFragment, getByRole } = render(
<SearchWarning isRoomEncrypted={true} kind={WarningKind.Search} />,
);
expect(queryByRole("img")).toBeInTheDocument();
expect(getByRole("img")).toHaveAttribute("src", "https://logo");
expect(asFragment()).toMatchSnapshot();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ import PlatformPeg from "../../../../src/PlatformPeg";

describe("<SpellCheckLanguagesDropdown />", () => {
it("renders as expected", async () => {
const platform: any = { getAvailableSpellCheckLanguages: jest.fn().mockResolvedValue(["en", "de", "qq"]) };
const platform: any = {
getAvailableSpellCheckLanguages: jest.fn().mockResolvedValue(["en", "de", "qq"]),
supportsSetting: jest.fn(),
};
PlatformPeg.set(platform);

const { asFragment } = render(
Expand Down
2 changes: 1 addition & 1 deletion test/components/views/messages/MImageBody-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ describe("<MImageBody/>", () => {
it("should generate a thumbnail if one isn't included for animated media", async () => {
Object.defineProperty(global.Image.prototype, "src", {
set(src) {
window.setTimeout(() => this.onload());
window.setTimeout(() => this.onload?.());
},
});
Object.defineProperty(global.Image.prototype, "height", {
Expand Down
14 changes: 9 additions & 5 deletions test/components/views/messages/MLocationBody-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { fireEvent, render, waitFor } from "@testing-library/react";
import { LocationAssetType, ClientEvent, RoomMember, SyncState } from "matrix-js-sdk/src/matrix";
import * as maplibregl from "maplibre-gl";
import { logger } from "matrix-js-sdk/src/logger";
import { sleep } from "matrix-js-sdk/src/utils";

import MLocationBody from "../../../../src/components/views/messages/MLocationBody";
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
Expand Down Expand Up @@ -64,9 +65,11 @@ describe("MLocationBody", () => {
});
const component = getComponent();

// simulate error initialising map in maplibregl
// @ts-ignore
mockMap.emit("error", { status: 404 });
sleep(10).then(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the sleep?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Timings change in React 18 and otherwise this error was emitted too early and missed by the component being rendered

// simulate error initialising map in maplibregl
// @ts-ignore
mockMap.emit("error", { status: 404 });
});

return component;
};
Expand Down Expand Up @@ -100,9 +103,10 @@ describe("MLocationBody", () => {
expect(component.container.querySelector(".mx_EventTile_body")).toMatchSnapshot();
});

it("displays correct fallback content when map_style_url is misconfigured", () => {
it("displays correct fallback content when map_style_url is misconfigured", async () => {
const component = getMapErrorComponent();
expect(component.container.querySelector(".mx_EventTile_body")).toMatchSnapshot();
await waitFor(() => expect(component.container.querySelector(".mx_EventTile_body")).toBeTruthy());
await waitFor(() => expect(component.container.querySelector(".mx_EventTile_body")).toMatchSnapshot());
});

it("should clear the error on reconnect", () => {
Expand Down
7 changes: 3 additions & 4 deletions test/components/views/messages/MPollEndBody-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ limitations under the License.
*/

import React from "react";
import { render } from "@testing-library/react";
import { render, waitFor } from "@testing-library/react";
import { EventTimeline, MatrixEvent, Room, M_TEXT } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";

Expand Down Expand Up @@ -129,13 +129,12 @@ describe("<MPollEndBody />", () => {
describe("when poll start event does not exist in current timeline", () => {
it("fetches the related poll start event and displays a poll tile", async () => {
await setupRoomWithEventsTimeline(pollEndEvent);
const { container, getByTestId } = getComponent();
const { container, getByTestId, getByRole } = getComponent();

// while fetching event, only icon is shown
expect(container).toMatchSnapshot();

// flush the fetch event promise
await flushPromises();
await waitFor(() => expect(getByRole("progressbar")).toBeInTheDocument());

expect(mockClient.fetchRoomEvent).toHaveBeenCalledWith(roomId, pollStartEvent.getId());

Expand Down
Loading
Loading