Replace some enzyme tests by @testing-library/react (#9822)

* Replace MessageComposerButtons-test.tsx enzyme by @testing-library/react

* Replace HtmlUtils-test.tsx enzyme by @testing-library/react
pull/28788/head^2
Florian Duros 2022-12-23 11:46:14 +01:00 committed by GitHub
parent 249bd779f2
commit 88c3864682
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 74 additions and 140 deletions

View File

@ -15,9 +15,8 @@ limitations under the License.
*/
import React from "react";
// eslint-disable-next-line deprecate/import
import { mount } from "enzyme";
import { mocked } from "jest-mock";
import { render, screen } from "@testing-library/react";
import { topicToHtml } from "../src/HtmlUtils";
import SettingsStore from "../src/settings/SettingsStore";
@ -31,38 +30,35 @@ const enableHtmlTopicFeature = () => {
};
describe("HtmlUtils", () => {
function getContent() {
return screen.getByRole("contentinfo").children[0].innerHTML;
}
it("converts plain text topic to HTML", () => {
const component = mount(<div>{topicToHtml("pizza", null, null, false)}</div>);
const wrapper = component.render();
expect(wrapper.children().first().html()).toEqual("pizza");
render(<div role="contentinfo">{topicToHtml("pizza", undefined, null, false)}</div>);
expect(getContent()).toEqual("pizza");
});
it("converts plain text topic with emoji to HTML", () => {
const component = mount(<div>{topicToHtml("pizza 🍕", null, null, false)}</div>);
const wrapper = component.render();
expect(wrapper.children().first().html()).toEqual('pizza <span class="mx_Emoji" title=":pizza:">🍕</span>');
render(<div role="contentinfo">{topicToHtml("pizza 🍕", undefined, null, false)}</div>);
expect(getContent()).toEqual('pizza <span class="mx_Emoji" title=":pizza:">🍕</span>');
});
it("converts literal HTML topic to HTML", async () => {
enableHtmlTopicFeature();
const component = mount(<div>{topicToHtml("<b>pizza</b>", null, null, false)}</div>);
const wrapper = component.render();
expect(wrapper.children().first().html()).toEqual("&lt;b&gt;pizza&lt;/b&gt;");
render(<div role="contentinfo">{topicToHtml("<b>pizza</b>", undefined, null, false)}</div>);
expect(getContent()).toEqual("&lt;b&gt;pizza&lt;/b&gt;");
});
it("converts true HTML topic to HTML", async () => {
enableHtmlTopicFeature();
const component = mount(<div>{topicToHtml("**pizza**", "<b>pizza</b>", null, false)}</div>);
const wrapper = component.render();
expect(wrapper.children().first().html()).toEqual("<b>pizza</b>");
render(<div role="contentinfo">{topicToHtml("**pizza**", "<b>pizza</b>", null, false)}</div>);
expect(getContent()).toEqual("<b>pizza</b>");
});
it("converts true HTML topic with emoji to HTML", async () => {
enableHtmlTopicFeature();
const component = mount(<div>{topicToHtml("**pizza** 🍕", "<b>pizza</b> 🍕", null, false)}</div>);
const wrapper = component.render();
expect(wrapper.children().first().html()).toEqual(
'<b>pizza</b> <span class="mx_Emoji" title=":pizza:">🍕</span>',
);
render(<div role="contentinfo">{topicToHtml("**pizza** 🍕", "<b>pizza</b> 🍕", null, false)}</div>);
expect(getContent()).toEqual('<b>pizza</b> <span class="mx_Emoji" title=":pizza:">🍕</span>');
});
});

View File

@ -15,34 +15,60 @@ limitations under the License.
*/
import React from "react";
// eslint-disable-next-line deprecate/import
import { mount, ReactWrapper } from "enzyme";
import { Room } from "matrix-js-sdk/src/models/room";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { render, screen } from "@testing-library/react";
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
import { Layout } from "../../../../src/settings/enums/Layout";
import RoomContext, { TimelineRenderingType } from "../../../../src/contexts/RoomContext";
import { createTestClient } from "../../../test-utils";
import RoomContext from "../../../../src/contexts/RoomContext";
import { createTestClient, getRoomContext, mkStubRoom } from "../../../test-utils";
import { IRoomState } from "../../../../src/components/structures/RoomView";
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
import MessageComposerButtons from "../../../../src/components/views/rooms/MessageComposerButtons";
// @ts-ignore - we're deliberately not implementing the whole interface here, but
// can't use Partial<> for types because it'll annoy TS more than it helps.
const mockProps: React.ComponentProps<typeof MessageComposerButtons> = {
addEmoji: () => false,
haveRecording: false,
isStickerPickerOpen: false,
menuPosition: undefined,
onRecordStartEndClick: () => {},
setStickerPickerOpen: () => {},
toggleButtonMenu: () => {},
};
describe("MessageComposerButtons", () => {
// @ts-ignore - we're deliberately not implementing the whole interface here, but
// can't use Partial<> for types because it'll annoy TS more than it helps.
const mockProps: React.ComponentProps<typeof MessageComposerButtons> = {
addEmoji: () => false,
haveRecording: false,
isStickerPickerOpen: false,
menuPosition: undefined,
onRecordStartEndClick: () => {},
setStickerPickerOpen: () => {},
toggleButtonMenu: () => {},
};
const mockClient = createTestClient();
jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient);
function getButtonLabels() {
const getLabels = (elements: HTMLElement[]): string[] =>
elements
.map((element) => element.getAttribute("aria-label"))
.filter((label): label is string => label !== null);
const mainLabels: Array<string | string[]> = getLabels(screen.queryAllByRole("button"));
const menuLabels = getLabels(screen.queryAllByRole("menuitem"));
if (menuLabels.length) {
mainLabels.push(getLabels(screen.queryAllByRole("menuitem")));
}
return mainLabels;
}
function wrapAndRender(component: React.ReactElement, narrow: boolean) {
const mockRoom = mkStubRoom("myfakeroom", "myfakeroom", mockClient) as any;
const defaultRoomContext: IRoomState = getRoomContext(mockRoom, { narrow });
return render(
<MatrixClientContext.Provider value={mockClient}>
<RoomContext.Provider value={defaultRoomContext}>{component}</RoomContext.Provider>
</MatrixClientContext.Provider>,
);
}
it("Renders emoji and upload buttons in wide mode", () => {
const buttons = wrapAndRender(
wrapAndRender(
<MessageComposerButtons
{...mockProps}
isMenuOpen={false}
@ -53,11 +79,11 @@ describe("MessageComposerButtons", () => {
false,
);
expect(buttonLabels(buttons)).toEqual(["Emoji", "Attachment", "More options"]);
expect(getButtonLabels()).toEqual(["Emoji", "Attachment", "More options"]);
});
it("Renders other buttons in menu in wide mode", () => {
const buttons = wrapAndRender(
wrapAndRender(
<MessageComposerButtons
{...mockProps}
isMenuOpen={true}
@ -68,7 +94,7 @@ describe("MessageComposerButtons", () => {
false,
);
expect(buttonLabels(buttons)).toEqual([
expect(getButtonLabels()).toEqual([
"Emoji",
"Attachment",
"More options",
@ -77,7 +103,7 @@ describe("MessageComposerButtons", () => {
});
it("Renders only some buttons in narrow mode", () => {
const buttons = wrapAndRender(
wrapAndRender(
<MessageComposerButtons
{...mockProps}
isMenuOpen={false}
@ -88,11 +114,11 @@ describe("MessageComposerButtons", () => {
true,
);
expect(buttonLabels(buttons)).toEqual(["Emoji", "More options"]);
expect(getButtonLabels()).toEqual(["Emoji", "More options"]);
});
it("Renders other buttons in menu (except voice messages) in narrow mode", () => {
const buttons = wrapAndRender(
wrapAndRender(
<MessageComposerButtons
{...mockProps}
isMenuOpen={true}
@ -103,12 +129,12 @@ describe("MessageComposerButtons", () => {
true,
);
expect(buttonLabels(buttons)).toEqual(["Emoji", "More options", ["Attachment", "Sticker", "Poll", "Location"]]);
expect(getButtonLabels()).toEqual(["Emoji", "More options", ["Attachment", "Sticker", "Poll", "Location"]]);
});
describe("polls button", () => {
it("should render when asked to", () => {
const buttons = wrapAndRender(
wrapAndRender(
<MessageComposerButtons
{...mockProps}
isMenuOpen={true}
@ -119,15 +145,11 @@ describe("MessageComposerButtons", () => {
true,
);
expect(buttonLabels(buttons)).toEqual([
"Emoji",
"More options",
["Attachment", "Sticker", "Poll", "Location"],
]);
expect(getButtonLabels()).toEqual(["Emoji", "More options", ["Attachment", "Sticker", "Poll", "Location"]]);
});
it("should not render when asked not to", () => {
const buttons = wrapAndRender(
wrapAndRender(
<MessageComposerButtons
{...mockProps}
isMenuOpen={true}
@ -138,7 +160,7 @@ describe("MessageComposerButtons", () => {
true,
);
expect(buttonLabels(buttons)).toEqual([
expect(getButtonLabels()).toEqual([
"Emoji",
"More options",
[
@ -153,7 +175,7 @@ describe("MessageComposerButtons", () => {
describe("with showVoiceBroadcastButton = true", () => {
it("should render the »Voice broadcast« button", () => {
const buttons = wrapAndRender(
wrapAndRender(
<MessageComposerButtons
{...mockProps}
isMenuOpen={true}
@ -165,7 +187,7 @@ describe("MessageComposerButtons", () => {
false,
);
expect(buttonLabels(buttons)).toEqual([
expect(getButtonLabels()).toEqual([
"Emoji",
"Attachment",
"More options",
@ -174,87 +196,3 @@ describe("MessageComposerButtons", () => {
});
});
});
function wrapAndRender(component: React.ReactElement, narrow: boolean): ReactWrapper {
const mockClient = createTestClient();
jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient);
const roomId = "myroomid";
const mockRoom: any = {
currentState: undefined,
roomId,
client: mockClient,
getMember: function (userId: string): RoomMember {
return new RoomMember(roomId, userId);
},
};
const roomState = createRoomState(mockRoom, narrow);
return mount(
<MatrixClientContext.Provider value={mockClient}>
<RoomContext.Provider value={roomState}>{component}</RoomContext.Provider>
</MatrixClientContext.Provider>,
);
}
function createRoomState(room: Room, narrow: boolean): IRoomState {
return {
room: room,
roomId: room.roomId,
roomLoading: true,
peekLoading: false,
shouldPeek: true,
membersLoaded: false,
numUnreadMessages: 0,
canSelfRedact: false,
canPeek: false,
showApps: false,
isPeeking: false,
showRightPanel: true,
joining: false,
atEndOfLiveTimeline: true,
showTopUnreadMessagesBar: false,
statusBarVisible: false,
canReact: false,
canSendMessages: false,
layout: Layout.Group,
lowBandwidth: false,
alwaysShowTimestamps: false,
showTwelveHourTimestamps: false,
readMarkerInViewThresholdMs: 3000,
readMarkerOutOfViewThresholdMs: 30000,
showHiddenEvents: false,
showReadReceipts: true,
showRedactions: true,
showJoinLeaves: true,
showAvatarChanges: true,
showDisplaynameChanges: true,
matrixClientIsReady: false,
timelineRenderingType: TimelineRenderingType.Room,
liveTimeline: undefined,
resizing: false,
narrow,
activeCall: null,
};
}
function buttonLabels(buttons: ReactWrapper): any[] {
// Note: Depends on the fact that the mini buttons use aria-label
// and the labels under More options use textContent
const mainButtons = buttons
.find("div.mx_MessageComposer_button[aria-label]")
.map((button: ReactWrapper) => button.prop("aria-label") as string)
.filter((x) => x);
const extraButtons = buttons
.find('.mx_MessageComposer_Menu div.mx_AccessibleButton[role="menuitem"]')
.map((button: ReactWrapper) => button.text())
.filter((x) => x);
const list: any[] = [...mainButtons];
if (extraButtons.length > 0) {
list.push(extraButtons);
}
return list;
}