mirror of https://github.com/vector-im/riot-web
Replace react-dom tests with react testing-library tests (#10260)
parent
5398db21ad
commit
e5291c195d
|
@ -68,8 +68,7 @@ interface IState {
|
|||
}
|
||||
|
||||
export default class MemberList extends React.Component<IProps, IState> {
|
||||
// XXX: exported for tests
|
||||
public showPresence = true;
|
||||
private readonly showPresence: boolean;
|
||||
private mounted = false;
|
||||
|
||||
public static contextType = SDKContext;
|
||||
|
@ -260,32 +259,6 @@ export default class MemberList extends React.Component<IProps, IState> {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* SHOULD ONLY BE USED BY TESTS
|
||||
*/
|
||||
public memberString(member: RoomMember): string {
|
||||
if (!member) {
|
||||
return "(null)";
|
||||
} else {
|
||||
const u = member.user;
|
||||
return (
|
||||
"(" +
|
||||
member.name +
|
||||
", " +
|
||||
member.powerLevel +
|
||||
", " +
|
||||
(u ? u.lastActiveAgo : "<null>") +
|
||||
", " +
|
||||
(u ? u.getLastActiveTs() : "<null>") +
|
||||
", " +
|
||||
(u ? u.currentlyActive : "<null>") +
|
||||
", " +
|
||||
(u ? u.presence : "<null>") +
|
||||
")"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: Readonly<IProps>, prevState: Readonly<IState>, snapshot?: any): void {
|
||||
if (prevProps.searchQuery !== this.props.searchQuery) {
|
||||
this.updateListNow(false);
|
||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from "react";
|
||||
import { act, renderIntoDocument, Simulate } from "react-dom/test-utils";
|
||||
import { fireEvent, render } from "@testing-library/react";
|
||||
|
||||
import { Alignment } from "../../../../src/components/views/elements/Tooltip";
|
||||
import TooltipTarget from "../../../../src/components/views/elements/TooltipTarget";
|
||||
|
@ -28,27 +28,19 @@ describe("<TooltipTarget />", () => {
|
|||
"label": "test label",
|
||||
"alignment": Alignment.Left,
|
||||
"id": "test id",
|
||||
"data-test-id": "test",
|
||||
"data-testid": "test",
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
// clean up renderer tooltips
|
||||
const wrapper = document.querySelector(".mx_Tooltip_wrapper");
|
||||
while (wrapper?.firstChild) {
|
||||
wrapper.removeChild(wrapper.lastChild!);
|
||||
}
|
||||
});
|
||||
|
||||
const getComponent = (props = {}) => {
|
||||
const wrapper = renderIntoDocument<HTMLSpanElement>(
|
||||
const wrapper = render(
|
||||
// wrap in element so renderIntoDocument can render functional component
|
||||
<span>
|
||||
<TooltipTarget {...defaultProps} {...props}>
|
||||
<span>child</span>
|
||||
</TooltipTarget>
|
||||
</span>,
|
||||
) as HTMLSpanElement;
|
||||
return wrapper.querySelector("[data-test-id=test]");
|
||||
);
|
||||
return wrapper.getByTestId("test");
|
||||
};
|
||||
|
||||
const getVisibleTooltip = () => document.querySelector(".mx_Tooltip.mx_Tooltip_visible");
|
||||
|
@ -62,41 +54,29 @@ describe("<TooltipTarget />", () => {
|
|||
const alignmentKeys = Object.keys(Alignment).filter((o: any) => isNaN(o));
|
||||
it.each(alignmentKeys)("displays %s aligned tooltip on mouseover", async (alignment: any) => {
|
||||
const wrapper = getComponent({ alignment: Alignment[alignment] })!;
|
||||
act(() => {
|
||||
Simulate.mouseOver(wrapper);
|
||||
});
|
||||
fireEvent.mouseOver(wrapper);
|
||||
expect(getVisibleTooltip()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("hides tooltip on mouseleave", () => {
|
||||
const wrapper = getComponent()!;
|
||||
act(() => {
|
||||
Simulate.mouseOver(wrapper);
|
||||
});
|
||||
fireEvent.mouseOver(wrapper);
|
||||
expect(getVisibleTooltip()).toBeTruthy();
|
||||
act(() => {
|
||||
Simulate.mouseLeave(wrapper);
|
||||
});
|
||||
fireEvent.mouseLeave(wrapper);
|
||||
expect(getVisibleTooltip()).toBeFalsy();
|
||||
});
|
||||
|
||||
it("displays tooltip on focus", () => {
|
||||
const wrapper = getComponent()!;
|
||||
act(() => {
|
||||
Simulate.focus(wrapper);
|
||||
});
|
||||
fireEvent.focus(wrapper);
|
||||
expect(getVisibleTooltip()).toBeTruthy();
|
||||
});
|
||||
|
||||
it("hides tooltip on blur", async () => {
|
||||
const wrapper = getComponent()!;
|
||||
act(() => {
|
||||
Simulate.focus(wrapper);
|
||||
});
|
||||
fireEvent.focus(wrapper);
|
||||
expect(getVisibleTooltip()).toBeTruthy();
|
||||
act(() => {
|
||||
Simulate.blur(wrapper);
|
||||
});
|
||||
fireEvent.blur(wrapper);
|
||||
expect(getVisibleTooltip()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -95,7 +95,7 @@ exports[`<TooltipTarget /> renders container 1`] = `
|
|||
<div
|
||||
aria-describedby="test id"
|
||||
class="test tooltipTargetClassName"
|
||||
data-test-id="test"
|
||||
data-testid="test"
|
||||
tabindex="0"
|
||||
>
|
||||
<span>
|
||||
|
|
|
@ -15,10 +15,8 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { Component } from "react";
|
||||
// eslint-disable-next-line deprecate/import
|
||||
import ReactTestUtils from "react-dom/test-utils";
|
||||
import ReactDOM from "react-dom";
|
||||
import React from "react";
|
||||
import { act, render, RenderResult } from "@testing-library/react";
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||
import { User } from "matrix-js-sdk/src/models/user";
|
||||
|
@ -28,7 +26,6 @@ import { MatrixClient, RoomState } from "matrix-js-sdk/src/matrix";
|
|||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
import * as TestUtils from "../../../test-utils";
|
||||
import MemberList from "../../../../src/components/views/rooms/MemberList";
|
||||
import MemberTile from "../../../../src/components/views/rooms/MemberTile";
|
||||
import { SDKContext } from "../../../../src/contexts/SDKContext";
|
||||
import { TestSdkContext } from "../../../TestSdkContext";
|
||||
|
||||
|
@ -45,9 +42,8 @@ describe("MemberList", () => {
|
|||
return room;
|
||||
}
|
||||
|
||||
let parentDiv: HTMLDivElement;
|
||||
let client: MatrixClient;
|
||||
let root: Component;
|
||||
let root: RenderResult;
|
||||
let memberListRoom: Room;
|
||||
let memberList: MemberList;
|
||||
|
||||
|
@ -55,108 +51,40 @@ describe("MemberList", () => {
|
|||
let moderatorUsers: RoomMember[] = [];
|
||||
let defaultUsers: RoomMember[] = [];
|
||||
|
||||
beforeEach(function () {
|
||||
TestUtils.stubClient();
|
||||
client = MatrixClientPeg.get();
|
||||
client.hasLazyLoadMembersEnabled = () => false;
|
||||
|
||||
parentDiv = document.createElement("div");
|
||||
document.body.appendChild(parentDiv);
|
||||
|
||||
// Make room
|
||||
memberListRoom = createRoom();
|
||||
expect(memberListRoom.roomId).toBeTruthy();
|
||||
|
||||
// Make users
|
||||
adminUsers = [];
|
||||
moderatorUsers = [];
|
||||
defaultUsers = [];
|
||||
const usersPerLevel = 2;
|
||||
for (let i = 0; i < usersPerLevel; i++) {
|
||||
const adminUser = new RoomMember(memberListRoom.roomId, `@admin${i}:localhost`);
|
||||
adminUser.membership = "join";
|
||||
adminUser.powerLevel = 100;
|
||||
adminUser.user = new User(adminUser.userId);
|
||||
adminUser.user.currentlyActive = true;
|
||||
adminUser.user.presence = "online";
|
||||
adminUser.user.lastPresenceTs = 1000;
|
||||
adminUser.user.lastActiveAgo = 10;
|
||||
adminUsers.push(adminUser);
|
||||
|
||||
const moderatorUser = new RoomMember(memberListRoom.roomId, `@moderator${i}:localhost`);
|
||||
moderatorUser.membership = "join";
|
||||
moderatorUser.powerLevel = 50;
|
||||
moderatorUser.user = new User(moderatorUser.userId);
|
||||
moderatorUser.user.currentlyActive = true;
|
||||
moderatorUser.user.presence = "online";
|
||||
moderatorUser.user.lastPresenceTs = 1000;
|
||||
moderatorUser.user.lastActiveAgo = 10;
|
||||
moderatorUsers.push(moderatorUser);
|
||||
|
||||
const defaultUser = new RoomMember(memberListRoom.roomId, `@default${i}:localhost`);
|
||||
defaultUser.membership = "join";
|
||||
defaultUser.powerLevel = 0;
|
||||
defaultUser.user = new User(defaultUser.userId);
|
||||
defaultUser.user.currentlyActive = true;
|
||||
defaultUser.user.presence = "online";
|
||||
defaultUser.user.lastPresenceTs = 1000;
|
||||
defaultUser.user.lastActiveAgo = 10;
|
||||
defaultUsers.push(defaultUser);
|
||||
function memberString(member: RoomMember): string {
|
||||
if (!member) {
|
||||
return "(null)";
|
||||
} else {
|
||||
const u = member.user;
|
||||
return (
|
||||
"(" +
|
||||
member.name +
|
||||
", " +
|
||||
member.powerLevel +
|
||||
", " +
|
||||
(u ? u.lastActiveAgo : "<null>") +
|
||||
", " +
|
||||
(u ? u.getLastActiveTs() : "<null>") +
|
||||
", " +
|
||||
(u ? u.currentlyActive : "<null>") +
|
||||
", " +
|
||||
(u ? u.presence : "<null>") +
|
||||
")"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
client.getRoom = (roomId) => {
|
||||
if (roomId === memberListRoom.roomId) return memberListRoom;
|
||||
else return null;
|
||||
};
|
||||
memberListRoom.currentState = {
|
||||
members: {},
|
||||
getMember: jest.fn(),
|
||||
getStateEvents: ((eventType, stateKey) =>
|
||||
stateKey === undefined ? [] : null) as RoomState["getStateEvents"], // ignore 3pid invites
|
||||
} as unknown as RoomState;
|
||||
for (const member of [...adminUsers, ...moderatorUsers, ...defaultUsers]) {
|
||||
memberListRoom.currentState.members[member.userId] = member;
|
||||
}
|
||||
|
||||
const gatherWrappedRef = (r: MemberList) => {
|
||||
memberList = r;
|
||||
};
|
||||
const context = new TestSdkContext();
|
||||
context.client = client;
|
||||
root = ReactDOM.render(
|
||||
<SDKContext.Provider value={context}>
|
||||
<MemberList
|
||||
searchQuery=""
|
||||
onClose={jest.fn()}
|
||||
onSearchQueryChanged={jest.fn()}
|
||||
roomId={memberListRoom.roomId}
|
||||
ref={gatherWrappedRef}
|
||||
/>
|
||||
</SDKContext.Provider>,
|
||||
parentDiv,
|
||||
) as unknown as Component;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (parentDiv) {
|
||||
ReactDOM.unmountComponentAtNode(parentDiv);
|
||||
parentDiv.remove();
|
||||
}
|
||||
});
|
||||
|
||||
function expectOrderedByPresenceAndPowerLevel(memberTiles: MemberTile[], isPresenceEnabled: boolean) {
|
||||
function expectOrderedByPresenceAndPowerLevel(memberTiles: NodeListOf<Element>, isPresenceEnabled: boolean) {
|
||||
let prevMember: RoomMember | undefined;
|
||||
for (const tile of memberTiles) {
|
||||
const memberA = prevMember;
|
||||
const memberB = tile.props.member;
|
||||
const memberB = memberListRoom.currentState.members[tile.getAttribute("title")!.split(" ")[0]];
|
||||
prevMember = memberB; // just in case an expect fails, set this early
|
||||
if (!memberA) {
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log("COMPARING A VS B:");
|
||||
console.log(memberList.memberString(memberA));
|
||||
console.log(memberList.memberString(memberB));
|
||||
console.log("COMPARING A VS B:", memberString(memberA), memberString(memberB));
|
||||
|
||||
const userA = memberA.user!;
|
||||
const userB = memberB.user!;
|
||||
|
@ -212,7 +140,86 @@ describe("MemberList", () => {
|
|||
}
|
||||
}
|
||||
|
||||
function itDoesOrderMembersCorrectly(enablePresence: boolean) {
|
||||
describe.each([false, true])("does order members correctly (presence %s)", (enablePresence) => {
|
||||
beforeEach(function () {
|
||||
TestUtils.stubClient();
|
||||
client = MatrixClientPeg.get();
|
||||
client.hasLazyLoadMembersEnabled = () => false;
|
||||
|
||||
// Make room
|
||||
memberListRoom = createRoom();
|
||||
expect(memberListRoom.roomId).toBeTruthy();
|
||||
|
||||
// Make users
|
||||
adminUsers = [];
|
||||
moderatorUsers = [];
|
||||
defaultUsers = [];
|
||||
const usersPerLevel = 2;
|
||||
for (let i = 0; i < usersPerLevel; i++) {
|
||||
const adminUser = new RoomMember(memberListRoom.roomId, `@admin${i}:localhost`);
|
||||
adminUser.membership = "join";
|
||||
adminUser.powerLevel = 100;
|
||||
adminUser.user = new User(adminUser.userId);
|
||||
adminUser.user.currentlyActive = true;
|
||||
adminUser.user.presence = "online";
|
||||
adminUser.user.lastPresenceTs = 1000;
|
||||
adminUser.user.lastActiveAgo = 10;
|
||||
adminUsers.push(adminUser);
|
||||
|
||||
const moderatorUser = new RoomMember(memberListRoom.roomId, `@moderator${i}:localhost`);
|
||||
moderatorUser.membership = "join";
|
||||
moderatorUser.powerLevel = 50;
|
||||
moderatorUser.user = new User(moderatorUser.userId);
|
||||
moderatorUser.user.currentlyActive = true;
|
||||
moderatorUser.user.presence = "online";
|
||||
moderatorUser.user.lastPresenceTs = 1000;
|
||||
moderatorUser.user.lastActiveAgo = 10;
|
||||
moderatorUsers.push(moderatorUser);
|
||||
|
||||
const defaultUser = new RoomMember(memberListRoom.roomId, `@default${i}:localhost`);
|
||||
defaultUser.membership = "join";
|
||||
defaultUser.powerLevel = 0;
|
||||
defaultUser.user = new User(defaultUser.userId);
|
||||
defaultUser.user.currentlyActive = true;
|
||||
defaultUser.user.presence = "online";
|
||||
defaultUser.user.lastPresenceTs = 1000;
|
||||
defaultUser.user.lastActiveAgo = 10;
|
||||
defaultUsers.push(defaultUser);
|
||||
}
|
||||
|
||||
client.getRoom = (roomId) => {
|
||||
if (roomId === memberListRoom.roomId) return memberListRoom;
|
||||
else return null;
|
||||
};
|
||||
memberListRoom.currentState = {
|
||||
members: {},
|
||||
getMember: jest.fn(),
|
||||
getStateEvents: ((eventType, stateKey) =>
|
||||
stateKey === undefined ? [] : null) as RoomState["getStateEvents"], // ignore 3pid invites
|
||||
} as unknown as RoomState;
|
||||
for (const member of [...adminUsers, ...moderatorUsers, ...defaultUsers]) {
|
||||
memberListRoom.currentState.members[member.userId] = member;
|
||||
}
|
||||
|
||||
const gatherWrappedRef = (r: MemberList) => {
|
||||
memberList = r;
|
||||
};
|
||||
const context = new TestSdkContext();
|
||||
context.client = client;
|
||||
context.memberListStore.isPresenceEnabled = jest.fn().mockReturnValue(enablePresence);
|
||||
root = render(
|
||||
<SDKContext.Provider value={context}>
|
||||
<MemberList
|
||||
searchQuery=""
|
||||
onClose={jest.fn()}
|
||||
onSearchQueryChanged={jest.fn()}
|
||||
roomId={memberListRoom.roomId}
|
||||
ref={gatherWrappedRef}
|
||||
/>
|
||||
</SDKContext.Provider>,
|
||||
);
|
||||
});
|
||||
|
||||
describe("does order members correctly", () => {
|
||||
// Note: even if presence is disabled, we still expect that the presence
|
||||
// tests will pass. All expectOrderedByPresenceAndPowerLevel does is ensure
|
||||
|
@ -221,7 +228,7 @@ describe("MemberList", () => {
|
|||
// Each of the 4 tests here is done to prove that the member list can meet
|
||||
// all 4 criteria independently. Together, they should work.
|
||||
|
||||
it("by presence state", () => {
|
||||
it("by presence state", async () => {
|
||||
// Intentionally pick users that will confuse the power level sorting
|
||||
const activeUsers = [defaultUsers[0]];
|
||||
const onlineUsers = [adminUsers[0]];
|
||||
|
@ -240,25 +247,23 @@ describe("MemberList", () => {
|
|||
});
|
||||
|
||||
// Bypass all the event listeners and skip to the good part
|
||||
memberList.showPresence = enablePresence;
|
||||
memberList.updateListNow();
|
||||
await act(() => memberList.updateListNow(true));
|
||||
|
||||
const tiles = ReactTestUtils.scryRenderedComponentsWithType(root, MemberTile);
|
||||
const tiles = root.container.querySelectorAll(".mx_EntityTile");
|
||||
expectOrderedByPresenceAndPowerLevel(tiles, enablePresence);
|
||||
});
|
||||
|
||||
it("by power level", () => {
|
||||
it("by power level", async () => {
|
||||
// We already have admin, moderator, and default users so leave them alone
|
||||
|
||||
// Bypass all the event listeners and skip to the good part
|
||||
memberList.showPresence = enablePresence;
|
||||
memberList.updateListNow();
|
||||
await act(() => memberList.updateListNow(true));
|
||||
|
||||
const tiles = ReactTestUtils.scryRenderedComponentsWithType(root, MemberTile);
|
||||
const tiles = root.container.querySelectorAll(".mx_EntityTile");
|
||||
expectOrderedByPresenceAndPowerLevel(tiles, enablePresence);
|
||||
});
|
||||
|
||||
it("by last active timestamp", () => {
|
||||
it("by last active timestamp", async () => {
|
||||
// Intentionally pick users that will confuse the power level sorting
|
||||
// lastActiveAgoTs == lastPresenceTs - lastActiveAgo
|
||||
const activeUsers = [defaultUsers[0]];
|
||||
|
@ -281,14 +286,13 @@ describe("MemberList", () => {
|
|||
});
|
||||
|
||||
// Bypass all the event listeners and skip to the good part
|
||||
memberList.showPresence = enablePresence;
|
||||
memberList.updateListNow();
|
||||
await act(() => memberList.updateListNow(true));
|
||||
|
||||
const tiles = ReactTestUtils.scryRenderedComponentsWithType(root, MemberTile);
|
||||
const tiles = root.container.querySelectorAll(".mx_EntityTile");
|
||||
expectOrderedByPresenceAndPowerLevel(tiles, enablePresence);
|
||||
});
|
||||
|
||||
it("by name", () => {
|
||||
it("by name", async () => {
|
||||
// Intentionally put everyone on the same level to force a name comparison
|
||||
const allUsers = [...adminUsers, ...moderatorUsers, ...defaultUsers];
|
||||
allUsers.forEach((u) => {
|
||||
|
@ -300,20 +304,11 @@ describe("MemberList", () => {
|
|||
});
|
||||
|
||||
// Bypass all the event listeners and skip to the good part
|
||||
memberList.showPresence = enablePresence;
|
||||
memberList.updateListNow();
|
||||
await act(() => memberList.updateListNow(true));
|
||||
|
||||
const tiles = ReactTestUtils.scryRenderedComponentsWithType(root, MemberTile);
|
||||
const tiles = root.container.querySelectorAll(".mx_EntityTile");
|
||||
expectOrderedByPresenceAndPowerLevel(tiles, enablePresence);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe("when presence is enabled", () => {
|
||||
itDoesOrderMembersCorrectly(true);
|
||||
});
|
||||
|
||||
describe("when presence is not enabled", () => {
|
||||
itDoesOrderMembersCorrectly(false);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue