Skip to content

Commit ed76cea

Browse files
committed
Refactor dialog tests to use RTL
1 parent 5178454 commit ed76cea

File tree

1 file changed

+116
-111
lines changed

1 file changed

+116
-111
lines changed

packages/core/test/dialog/dialogTests.tsx

Lines changed: 116 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -15,208 +15,213 @@
1515
*/
1616

1717
import { waitFor } from "@testing-library/dom";
18-
import { assert } from "chai";
19-
import { mount } from "enzyme";
18+
import { render, screen } from "@testing-library/react";
19+
import userEvent from "@testing-library/user-event";
20+
import { expect } from "chai";
2021
import * as React from "react";
2122
import { spy } from "sinon";
2223

2324
import { Button, Classes, Dialog, DialogBody, DialogFooter, type DialogProps } from "../../src";
2425

2526
const COMMON_PROPS: DialogProps = {
27+
backdropProps: { role: "presentation" },
2628
icon: "inbox",
2729
isOpen: true,
2830
title: "Dialog header",
2931
usePortal: false,
3032
};
3133

3234
describe("<Dialog>", () => {
33-
it("renders its content correctly", () => {
34-
const dialog = mount(<Dialog {...COMMON_PROPS}>{renderDialogBodyAndFooter()}</Dialog>);
35-
[
36-
Classes.DIALOG,
37-
Classes.DIALOG_BODY,
38-
Classes.DIALOG_FOOTER,
39-
Classes.DIALOG_FOOTER_ACTIONS,
40-
Classes.DIALOG_HEADER,
41-
Classes.OVERLAY_BACKDROP,
42-
].forEach(className => {
43-
assert.lengthOf(dialog.find(`.${className}`), 1, `missing ${className}`);
44-
});
35+
it("should render its content correctly", () => {
36+
render(
37+
<Dialog {...COMMON_PROPS}>
38+
<DialogBodyAndFooter />
39+
</Dialog>,
40+
);
41+
const dialog = screen.getByRole("dialog");
42+
43+
expect(dialog).to.exist;
44+
expect(dialog.querySelector(`.${Classes.DIALOG_BODY}`)).to.exist;
45+
expect(dialog.querySelector(`.${Classes.DIALOG_FOOTER}`)).to.exist;
46+
expect(dialog.querySelector(`.${Classes.DIALOG_FOOTER_ACTIONS}`)).to.exist;
47+
expect(dialog.querySelector(`.${Classes.DIALOG_HEADER}`)).to.exist;
4548
});
4649

47-
it("portalClassName appears on Portal", () => {
50+
it("should add portalClassName to Portal", () => {
4851
const TEST_CLASS = "test-class";
49-
const dialog = mount(
52+
render(
5053
<Dialog {...COMMON_PROPS} usePortal={true} portalClassName={TEST_CLASS}>
51-
{renderDialogBodyAndFooter()}
54+
<DialogBodyAndFooter />
5255
</Dialog>,
5356
);
54-
assert.isDefined(document.querySelector(`.${Classes.PORTAL}.${TEST_CLASS}`));
55-
dialog.unmount();
57+
58+
expect(document.querySelector(`.${Classes.PORTAL}.${TEST_CLASS}`)).to.exist;
5659
});
5760

58-
it("renders contents to specified container correctly", () => {
61+
it("should render contents in specified container", () => {
5962
const container = document.createElement("div");
6063
document.body.appendChild(container);
61-
mount(
64+
render(
6265
<Dialog {...COMMON_PROPS} usePortal={true} portalContainer={container}>
63-
{renderDialogBodyAndFooter()}
66+
<DialogBodyAndFooter />
6467
</Dialog>,
6568
);
66-
assert.lengthOf(container.getElementsByClassName(Classes.DIALOG), 1, `missing ${Classes.DIALOG}`);
69+
70+
expect(container.querySelector(`.${Classes.DIALOG}`)).to.exist;
6771
document.body.removeChild(container);
6872
});
6973

70-
it("attempts to close when overlay backdrop element is moused down", () => {
74+
it("should close when overlay backdrop is clicked", async () => {
7175
const onClose = spy();
72-
const dialog = mount(
76+
render(
7377
<Dialog {...COMMON_PROPS} onClose={onClose}>
74-
{renderDialogBodyAndFooter()}
78+
<DialogBodyAndFooter />
7579
</Dialog>,
7680
);
77-
dialog.find(`.${Classes.OVERLAY_BACKDROP}`).simulate("mousedown");
78-
assert.isTrue(onClose.calledOnce);
81+
82+
const backdrop = screen.getByRole("presentation");
83+
await userEvent.click(backdrop);
84+
expect(onClose.calledOnce).to.be.true;
7985
});
8086

81-
it("doesn't close when canOutsideClickClose=false and overlay backdrop element is moused down", () => {
87+
it("should not close when canOutsideClickClose=false and backdrop is clicked", async () => {
8288
const onClose = spy();
83-
const dialog = mount(
89+
render(
8490
<Dialog {...COMMON_PROPS} canOutsideClickClose={false} onClose={onClose}>
85-
{renderDialogBodyAndFooter()}
91+
<DialogBodyAndFooter />
8692
</Dialog>,
8793
);
88-
dialog.find(`.${Classes.OVERLAY_BACKDROP}`).simulate("mousedown");
89-
assert.isTrue(onClose.notCalled);
94+
95+
const backdrop = screen.getByRole("presentation");
96+
await userEvent.click(backdrop);
97+
expect(onClose.called).to.be.false;
9098
});
9199

92-
it("doesn't close when canEscapeKeyClose=false and escape key is pressed", () => {
100+
it("should not close when canEscapeKeyClose=false and escape is pressed", async () => {
93101
const onClose = spy();
94-
const dialog = mount(
102+
render(
95103
<Dialog {...COMMON_PROPS} canEscapeKeyClose={false} onClose={onClose}>
96-
{renderDialogBodyAndFooter()}
104+
<DialogBodyAndFooter />
97105
</Dialog>,
98106
);
99-
dialog.simulate("keydown", { key: "Escape" });
100-
assert.isTrue(onClose.notCalled);
107+
await userEvent.keyboard("{Escape}");
108+
expect(onClose.called).to.be.false;
101109
});
102110

103-
it("supports overlay lifecycle props", () => {
104-
const onOpening = spy();
105-
mount(
106-
<Dialog {...COMMON_PROPS} onOpening={onOpening}>
107-
body
108-
</Dialog>,
109-
);
110-
assert.isTrue(onOpening.calledOnce);
111+
it("should add className in only one location", () => {
112+
const { container } = render(<Dialog {...COMMON_PROPS} className="foo" />);
113+
expect(container.getElementsByClassName("foo")).to.have.lengthOf(1);
111114
});
112115

113116
describe("header", () => {
114-
it(`renders .${Classes.DIALOG_HEADER} if title prop is given`, () => {
115-
const dialog = mount(
117+
it("should render header when title prop is provided", () => {
118+
render(
116119
<Dialog {...COMMON_PROPS} title="Hello!">
117-
dialog body
120+
<DialogBodyAndFooter />
118121
</Dialog>,
119122
);
120-
assert.match(dialog.find(`.${Classes.DIALOG_HEADER}`).text(), /^Hello!/);
123+
const header = screen.getByText("Hello!");
124+
expect(header).to.exist;
125+
expect(header.closest(`.${Classes.DIALOG_HEADER}`)).to.not.be.null;
121126
});
122127

123-
it(`renders close button if isCloseButtonShown={true}`, () => {
124-
const dialog = mount(
125-
<Dialog {...COMMON_PROPS} isCloseButtonShown={true}>
126-
dialog body
128+
it("should render and remove close button based on isCloseButtonShown", async () => {
129+
const onClose = spy();
130+
const { rerender } = render(
131+
<Dialog {...COMMON_PROPS} isCloseButtonShown={true} onClose={onClose}>
132+
<DialogBodyAndFooter />
127133
</Dialog>,
128134
);
129-
assert.lengthOf(dialog.find(`.${Classes.DIALOG_HEADER}`).find(Button), 1);
130135

131-
dialog.setProps({ isCloseButtonShown: false });
132-
assert.lengthOf(dialog.find(`.${Classes.DIALOG_HEADER}`).find(Button), 0);
133-
});
136+
const closeButton = screen.getByLabelText("Close");
137+
expect(closeButton).to.exist;
138+
await userEvent.click(closeButton);
139+
expect(onClose.calledOnce).to.be.true;
134140

135-
it("clicking close button triggers onClose", () => {
136-
const onClose = spy();
137-
const dialog = mount(
138-
<Dialog {...COMMON_PROPS} isCloseButtonShown={true} onClose={onClose}>
139-
dialog body
141+
// Test that button is removed when prop is false
142+
rerender(
143+
<Dialog {...COMMON_PROPS} isCloseButtonShown={false} onClose={onClose}>
144+
<DialogBodyAndFooter />
140145
</Dialog>,
141146
);
142-
dialog.find(`.${Classes.DIALOG_HEADER}`).find(Button).simulate("click");
143-
assert.isTrue(onClose.calledOnce, "onClose not called");
147+
expect(() => screen.getByLabelText("Close")).to.throw;
144148
});
145149
});
146150

147-
it("only adds its className in one location", () => {
148-
const dialog = mount(<Dialog {...COMMON_PROPS} className="foo" />);
149-
assert.lengthOf(dialog.find(".foo").hostNodes(), 1);
150-
});
151-
152151
describe("accessibility features", () => {
153-
const mountDialog = (props: Partial<DialogProps>) => {
154-
return mount(
155-
<Dialog {...COMMON_PROPS} {...props}>
156-
{renderDialogBodyAndFooter()}
152+
it("should render with role='dialog'", () => {
153+
render(
154+
<Dialog {...COMMON_PROPS}>
155+
<DialogBodyAndFooter />
157156
</Dialog>,
158157
);
159-
};
160-
161-
it("renders with role={dialog}", () => {
162-
const dialog = mountDialog({ className: "check-role" });
163-
assert.equal(dialog.find(`.check-role`).hostNodes().prop("role"), "dialog", "missing dialog role!!");
158+
expect(screen.getByRole("dialog")).to.exist;
164159
});
165160

166-
it("renders with provided aria-labelledby and aria-described by from props", () => {
167-
const dialog = mountDialog({
168-
"aria-describedby": "dialog-description",
169-
"aria-labelledby": "dialog-title",
170-
className: "renders-with-props",
171-
});
172-
const dialogElement = dialog.find(`.renders-with-props`).hostNodes();
173-
assert.equal(dialogElement.prop("aria-labelledby"), "dialog-title");
174-
assert.equal(dialogElement.prop("aria-describedby"), "dialog-description");
161+
it("should render with provided aria attributes", () => {
162+
render(
163+
<Dialog {...COMMON_PROPS} aria-describedby="dialog-description" aria-labelledby="dialog-title">
164+
<DialogBodyAndFooter />
165+
</Dialog>,
166+
);
167+
const dialog = screen.getByRole("dialog");
168+
expect(dialog.getAttribute("aria-labelledby")).to.equal("dialog-title");
169+
expect(dialog.getAttribute("aria-describedby")).to.equal("dialog-description");
175170
});
176171

177-
it("uses title as default aria-labelledby", () => {
178-
const dialog = mountDialog({ className: "default-title", title: "Title by props" });
179-
// test existence here because id is generated
180-
assert.exists(dialog.find(".default-title").hostNodes().prop("aria-labelledby"));
172+
it("should use title as default aria-labelledby", () => {
173+
render(
174+
<Dialog {...COMMON_PROPS} title="Title by props">
175+
<DialogBodyAndFooter />
176+
</Dialog>,
177+
);
178+
const dialog = screen.getByRole("dialog");
179+
expect(dialog.getAttribute("aria-labelledby")).to.exist;
181180
});
182181

183-
it("does not apply default aria-labelledby if no title", () => {
184-
const dialog = mountDialog({ className: "no-default-if-no-title", title: null });
185-
// test existence here because id is generated
186-
assert.notExists(dialog.find(".no-default-if-no-title").hostNodes().prop("aria-labelledby"));
182+
it("should not apply default aria-labelledby without title", () => {
183+
render(
184+
<Dialog {...COMMON_PROPS} title={null}>
185+
<DialogBodyAndFooter />
186+
</Dialog>,
187+
);
188+
const dialog = screen.getByRole("dialog");
189+
expect(dialog.getAttribute("aria-labelledby")).to.not.exist;
187190
});
188191

189-
it("supports ref objects attached to container", async () => {
192+
it("should support ref objects attached to container", async () => {
190193
const containerRef = React.createRef<HTMLDivElement>();
191-
mountDialog({ containerRef });
194+
render(
195+
<Dialog {...COMMON_PROPS} containerRef={containerRef}>
196+
<DialogBodyAndFooter />
197+
</Dialog>,
198+
);
192199

193-
// wait for the whole lifecycle to run
194200
await waitFor(() => {
195-
assert.isTrue(containerRef.current?.classList.contains(Classes.DIALOG_CONTAINER));
201+
expect(containerRef.current?.classList.contains(Classes.DIALOG_CONTAINER)).to.be.true;
196202
});
197203
});
198204
});
205+
});
199206

200-
// N.B. everything else about Dialog is tested by Overlay2
201-
202-
function renderDialogBodyAndFooter(): React.JSX.Element[] {
203-
return [
204-
<DialogBody key="body">
207+
function DialogBodyAndFooter() {
208+
return (
209+
<>
210+
<DialogBody>
205211
<p id="dialog-description">
206212
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore
207213
et dolore magna alqua. Ut enim ad minimum veniam, quis nostrud exercitation ullamco laboris nisi ut
208214
aliquip ex ea commodo consequat.
209215
</p>
210-
</DialogBody>,
216+
</DialogBody>
211217
<DialogFooter
212-
key="footer"
213218
actions={
214219
<>
215220
<Button text="Secondary" />
216221
<Button className={Classes.INTENT_PRIMARY} type="submit" text="Primary" />
217222
</>
218223
}
219-
/>,
220-
];
221-
}
222-
});
224+
/>
225+
</>
226+
);
227+
}

0 commit comments

Comments
 (0)