More tests for UserInfo (#10677)

* UserInfo-test: move mocking to `beforeEach`

... so that changes to the mocks do not leak between tests

* Add some more tests for UserInfo
pull/28788/head^2
Richard van der Hoff 2023-04-21 14:48:27 +01:00 committed by GitHub
parent 893feed13b
commit 83f12fcba0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 171 additions and 93 deletions

View File

@ -15,9 +15,9 @@ limitations under the License.
*/ */
import React from "react"; import React from "react";
import { fireEvent, render, screen, waitFor, cleanup, act } from "@testing-library/react"; import { fireEvent, render, screen, waitFor, cleanup, act, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event"; import userEvent from "@testing-library/user-event";
import { mocked } from "jest-mock"; import { Mocked, mocked } from "jest-mock";
import { Room, User, MatrixClient, RoomMember, MatrixEvent, EventType } from "matrix-js-sdk/src/matrix"; import { Room, User, MatrixClient, RoomMember, MatrixEvent, EventType } from "matrix-js-sdk/src/matrix";
import { Phase, VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; import { Phase, VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import { DeviceTrustLevel, UserTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning"; import { DeviceTrustLevel, UserTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning";
@ -72,73 +72,78 @@ jest.mock("../../../../src/utils/DMRoomMap", () => {
}; };
}); });
const mockRoom = mocked({ const defaultRoomId = "!fkfk";
roomId: "!fkfk",
getType: jest.fn().mockReturnValue(undefined),
isSpaceRoom: jest.fn().mockReturnValue(false),
getMember: jest.fn().mockReturnValue(undefined),
getMxcAvatarUrl: jest.fn().mockReturnValue("mock-avatar-url"),
name: "test room",
on: jest.fn(),
off: jest.fn(),
currentState: {
getStateEvents: jest.fn(),
on: jest.fn(),
},
getEventReadUpTo: jest.fn(),
} as unknown as Room);
const mockSpace = mocked({
roomId: "!fkfk",
getType: jest.fn().mockReturnValue("m.space"),
isSpaceRoom: jest.fn().mockReturnValue(true),
getMember: jest.fn().mockReturnValue(undefined),
getMxcAvatarUrl: jest.fn().mockReturnValue("mock-avatar-url"),
name: "test room",
on: jest.fn(),
off: jest.fn(),
currentState: {
getStateEvents: jest.fn(),
on: jest.fn(),
},
getEventReadUpTo: jest.fn(),
} as unknown as Room);
const mockClient = mocked({
getUser: jest.fn(),
isGuest: jest.fn().mockReturnValue(false),
isUserIgnored: jest.fn(),
getIgnoredUsers: jest.fn(),
setIgnoredUsers: jest.fn(),
isCryptoEnabled: jest.fn(),
getUserId: jest.fn(),
getSafeUserId: jest.fn(),
on: jest.fn(),
off: jest.fn(),
isSynapseAdministrator: jest.fn().mockResolvedValue(false),
isRoomEncrypted: jest.fn().mockReturnValue(false),
doesServerSupportUnstableFeature: jest.fn().mockReturnValue(false),
mxcUrlToHttp: jest.fn().mockReturnValue("mock-mxcUrlToHttp"),
removeListener: jest.fn(),
currentState: {
on: jest.fn(),
},
checkDeviceTrust: jest.fn(),
checkUserTrust: jest.fn(),
getRoom: jest.fn(),
credentials: {},
setPowerLevel: jest.fn(),
} as unknown as MatrixClient);
const defaultUserId = "@user:example.com"; const defaultUserId = "@user:example.com";
const defaultUser = new User(defaultUserId); const defaultUser = new User(defaultUserId);
beforeEach(() => { let mockRoom: Mocked<Room>;
jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient); let mockSpace: Mocked<Room>;
}); let mockClient: Mocked<MatrixClient>;
afterEach(() => { beforeEach(() => {
mockClient.getUser.mockClear().mockReturnValue({} as unknown as User); mockRoom = mocked({
roomId: defaultRoomId,
getType: jest.fn().mockReturnValue(undefined),
isSpaceRoom: jest.fn().mockReturnValue(false),
getMember: jest.fn().mockReturnValue(undefined),
getMxcAvatarUrl: jest.fn().mockReturnValue("mock-avatar-url"),
name: "test room",
on: jest.fn(),
off: jest.fn(),
currentState: {
getStateEvents: jest.fn(),
on: jest.fn(),
off: jest.fn(),
},
getEventReadUpTo: jest.fn(),
} as unknown as Room);
mockSpace = mocked({
roomId: defaultRoomId,
getType: jest.fn().mockReturnValue("m.space"),
isSpaceRoom: jest.fn().mockReturnValue(true),
getMember: jest.fn().mockReturnValue(undefined),
getMxcAvatarUrl: jest.fn().mockReturnValue("mock-avatar-url"),
name: "test room",
on: jest.fn(),
off: jest.fn(),
currentState: {
getStateEvents: jest.fn(),
on: jest.fn(),
off: jest.fn(),
},
getEventReadUpTo: jest.fn(),
} as unknown as Room);
mockClient = mocked({
getUser: jest.fn(),
isGuest: jest.fn().mockReturnValue(false),
isUserIgnored: jest.fn(),
getIgnoredUsers: jest.fn(),
setIgnoredUsers: jest.fn(),
isCryptoEnabled: jest.fn(),
getUserId: jest.fn(),
getSafeUserId: jest.fn(),
on: jest.fn(),
off: jest.fn(),
isSynapseAdministrator: jest.fn().mockResolvedValue(false),
isRoomEncrypted: jest.fn().mockReturnValue(false),
doesServerSupportUnstableFeature: jest.fn().mockReturnValue(false),
mxcUrlToHttp: jest.fn().mockReturnValue("mock-mxcUrlToHttp"),
removeListener: jest.fn(),
currentState: {
on: jest.fn(),
},
checkDeviceTrust: jest.fn(),
checkUserTrust: jest.fn(),
getRoom: jest.fn(),
credentials: {},
setPowerLevel: jest.fn(),
downloadKeys: jest.fn(),
getStoredDevicesForUser: jest.fn(),
} as unknown as MatrixClient);
jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient);
}); });
describe("<UserInfo />", () => { describe("<UserInfo />", () => {
@ -241,14 +246,76 @@ describe("<UserInfo />", () => {
expect(screen.getByText(/try with a different client/i)).toBeInTheDocument(); expect(screen.getByText(/try with a different client/i)).toBeInTheDocument();
}); });
}); });
describe("with crypto enabled", () => {
beforeEach(() => {
mockClient.isCryptoEnabled.mockReturnValue(true);
mockClient.checkUserTrust.mockReturnValue(new UserTrustLevel(false, false, false));
mockClient.checkDeviceTrust.mockReturnValue(new DeviceTrustLevel(false, false, false, false));
const device1 = DeviceInfo.fromStorage(
{
unsigned: { device_display_name: "my device" },
},
"d1",
);
mockClient.getStoredDevicesForUser.mockReturnValue([device1]);
});
it("renders a device list which can be expanded", async () => {
renderComponent();
await act(flushPromises);
// check the button exists with the expected text
const devicesButton = screen.getByRole("button", { name: "1 session" });
// click it
await userEvent.click(devicesButton);
// there should now be a button with the device id ...
const deviceButton = screen.getByRole("button", { description: "d1" });
// ... which should contain the device name
expect(within(deviceButton).getByText("my device")).toBeInTheDocument();
});
});
describe("with an encrypted room", () => {
beforeEach(() => {
mockClient.isCryptoEnabled.mockReturnValue(true);
mockClient.isRoomEncrypted.mockReturnValue(true);
});
it("renders unverified user info", async () => {
mockClient.checkUserTrust.mockReturnValue(new UserTrustLevel(false, false, false));
renderComponent({ room: mockRoom });
await act(flushPromises);
const userHeading = screen.getByRole("heading", { name: defaultUserId });
// there should be a "normal" E2E padlock
expect(userHeading.getElementsByClassName("mx_E2EIcon_normal")).toHaveLength(1);
});
it("renders verified user info", async () => {
mockClient.checkUserTrust.mockReturnValue(new UserTrustLevel(true, false, false));
renderComponent({ room: mockRoom });
await act(flushPromises);
const userHeading = screen.getByRole("heading", { name: defaultUserId });
// there should be a "verified" E2E padlock
expect(userHeading.getElementsByClassName("mx_E2EIcon_verified")).toHaveLength(1);
});
});
}); });
describe("<UserInfoHeader />", () => { describe("<UserInfoHeader />", () => {
const defaultMember = new RoomMember(mockRoom.roomId, defaultUserId); const defaultMember = new RoomMember(defaultRoomId, defaultUserId);
const defaultProps = { const defaultProps = {
member: defaultMember, member: defaultMember,
roomId: mockRoom.roomId, roomId: defaultRoomId,
}; };
const renderComponent = (props = {}) => { const renderComponent = (props = {}) => {
@ -399,7 +466,7 @@ describe("<DeviceItem />", () => {
}); });
describe("<UserOptionsSection />", () => { describe("<UserOptionsSection />", () => {
const member = new RoomMember(mockRoom.roomId, defaultUserId); const member = new RoomMember(defaultRoomId, defaultUserId);
const defaultProps = { member, isIgnored: false, canInvite: false, isSpace: false }; const defaultProps = { member, isIgnored: false, canInvite: false, isSpace: false };
const renderComponent = (props = {}) => { const renderComponent = (props = {}) => {
@ -644,17 +711,20 @@ describe("<UserOptionsSection />", () => {
}); });
describe("<PowerLevelEditor />", () => { describe("<PowerLevelEditor />", () => {
const defaultMember = new RoomMember(mockRoom.roomId, defaultUserId); const defaultMember = new RoomMember(defaultRoomId, defaultUserId);
const defaultProps = { let defaultProps: Parameters<typeof PowerLevelEditor>[0];
user: defaultMember, beforeEach(() => {
room: mockRoom, defaultProps = {
roomPermissions: { user: defaultMember,
modifyLevelMax: 100, room: mockRoom,
canEdit: false, roomPermissions: {
canInvite: false, modifyLevelMax: 100,
}, canEdit: false,
}; canInvite: false,
},
};
});
const renderComponent = (props = {}) => { const renderComponent = (props = {}) => {
const Wrapper = (wrapperProps = {}) => { const Wrapper = (wrapperProps = {}) => {
@ -705,11 +775,14 @@ describe("<PowerLevelEditor />", () => {
}); });
describe("<RoomKickButton />", () => { describe("<RoomKickButton />", () => {
const defaultMember = new RoomMember(mockRoom.roomId, defaultUserId); const defaultMember = new RoomMember(defaultRoomId, defaultUserId);
const memberWithInviteMembership = { ...defaultMember, membership: "invite" }; const memberWithInviteMembership = { ...defaultMember, membership: "invite" };
const memberWithJoinMembership = { ...defaultMember, membership: "join" }; const memberWithJoinMembership = { ...defaultMember, membership: "join" };
const defaultProps = { room: mockRoom, member: defaultMember, startUpdating: jest.fn(), stopUpdating: jest.fn() }; let defaultProps: Parameters<typeof RoomKickButton>[0];
beforeEach(() => {
defaultProps = { room: mockRoom, member: defaultMember, startUpdating: jest.fn(), stopUpdating: jest.fn() };
});
const renderComponent = (props = {}) => { const renderComponent = (props = {}) => {
const Wrapper = (wrapperProps = {}) => { const Wrapper = (wrapperProps = {}) => {
@ -805,10 +878,12 @@ describe("<RoomKickButton />", () => {
}); });
describe("<BanToggleButton />", () => { describe("<BanToggleButton />", () => {
const defaultMember = new RoomMember(mockRoom.roomId, defaultUserId); const defaultMember = new RoomMember(defaultRoomId, defaultUserId);
const memberWithBanMembership = { ...defaultMember, membership: "ban" }; const memberWithBanMembership = { ...defaultMember, membership: "ban" };
let defaultProps: Parameters<typeof BanToggleButton>[0];
const defaultProps = { room: mockRoom, member: defaultMember, startUpdating: jest.fn(), stopUpdating: jest.fn() }; beforeEach(() => {
defaultProps = { room: mockRoom, member: defaultMember, startUpdating: jest.fn(), stopUpdating: jest.fn() };
});
const renderComponent = (props = {}) => { const renderComponent = (props = {}) => {
const Wrapper = (wrapperProps = {}) => { const Wrapper = (wrapperProps = {}) => {
@ -927,16 +1002,19 @@ describe("<BanToggleButton />", () => {
}); });
describe("<RoomAdminToolsContainer />", () => { describe("<RoomAdminToolsContainer />", () => {
const defaultMember = new RoomMember(mockRoom.roomId, defaultUserId); const defaultMember = new RoomMember(defaultRoomId, defaultUserId);
defaultMember.membership = "invite"; defaultMember.membership = "invite";
const defaultProps = { let defaultProps: Parameters<typeof RoomAdminToolsContainer>[0];
room: mockRoom, beforeEach(() => {
member: defaultMember, defaultProps = {
startUpdating: jest.fn(), room: mockRoom,
stopUpdating: jest.fn(), member: defaultMember,
powerLevels: {}, startUpdating: jest.fn(),
}; stopUpdating: jest.fn(),
powerLevels: {},
};
});
const renderComponent = (props = {}) => { const renderComponent = (props = {}) => {
const Wrapper = (wrapperProps = {}) => { const Wrapper = (wrapperProps = {}) => {
@ -1039,7 +1117,7 @@ describe("disambiguateDevices", () => {
describe("isMuted", () => { describe("isMuted", () => {
// this member has a power level of 0 // this member has a power level of 0
const isMutedMember = new RoomMember(mockRoom.roomId, defaultUserId); const isMutedMember = new RoomMember(defaultRoomId, defaultUserId);
it("returns false if either argument is falsy", () => { it("returns false if either argument is falsy", () => {
// @ts-ignore to let us purposely pass incorrect args // @ts-ignore to let us purposely pass incorrect args

View File

@ -127,7 +127,7 @@ export function untilEmission(
}); });
} }
export const flushPromises = async () => await new Promise((resolve) => window.setTimeout(resolve)); export const flushPromises = async () => await new Promise<void>((resolve) => window.setTimeout(resolve));
// with jest's modern fake timers process.nextTick is also mocked, // with jest's modern fake timers process.nextTick is also mocked,
// flushing promises in the normal way then waits for some advancement // flushing promises in the normal way then waits for some advancement