Fix flaky jest tests (#12486)

...and remove the code that causes them to be retried in CI. Most of
these were just lack of waiting for async things to happen, mostly
lazy loading components, hence whythey worked on the retry: because
the code had been loaded by then.
This commit is contained in:
David Baker 2024-05-02 15:53:35 +01:00 committed by GitHub
parent 7193d4c695
commit 374cee9080
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 36 additions and 27 deletions

View file

@ -197,7 +197,9 @@ describe("ThreadView", () => {
it("sets the correct thread in the room view store", async () => { it("sets the correct thread in the room view store", async () => {
// expect(SdkContextClass.instance.roomViewStore.getThreadId()).toBeNull(); // expect(SdkContextClass.instance.roomViewStore.getThreadId()).toBeNull();
const { unmount } = await getComponent(); const { unmount } = await getComponent();
waitFor(() => {
expect(SdkContextClass.instance.roomViewStore.getThreadId()).toBe(rootEvent.getId()); expect(SdkContextClass.instance.roomViewStore.getThreadId()).toBe(rootEvent.getId());
});
unmount(); unmount();
await waitFor(() => expect(SdkContextClass.instance.roomViewStore.getThreadId()).toBeNull()); await waitFor(() => expect(SdkContextClass.instance.roomViewStore.getThreadId()).toBeNull());

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import React from "react"; import React from "react";
import { act, render, screen } from "@testing-library/react"; import { act, render, screen, waitFor } from "@testing-library/react";
import * as maplibregl from "maplibre-gl"; import * as maplibregl from "maplibre-gl";
import { Beacon, Room, RoomMember, MatrixEvent, getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix"; import { Beacon, Room, RoomMember, MatrixEvent, getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix";
@ -111,14 +111,16 @@ describe("<BeaconMarker />", () => {
expect(screen.queryByTestId("avatar-img")).not.toBeInTheDocument(); expect(screen.queryByTestId("avatar-img")).not.toBeInTheDocument();
}); });
it("renders marker when beacon has location", () => { it("renders marker when beacon has location", async () => {
const room = setupRoom([defaultEvent]); const room = setupRoom([defaultEvent]);
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent)); const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent));
beacon?.addLocations([location1]); beacon?.addLocations([location1]);
const { asFragment } = renderComponent({ beacon }); const { asFragment } = renderComponent({ beacon });
expect(asFragment()).toMatchSnapshot(); await waitFor(() => {
expect(screen.getByTestId("avatar-img")).toBeInTheDocument(); expect(screen.getByTestId("avatar-img")).toBeInTheDocument();
}); });
expect(asFragment()).toMatchSnapshot();
});
it("updates with new locations", () => { it("updates with new locations", () => {
const lonLat1 = { lon: 41, lat: 51 }; const lonLat1 = { lon: 41, lat: 51 };

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import React from "react"; import React from "react";
import { act, fireEvent, render, RenderResult } from "@testing-library/react"; import { act, fireEvent, render, RenderResult, waitFor } from "@testing-library/react";
import { MatrixClient, MatrixEvent, Room, RoomMember, getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix"; import { MatrixClient, MatrixEvent, Room, RoomMember, getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix";
import * as maplibregl from "maplibre-gl"; import * as maplibregl from "maplibre-gl";
import { mocked } from "jest-mock"; import { mocked } from "jest-mock";
@ -92,7 +92,7 @@ describe("<BeaconViewDialog />", () => {
jest.clearAllMocks(); jest.clearAllMocks();
}); });
it("renders a map with markers", () => { it("renders a map with markers", async () => {
const room = setupRoom([defaultEvent]); const room = setupRoom([defaultEvent]);
const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!; const beacon = room.currentState.beacons.get(getBeaconInfoIdentifier(defaultEvent))!;
beacon.addLocations([location1]); beacon.addLocations([location1]);
@ -103,8 +103,10 @@ describe("<BeaconViewDialog />", () => {
lat: 51, lat: 51,
}); });
// marker added // marker added
await waitFor(() => {
expect(mockMarker.addTo).toHaveBeenCalledWith(mockMap); expect(mockMarker.addTo).toHaveBeenCalledWith(mockMap);
}); });
});
it("does not render any own beacon status when user is not live sharing", () => { it("does not render any own beacon status when user is not live sharing", () => {
// default event belongs to alice, we are bob // default event belongs to alice, we are bob

View file

@ -130,9 +130,9 @@ describe("ForwardDialog", () => {
expect(container.querySelectorAll(".mx_ForwardList_entry")).toHaveLength(3); expect(container.querySelectorAll(".mx_ForwardList_entry")).toHaveLength(3);
const searchInput = getByTestId(container, "searchbox-input"); const searchInput = getByTestId(container, "searchbox-input");
act(() => userEvent.type(searchInput, "a")); await userEvent.type(searchInput, "a");
expect(container.querySelectorAll(".mx_ForwardList_entry")).toHaveLength(3); expect(container.querySelectorAll(".mx_ForwardList_entry")).toHaveLength(2);
}); });
it("should be navigable using arrow keys", async () => { it("should be navigable using arrow keys", async () => {

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import React, { ComponentProps } from "react"; import React, { ComponentProps } from "react";
import { fireEvent, render } from "@testing-library/react"; import { fireEvent, render, waitFor } from "@testing-library/react";
import { LocationAssetType, ClientEvent, RoomMember, SyncState } from "matrix-js-sdk/src/matrix"; import { LocationAssetType, ClientEvent, RoomMember, SyncState } from "matrix-js-sdk/src/matrix";
import * as maplibregl from "maplibre-gl"; import * as maplibregl from "maplibre-gl";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
@ -90,8 +90,13 @@ describe("MLocationBody", () => {
jest.spyOn(logger, "error").mockRestore(); jest.spyOn(logger, "error").mockRestore();
}); });
it("displays correct fallback content without error style when map_style_url is not configured", () => { it("displays correct fallback content without error style when map_style_url is not configured", async () => {
const component = getComponent(); const component = getComponent();
// The map code needs to be lazy loaded so this will take some time to appear
await waitFor(() =>
expect(component.container.querySelector(".mx_EventTile_body")).toBeInTheDocument(),
);
expect(component.container.querySelector(".mx_EventTile_body")).toMatchSnapshot(); expect(component.container.querySelector(".mx_EventTile_body")).toMatchSnapshot();
}); });

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import React from "react"; import React from "react";
import { render, screen } from "@testing-library/react"; import { render, screen, waitFor } from "@testing-library/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";
@ -82,7 +82,7 @@ describe("MessageComposerButtons", () => {
expect(getButtonLabels()).toEqual(["Emoji", "Attachment", "More options"]); expect(getButtonLabels()).toEqual(["Emoji", "Attachment", "More options"]);
}); });
it("Renders other buttons in menu in wide mode", () => { it("Renders other buttons in menu in wide mode", async () => {
wrapAndRender( wrapAndRender(
<MessageComposerButtons <MessageComposerButtons
{...mockProps} {...mockProps}
@ -94,6 +94,9 @@ describe("MessageComposerButtons", () => {
false, false,
); );
// The location code is lazy loaded, so the button will take a little while
// to appear, so we need to wait.
await waitFor(() => {
expect(getButtonLabels()).toEqual([ expect(getButtonLabels()).toEqual([
"Emoji", "Emoji",
"Attachment", "Attachment",
@ -101,6 +104,7 @@ describe("MessageComposerButtons", () => {
["Sticker", "Voice Message", "Poll", "Location"], ["Sticker", "Voice Message", "Poll", "Location"],
]); ]);
}); });
});
it("Renders only some buttons in narrow mode", () => { it("Renders only some buttons in narrow mode", () => {
wrapAndRender( wrapAndRender(

View file

@ -30,7 +30,7 @@ import {
ThreepidMedium, ThreepidMedium,
} from "matrix-js-sdk/src/matrix"; } from "matrix-js-sdk/src/matrix";
import { randomString } from "matrix-js-sdk/src/randomstring"; import { randomString } from "matrix-js-sdk/src/randomstring";
import { act, fireEvent, getByTestId, render, screen, within } from "@testing-library/react"; import { act, fireEvent, getByTestId, render, screen, waitFor, within } from "@testing-library/react";
import { mocked } from "jest-mock"; import { mocked } from "jest-mock";
import userEvent from "@testing-library/user-event"; import userEvent from "@testing-library/user-event";
@ -907,6 +907,7 @@ describe("<Notifications />", () => {
fireEvent.click(clearNotificationEl); fireEvent.click(clearNotificationEl);
expect(clearNotificationEl.className).toContain("mx_AccessibleButton_disabled"); expect(clearNotificationEl.className).toContain("mx_AccessibleButton_disabled");
await waitFor(() => expect(clearNotificationEl.className).not.toContain("mx_AccessibleButton_disabled"));
expect(mockClient.sendReadReceipt).toHaveBeenCalled(); expect(mockClient.sendReadReceipt).toHaveBeenCalled();
}); });
}); });

View file

@ -37,13 +37,6 @@ beforeEach(() => {
}); });
}); });
// Retry to work around our flaky app & tests
if (process.env.CI) {
jest.retryTimes(2, {
logErrorsBeforeRetry: true,
});
}
// Very carefully enable the mocks for everything else in // Very carefully enable the mocks for everything else in
// a specific order. We use this order to ensure we properly // a specific order. We use this order to ensure we properly
// establish an application state that actually works. // establish an application state that actually works.