Deflake MatrixChat-test (#31383)

Add a workaround for the fact that MatrixChat attempts to use React state for
the state of a state machine: a small `sleep` to let the state settle.

As a result, it turns out we may not see the "Syncing..." state, and in general
`waitForSyncAndLoad` doesn't seem to be doing anything useful.
This commit is contained in:
Richard van der Hoff 2025-12-02 10:42:15 +00:00 committed by GitHub
parent 6228edcd67
commit 57fd3c46bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -96,7 +96,17 @@ describe("<MatrixChat />", () => {
...mockClientMethodsUser(userId),
...mockClientMethodsServer(),
getVersions: jest.fn().mockResolvedValue({ versions: SERVER_SUPPORTED_MATRIX_VERSIONS }),
startClient: function () {
startClient: async function () {
// This `sleep` is a horrible hack, for which I am sorry.
//
// MatrixChat uses its `view` state as the tracker for the current state of its state machine. However,
// React state does not update immediately, with the result that if the client starts too quickly after the
// "OnLoggedIn" action, the state machine will be confused and the view will not be correctly updated.
//
// In practice it takes a little time for the client to start up (it has to read a load of stuff from
// indexedDB, so in some ways this is just a more realistic simulation of the real world 😇
await sleep(1);
// @ts-ignore
this.emit(ClientEvent.Sync, SyncState.Prepared, null);
},
@ -195,33 +205,6 @@ describe("<MatrixChat />", () => {
localStorage.setItem("mx_device_id", deviceId);
}
/**
* Wait for a bunch of stuff to happen
* between deciding we are logged in and removing the spinner
* including waiting for initial sync
*/
const waitForSyncAndLoad = async (client: MatrixClient, withoutSecuritySetup?: boolean): Promise<void> => {
// need to wait for different elements depending on which flow
// without security setup we go to a loading page
if (withoutSecuritySetup) {
// wait for logged in view to load
await screen.findByLabelText("User menu");
// otherwise we stay on login and load from there for longer
} else {
// we are logged in, but are still waiting for the /sync to complete
await screen.findByText("Syncing…");
// initial sync
await act(() => client.emit(ClientEvent.Sync, SyncState.Prepared, null));
}
// let things settle
await flushPromises();
// and some more for good measure
// this proved to be a little flaky
await flushPromises();
};
beforeEach(async () => {
await clearStorage();
Lifecycle.setSessionLockNotStolen();
@ -1281,7 +1264,7 @@ describe("<MatrixChat />", () => {
return renderResult;
};
const getComponentAndLogin = async (withoutSecuritySetup?: boolean): Promise<void> => {
const getComponentAndLogin = async (): Promise<void> => {
await getComponentAndWaitForReady();
fireEvent.change(screen.getByLabelText("Username"), { target: { value: userName } });
@ -1289,8 +1272,6 @@ describe("<MatrixChat />", () => {
// sign in button is an input
fireEvent.click(screen.getByDisplayValue("Sign in"));
await waitForSyncAndLoad(loginClient, withoutSecuritySetup);
};
beforeEach(() => {
@ -1337,12 +1318,15 @@ describe("<MatrixChat />", () => {
it("should go straight to logged in view when crypto is not enabled", async () => {
loginClient.getCrypto.mockReturnValue(undefined);
await getComponentAndLogin(true);
await getComponentAndLogin();
// wait for logged in view to load
await screen.findByLabelText("User menu");
expect(screen.getByRole("heading", { name: "Welcome Ernie" })).toBeInTheDocument();
});
describe("when server supports cross signing and user does not have cross signing setup", () => {
describe("when user does not have cross signing set up", () => {
beforeEach(() => {
jest.spyOn(loginClient.getCrypto()!, "userHasCrossSigningKeys").mockResolvedValue(false);
});
@ -1370,9 +1354,7 @@ describe("<MatrixChat />", () => {
it("should go straight to logged in view when user is not in any encrypted rooms", async () => {
loginClient.getRooms.mockReturnValue([unencryptedRoom]);
await getComponentAndLogin(false);
await flushPromises();
await getComponentAndLogin();
// logged in, did not set up keys
await screen.findByLabelText("User menu");
@ -1381,19 +1363,18 @@ describe("<MatrixChat />", () => {
it("should go to set up e2e screen when user is in encrypted rooms", async () => {
loginClient.getRooms.mockReturnValue([unencryptedRoom, encryptedRoom]);
await getComponentAndLogin();
await flushPromises();
// set up keys screen is rendered
expect(screen.getByText("Setting up keys")).toBeInTheDocument();
await screen.findByText("Setting up keys");
});
});
it("should go to set up e2e screen", async () => {
await getComponentAndLogin();
expect(loginClient.getCrypto()!.userHasCrossSigningKeys).toHaveBeenCalled();
// set up keys screen is rendered
await expect(await screen.findByText("Setting up keys")).toBeInTheDocument();
await screen.findByText("Setting up keys");
expect(loginClient.getCrypto()!.userHasCrossSigningKeys).toHaveBeenCalled();
});
});
@ -1402,21 +1383,19 @@ describe("<MatrixChat />", () => {
await getComponentAndLogin();
expect(loginClient.getCrypto()!.userHasCrossSigningKeys).toHaveBeenCalled();
await flushPromises();
// Complete security begin screen is rendered
expect(screen.getByText("Confirm your identity")).toBeInTheDocument();
await screen.findByText("Confirm your identity");
expect(loginClient.getCrypto()!.userHasCrossSigningKeys).toHaveBeenCalled();
});
it("should set up e2e", async () => {
await getComponentAndLogin();
expect(loginClient.getCrypto()!.userHasCrossSigningKeys).toHaveBeenCalled();
// set up keys screen is rendered
expect(screen.getByText("Setting up keys")).toBeInTheDocument();
await screen.findByText("Setting up keys");
expect(loginClient.getCrypto()!.userHasCrossSigningKeys).toHaveBeenCalled();
});
});
});
@ -1707,8 +1686,7 @@ describe("<MatrixChat />", () => {
jest.spyOn(MatrixJs, "createClient").mockReturnValue(client);
const rendered = getComponent({});
await waitForSyncAndLoad(client, true);
rendered.getByText("Welcome Ernie");
await rendered.findByText("Welcome Ernie");
// we're now at the welcome page. Another session wants the lock...
simulateSessionLockClaim();