Skip to content

Commit

Permalink
Convert front-end tests from enzyme to react testing library
Browse files Browse the repository at this point in the history
Also add @testing-library/user-event library.
  • Loading branch information
david-yz-liu committed Jan 12, 2025
1 parent 1d5811f commit 0bc48f0
Show file tree
Hide file tree
Showing 23 changed files with 822 additions and 3,132 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
- Cache playwright's chromium installation on GitHub Actions (#7372)
- Fix broken link to the Vagrant installation guide in `README.md` (#7349)
- Fix `extra_hosts` configuration in `compose.yaml` (#7375)
- Convert front-end tests from enzyme to react testing library; add `@testing-library/user-event` (#7379)

## [v2.6.1]

Expand Down
2 changes: 1 addition & 1 deletion app/assets/javascripts/Grader/marking.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
// designate $next_criteria as the currently selected criteria
function activeCriterion($next_criteria) {
if (!$next_criteria.hasClass("active-criterion")) {
$criteria_list = $(".marks-list > li");
const $criteria_list = $(".marks-list > li");
// remove all previous active-criterion (there should only be one)
$criteria_list.removeClass("active-criterion");
// scroll the $next_criteria to the top of the criterion bar
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export class GraderDistributionModal extends React.Component {
</label>
<input
className={`input-${grader.user_name}`}
id={`input-${grader.user_name}`}
type="number"
step="0.01"
min="0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class SubmissionFileUploadModal extends React.Component {
);
} else if (this.props.requiredFiles.length >= 1) {
fileRenameInput = [
<datalist id="fileInput_datalist">
<datalist id="fileInput_datalist" key={`datalist-${filesToShow}`}>
{filesToShow.map(filename => {
return <option key={filename} value={filename}></option>;
})}
Expand All @@ -103,6 +103,7 @@ class SubmissionFileUploadModal extends React.Component {
disabled={this.state.newFiles.length !== 1}
title={I18n.t("submissions.student.one_file_allowed")}
id={"rename-box"}
key={"datalist-textbox"}
/>,
];
} else {
Expand Down Expand Up @@ -155,6 +156,7 @@ class SubmissionFileUploadModal extends React.Component {
name={"new_files"}
multiple={true}
onChange={this.handleFileUpload}
title={I18n.t("modals.file_upload.file_input_label")}
/>
</div>
<label htmlFor={"rename-box"}>
Expand Down
1 change: 1 addition & 0 deletions app/javascript/Components/Result/marks_panel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ export class FlexibleCriterionInput extends React.Component {
a.id
)
}
href="#"
className={"red-text"}
>
{"-" + a.deduction}
Expand Down
3 changes: 2 additions & 1 deletion app/javascript/Components/Result/submission_selector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ export class SubmissionSelector extends React.Component {
}

return (
<div className="submission-selector-container">
<div className="submission-selector-container" data-testid="submission-selector-container">
<div className="submission-selector">
<button
className="button previous"
Expand All @@ -226,6 +226,7 @@ export class SubmissionSelector extends React.Component {
low={meterLow}
high={meterHigh}
optimum={this.props.num_collected}
data-testid="progress-bar"
>
{this.props.num_marked}/{this.props.num_collected}
</meter>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {shallow} from "enzyme";
import {render, screen} from "@testing-library/react";
import userEvent from "@testing-library/user-event";

import {GraderDistributionModal} from "../Modals/graders_distribution_modal";

Expand All @@ -10,13 +11,9 @@ const createExampleForm = () => {
};

describe("GraderDistributionModal", () => {
let wrapper, props;
const getWrapper = props => {
return shallow(<GraderDistributionModal {...props} />);
};
const fakeEvent = {preventDefault: jest.fn()};
let mockRef;
let props;
const form1 = createExampleForm();

beforeEach(() => {
props = {
graders: [
Expand All @@ -31,24 +28,32 @@ describe("GraderDistributionModal", () => {
});

it("should display as many rows as there are graders", () => {
wrapper = getWrapper(props);

expect(wrapper.find(".modal-inline-label").length).toBe(2);
render(<GraderDistributionModal {...props} />);
for (let grader of props.graders) {
expect(screen.getByRole("spinbutton", {name: grader.user_name, hidden: true})).toBeTruthy();
}
});

it("should close on submit", () => {
wrapper = getWrapper(props);

wrapper.find("form").simulate("submit", fakeEvent);
it("should close on submit", async () => {
render(<GraderDistributionModal {...props} />);
let submit = screen.getByRole("button", {
name: I18n.t("graders.actions.randomly_assign_graders"),
hidden: true,
});
await userEvent.click(submit);

expect(props.onSubmit).toHaveBeenCalled();
expect(props.isOpen).toBeFalsy();
});

it("should call setWeighting with value of 1 on build", () => {
wrapper = getWrapper(props);
it("should call setWeighting with value of 1 on build", async () => {
render(<GraderDistributionModal {...props} />);
let submit = screen.getByRole("button", {
name: I18n.t("graders.actions.randomly_assign_graders"),
hidden: true,
});
await userEvent.click(submit);

wrapper.find("form").simulate("submit", fakeEvent);
expect(props.onSubmit).toHaveBeenCalledWith({
1: "1",
2: "1",
Expand Down
34 changes: 20 additions & 14 deletions app/javascript/Components/__tests__/instructor_table.test.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {mount} from "enzyme";
import {render, screen} from "@testing-library/react";

import {InstructorTable} from "../instructor_table";

Expand All @@ -14,18 +14,24 @@ global.fetch = jest.fn(() =>
);

describe("For the InstructorTable's display of instructors", () => {
let wrapper, instructors_sample;
let instructors_sample;

describe("when some instructors are fetched", () => {
const instructors_in_one_row = (wrapper, instructor) => {
// Find the row
const row = wrapper.find({children: instructor.user_name}).parent();
// Expect the row to contain these information
expect(row.children({children: instructor.first_name})).toBeTruthy();
expect(row.children({children: instructor.last_name})).toBeTruthy();
if (instructor.email) {
expect(row.children({children: instructor.email})).toBeTruthy();
const instructors_in_one_row = instructor => {
const rows = screen.getAllByRole("row");
for (let row of rows) {
const cells = Array.from(row.childNodes).map(c => c.textContent);
if (cells[0] === instructor.user_name) {
expect(cells[1]).toEqual(instructor.first_name);
expect(cells[2]).toEqual(instructor.last_name);
if (instructor.email) {
expect(cells[3]).toEqual(instructor.email);
}
return;
}
}
// If the loop ends without finding the instructor, raise an error
throw `Could not find row for ${instructor.user_name}`;
};

beforeAll(() => {
Expand Down Expand Up @@ -56,11 +62,11 @@ describe("For the InstructorTable's display of instructors", () => {
counts: {all: 2, active: 1, inactive: 1},
}),
});
wrapper = mount(<InstructorTable course_id={1} />);
render(<InstructorTable course_id={1} />);
});

it("each instructor is displayed as a row of the table", () => {
instructors_sample.forEach(instructor => instructors_in_one_row(wrapper, instructor));
instructors_sample.forEach(instructor => instructors_in_one_row(instructor));
});
});

Expand All @@ -76,11 +82,11 @@ describe("For the InstructorTable's display of instructors", () => {
counts: {all: 0, active: 0, inactive: 0},
}),
});
wrapper = mount(<InstructorTable course_id={1} />);
render(<InstructorTable course_id={1} />);
});

it("No rows found is shown", () => {
expect(wrapper.find({children: "No rows found"})).toBeTruthy();
expect(screen.queryByText("No rows found")).toBeTruthy();
});
});
});
55 changes: 21 additions & 34 deletions app/javascript/Components/__tests__/markdown_editor.test.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import {shallow} from "enzyme";
import {cleanup, render, screen} from "@testing-library/react";
import userEvent from "@testing-library/user-event";

import MarkdownEditor from "../markdown_editor";

afterEach(cleanup);

const basicProps = {
content: "",
text_area_id: "new_annotation_content",
Expand All @@ -12,54 +16,37 @@ const basicProps = {
};

describe("MarkdownEditor", () => {
let props, wrapper;
const getWrapper = () => {
return shallow(<MarkdownEditor {...props} />);
};
let props;
beforeEach(() => {
props = {...basicProps};
});

it("should properly handle the text input change", () => {
wrapper = getWrapper(props);

const inputBox = wrapper.find("#new_annotation_content");

expect(inputBox.exists()).toBeTruthy();
it("should properly handle the text input change", async () => {
render(<MarkdownEditor {...props} />);

const event = {
target: {
value: "Hello world",
},
};
const inputBox = screen.getByRole("textbox");
await userEvent.type(inputBox, "Hello world");

inputBox.simulate("change", event);

expect(props.handleChange).toHaveBeenCalledWith(event);
expect(props.handleChange).toHaveBeenCalled();
});

it("should show autocomplete if desired", () => {
it("should show autocomplete if desired", async () => {
props.show_autocomplete = true;
props.annotation_text_id = "id";
wrapper = getWrapper(props);

const annotationList = wrapper.find("#annotation_text_list");
render(<MarkdownEditor {...props} />);

expect(annotationList.exists()).toBeTruthy();
const autocompleteList = screen.queryByTestId("markdown-editor-autocomplete-root");
expect(autocompleteList).toBeTruthy();
});

it("should properly display and pass down props to the preview tab", () => {
it("should properly display and pass down props to the preview tab", async () => {
props.content = "arma virumque cano";
wrapper = getWrapper(props);

//At 1 because it is the 2nd tab
wrapper.find("Tab").at(1).simulate("click");

const preview = wrapper.find("#markdown-preview");
render(<MarkdownEditor {...props} />);

expect(preview.exists()).toBeTruthy();
await userEvent.click(screen.getByRole("tab", {name: "Preview"}));

expect(preview.props().content).toBe(props.content);
expect(preview.props().updateAnnotationCompletion).toBe(props.updateAnnotationCompletion);
const preview = document.querySelector("#annotation-preview");
expect(preview).toBeTruthy();
expect(preview.textContent.trim()).toEqual(props.content);
});
});
Loading

0 comments on commit 0bc48f0

Please sign in to comment.