diff --git a/Changelog.md b/Changelog.md index 99a1c6f617..177df172bd 100644 --- a/Changelog.md +++ b/Changelog.md @@ -24,6 +24,7 @@ - Fix `extra_hosts` configuration in `compose.yaml` (#7375) - Add unit tests for `marks_graders_controller` (#7382) - Convert front-end tests from enzyme to react testing library; add `@testing-library/user-event` (#7379) +- Refactor the `Result` component and its children to use React context API (#7380) ## [v2.6.1] diff --git a/app/javascript/Components/Modals/create_modify_annotation_panel_modal.jsx b/app/javascript/Components/Modals/create_modify_annotation_panel_modal.jsx index 097a0c442b..8384a07299 100644 --- a/app/javascript/Components/Modals/create_modify_annotation_panel_modal.jsx +++ b/app/javascript/Components/Modals/create_modify_annotation_panel_modal.jsx @@ -2,6 +2,7 @@ import React from "react"; import Modal from "react-modal"; import MarkdownEditor from "../markdown_editor"; +import {ResultContext} from "../Result/result_context"; class CreateModifyAnnotationPanel extends React.Component { constructor(props) { @@ -16,6 +17,8 @@ class CreateModifyAnnotationPanel extends React.Component { }; } + static contextType = ResultContext; + updateAnnotationCompletion = () => { const annotationTextList = document.getElementById("annotation_text_list"); const value = this.state.content; @@ -30,8 +33,8 @@ class CreateModifyAnnotationPanel extends React.Component { } const url = Routes.find_annotation_text_course_assignment_annotation_categories_path( - this.props.course_id, - this.props.assignment_id, + this.context.course_id, + this.context.assignment_id, {string: value} ); @@ -93,7 +96,7 @@ class CreateModifyAnnotationPanel extends React.Component { } }); - if (!this.props.is_reviewer) { + if (!this.context.is_reviewer) { textArea.keyup(e => { if (typing_timer) { clearTimeout(typing_timer); @@ -222,7 +225,7 @@ class CreateModifyAnnotationPanel extends React.Component { name="annotation_text_id" value={this.props.annotation_text_id} /> - {this.props.is_reviewer ? ( + {this.context.is_reviewer ? ( { @@ -74,7 +77,4 @@ export default class CreateTagModal extends React.Component { CreateTagModal.propType = { isOpen: PropTypes.bool.isRequired, onRequestClose: PropTypes.func.isRequired, - grouping_id: PropTypes.number, - course_id: PropTypes.number.isRequired, - assignment_id: PropTypes.number.isRequired, }; diff --git a/app/javascript/Components/Modals/filter_modal.jsx b/app/javascript/Components/Modals/filter_modal.jsx index 2f11d03f46..adb820b1b8 100644 --- a/app/javascript/Components/Modals/filter_modal.jsx +++ b/app/javascript/Components/Modals/filter_modal.jsx @@ -4,8 +4,11 @@ import {MultiSelectDropdown} from "../DropDown/MultiSelectDropDown"; import {SingleSelectDropDown} from "../DropDown/SingleSelectDropDown"; import {CriteriaFilter} from "../criteria_filter"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import {ResultContext} from "../Result/result_context"; export class FilterModal extends React.Component { + static contextType = ResultContext; + onToggleOptionTas = user_name => { const newArray = [...this.props.filterData.tas]; if (newArray.includes(user_name)) { @@ -47,7 +50,7 @@ export class FilterModal extends React.Component { }; renderTasDropdown = () => { - if (this.props.role === "Instructor") { + if (this.context.role === "Instructor") { let tas = this.props.tas.map(option => { return {key: option[0], display: option[0] + " - " + option[1]}; }); diff --git a/app/javascript/Components/Result/feedback_file_panel.jsx b/app/javascript/Components/Result/feedback_file_panel.jsx index ed560a6df1..02e34768d8 100644 --- a/app/javascript/Components/Result/feedback_file_panel.jsx +++ b/app/javascript/Components/Result/feedback_file_panel.jsx @@ -1,8 +1,11 @@ import React from "react"; import {FileViewer} from "./file_viewer"; import mime from "mime/lite"; +import {ResultContext} from "./result_context"; export class FeedbackFilePanel extends React.Component { + static contextType = ResultContext; + constructor(props) { super(props); if (props.loading) { @@ -65,7 +68,7 @@ export class FeedbackFilePanel extends React.Component { ); if (this.state.selectedFile !== null) { - url = Routes.course_feedback_file_path(this.props.course_id, this.state.selectedFile); + url = Routes.course_feedback_file_path(this.context.course_id, this.state.selectedFile); file_obj = this.props.feedbackFiles.find(file => file.id === this.state.selectedFile); download_feedback_file = ( @@ -86,8 +89,8 @@ export class FeedbackFilePanel extends React.Component { key={"feedback-file-view"} > this.setState({tabIndex})}> + this.setState({tabIndex})} + > {I18n.t("activerecord.attributes.submission.submission_files")} {I18n.t("activerecord.models.annotation.other")} - + {I18n.t("activerecord.models.test_result.other")} - + {I18n.t("activerecord.attributes.submission.feedback_files")} - + {I18n.t("activerecord.attributes.submission.submitted_remark")}
- +
{/* student results page (with instructor tests released) does not need the button */} {!this.props.student_view && ( @@ -127,8 +131,8 @@ export class LeftPane extends React.Component { method="post" data-remote="true" action={Routes.run_tests_course_result_path( - this.props.course_id, - this.props.result_id + this.context.course_id, + this.context.result_id )} >
- + - +
diff --git a/app/javascript/Components/Result/remark_panel.jsx b/app/javascript/Components/Result/remark_panel.jsx index 20b131d1f5..55d0492843 100644 --- a/app/javascript/Components/Result/remark_panel.jsx +++ b/app/javascript/Components/Result/remark_panel.jsx @@ -1,8 +1,10 @@ import React from "react"; -import {render} from "react-dom"; import {TextForm} from "./autosave_text_form"; +import {ResultContext} from "./result_context"; export class RemarkPanel extends React.Component { + static contextType = ResultContext; + componentDidMount() { if (this.props.released_to_students) { const comment = this.props.overallComment; @@ -23,9 +25,9 @@ export class RemarkPanel extends React.Component { data[name] = "true"; return $.ajax({ url: Routes.update_remark_request_course_assignment_submission_path( - this.props.course_id, - this.props.assignment_id, - this.props.submission_id + this.context.course_id, + this.context.assignment_id, + this.context.submission_id ), method: "PATCH", data: data, diff --git a/app/javascript/Components/Result/result.jsx b/app/javascript/Components/Result/result.jsx index b49c1a0bca..5278c47f55 100644 --- a/app/javascript/Components/Result/result.jsx +++ b/app/javascript/Components/Result/result.jsx @@ -11,6 +11,7 @@ import {SubmissionSelector} from "./submission_selector"; import CreateModifyAnnotationPanel from "../Modals/create_modify_annotation_panel_modal"; import CreateTagModal from "../Modals/create_tag_modal"; import {pathToNode} from "../Helpers/range_selector"; +import {ResultContext} from "./result_context"; const INITIAL_ANNOTATION_MODAL_STATE = { show: false, @@ -875,149 +876,135 @@ class Result extends React.Component { }; render() { + const contextValue = { + result_id: this.state.result_id, + submission_id: this.state.submission_id, + assignment_id: this.state.assignment_id, + grouping_id: this.state.grouping_id, + course_id: this.props.course_id, + role: this.props.role, + is_reviewer: this.state.is_reviewer, + }; + return ( - - this.setState({ - annotationModal: INITIAL_ANNOTATION_MODAL_STATE, - }) - } - is_reviewer={this.state.is_reviewer} - assignment_id={this.state.assignment_id} - course_id={this.props.course_id} - {...this.state.annotationModal} - /> - -
- + + this.setState({ + annotationModal: INITIAL_ANNOTATION_MODAL_STATE, + }) + } + {...this.state.annotationModal} /> -
-
- -
-
-
- + +
+ +
+
+ +
+
+
+ +
-
+ ); } diff --git a/app/javascript/Components/Result/result_context.js b/app/javascript/Components/Result/result_context.js new file mode 100644 index 0000000000..aeef8ae890 --- /dev/null +++ b/app/javascript/Components/Result/result_context.js @@ -0,0 +1,12 @@ +import React from "react"; + +// This needs to be in its own file to avoid circular dependency. +export const ResultContext = React.createContext({ + assignment_id: null, + submission_id: null, + result_id: null, + grouping_id: null, + course_id: null, + role: null, + is_reviewer: null, +}); diff --git a/app/javascript/Components/Result/right_pane.jsx b/app/javascript/Components/Result/right_pane.jsx index 0be3c78200..beaaecbcb3 100644 --- a/app/javascript/Components/Result/right_pane.jsx +++ b/app/javascript/Components/Result/right_pane.jsx @@ -4,10 +4,13 @@ import {Tab, Tabs, TabList, TabPanel} from "react-tabs"; import {MarksPanel} from "./marks_panel"; import {SummaryPanel} from "./summary_panel"; import {SubmissionInfoPanel} from "./submission_info_panel"; +import {ResultContext} from "./result_context"; export class RightPane extends React.Component { + static contextType = ResultContext; + canShowSubmissionInfoPanel = () => { - return this.props.role !== "Student" && !this.props.is_reviewer; + return this.context.role !== "Student" && !this.context.is_reviewer; }; render() { @@ -29,7 +32,6 @@ export class RightPane extends React.Component { destroyMark={this.props.destroyMark} revertToAutomaticDeductions={this.props.revertToAutomaticDeductions} findDeductiveAnnotation={this.props.findDeductiveAnnotation} - course_id={this.props.course_id} /> @@ -38,7 +40,6 @@ export class RightPane extends React.Component { marks={this.props.marks} released_to_students={this.props.released_to_students} remark_submitted={this.props.remark_submitted} - is_reviewer={this.props.is_reviewer} assignment_max_mark={this.props.assignment_max_mark} old_total={this.props.old_total} total={this.props.total} @@ -50,7 +51,6 @@ export class RightPane extends React.Component { deleteGraceTokenDeduction={this.props.deleteGraceTokenDeduction} createExtraMark={this.props.createExtraMark} destroyExtraMark={this.props.destroyExtraMark} - course_id={this.props.course_id} /> {this.canShowSubmissionInfoPanel() && ( @@ -66,11 +66,7 @@ export class RightPane extends React.Component { addTag={this.props.addTag} removeTag={this.props.removeTag} newNote={this.props.newNote} - role={this.props.role} - assignment_id={this.props.assignment_id} - grouping_id={this.props.grouping_id} members={this.props.members} - course_id={this.props.course_id} /> )} diff --git a/app/javascript/Components/Result/submission_file_panel.jsx b/app/javascript/Components/Result/submission_file_panel.jsx index f53a63e940..4b12b4333d 100644 --- a/app/javascript/Components/Result/submission_file_panel.jsx +++ b/app/javascript/Components/Result/submission_file_panel.jsx @@ -206,7 +206,6 @@ export class SubmissionFilePanel extends React.Component { fileData={this.props.fileData} onSelectFile={this.selectFile} selectedFile={this.state.selectedFile} - course_id={this.props.course_id} /> {this.props.canDownload && ( @@ -216,7 +215,6 @@ export class SubmissionFilePanel extends React.Component { categories={this.props.annotation_categories} newAnnotation={this.props.newAnnotation} addExistingAnnotation={this.props.addExistingAnnotation} - course_id={this.props.course_id} /> )}
diff --git a/app/javascript/Components/Result/submission_info_panel.jsx b/app/javascript/Components/Result/submission_info_panel.jsx index 79debd08c0..d5a179de15 100644 --- a/app/javascript/Components/Result/submission_info_panel.jsx +++ b/app/javascript/Components/Result/submission_info_panel.jsx @@ -1,6 +1,9 @@ import React from "react"; +import {ResultContext} from "./result_context"; export class SubmissionInfoPanel extends React.Component { + static contextType = ResultContext; + static defaultProps = { availableTags: [], currentTags: [], @@ -51,7 +54,7 @@ export class SubmissionInfoPanel extends React.Component {
    {this.renderTagList()}

{I18n.t("tags.results.available_tags")}

    {this.renderAvailableTags()}
- {this.props.role === "Instructor" && ( + {this.context.role === "Instructor" && (

@@ -174,7 +176,7 @@ export class SubmissionSelector extends React.Component { } render() { - if (this.props.role === "Student" && !this.props.is_reviewer) { + if (this.context.role === "Student" && !this.context.is_reviewer) { return ""; } @@ -239,10 +241,8 @@ export class SubmissionSelector extends React.Component { SubmissionSelector.propTypes = { assignment_max_mark: PropTypes.number, can_release: PropTypes.bool, - course_id: PropTypes.number.isRequired, fullscreen: PropTypes.bool, group_name: PropTypes.string, - is_reviewer: PropTypes.bool, marking_state: PropTypes.string, marks: PropTypes.arrayOf(PropTypes.object), nextSubmission: PropTypes.func, @@ -250,8 +250,6 @@ SubmissionSelector.propTypes = { num_marked: PropTypes.number, previousSubmission: PropTypes.func, released_to_students: PropTypes.bool, - result_id: PropTypes.number.isRequired, - role: PropTypes.string, setReleasedToStudents: PropTypes.func, toggleFullscreen: PropTypes.func, toggleMarkingState: PropTypes.func, diff --git a/app/javascript/Components/Result/summary_panel.jsx b/app/javascript/Components/Result/summary_panel.jsx index 21e0341766..46b0dfeba8 100644 --- a/app/javascript/Components/Result/summary_panel.jsx +++ b/app/javascript/Components/Result/summary_panel.jsx @@ -1,10 +1,12 @@ import React from "react"; -import {render} from "react-dom"; import ReactTable from "react-table"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; import {DataChart} from "../Helpers/data_chart"; +import {ResultContext} from "./result_context"; export class SummaryPanel extends React.Component { + static contextType = ResultContext; + static defaultProps = { criterionSummaryData: [], extra_marks: [], @@ -205,7 +207,7 @@ export class SummaryPanel extends React.Component { renderExtraMarks = () => { // If there are no extra marks and this result is released, display nothing. if ( - this.props.is_reviewer || + this.context.is_reviewer || (this.props.released_to_students && this.props.extra_marks.length === 0) ) { return ""; @@ -247,7 +249,7 @@ export class SummaryPanel extends React.Component { }; renderGraceTokenDeductions = () => { - if (this.props.is_reviewer || this.props.graceTokenDeductions.length === 0) { + if (this.context.is_reviewer || this.props.graceTokenDeductions.length === 0) { return ""; } else { let rows = this.props.graceTokenDeductions.flatMap(d => { diff --git a/app/javascript/Components/__tests__/create_tag_modal.test.jsx b/app/javascript/Components/__tests__/create_tag_modal.test.jsx index c96dde6abc..ee9a8eaf8a 100644 --- a/app/javascript/Components/__tests__/create_tag_modal.test.jsx +++ b/app/javascript/Components/__tests__/create_tag_modal.test.jsx @@ -3,6 +3,8 @@ import {render, screen, fireEvent, waitFor} from "@testing-library/react"; import CreateTagModal from "../Modals/create_tag_modal"; import Modal from "react-modal"; import fetchMock from "jest-fetch-mock"; +import {ResultContext} from "../Result/result_context"; +import {DEFAULT_RESULT_CONTEXT_VALUE, renderInResultContext} from "./result_context_renderer"; describe("CreateTagModal", () => { let props; @@ -21,14 +23,12 @@ describe("CreateTagModal", () => { props = { isOpen: true, onRequestClose: jest.fn().mockImplementation(() => (props.isOpen = false)), - grouping_id: 1, - course_id: 1, - assignment_id: 1, }; // Set the app element for React Modal Modal.setAppElement("body"); - component = render(); + + component = renderInResultContext(, {role: "Student"}); // Enable submit tagName = "Name"; @@ -46,7 +46,7 @@ describe("CreateTagModal", () => { name: tagName, description: "", }, - grouping_id: props.grouping_id, + grouping_id: DEFAULT_RESULT_CONTEXT_VALUE.grouping_id, }; const options = { method: "POST", @@ -58,7 +58,9 @@ describe("CreateTagModal", () => { }; fetchMock.mockOnce(async req => { expect(req.url).toEqual( - Routes.course_tags_path(props.course_id, {assignment_id: props.assignment_id}) + Routes.course_tags_path(DEFAULT_RESULT_CONTEXT_VALUE.course_id, { + assignment_id: DEFAULT_RESULT_CONTEXT_VALUE.assignment_id, + }) ); expect(req.method).toBe(options.method); expect(req.headers.get("Content-Type")).toEqual(options.headers["Content-Type"]); diff --git a/app/javascript/Components/__tests__/filter_modal.test.jsx b/app/javascript/Components/__tests__/filter_modal.test.jsx index 5bf75e0285..e5c5f63039 100644 --- a/app/javascript/Components/__tests__/filter_modal.test.jsx +++ b/app/javascript/Components/__tests__/filter_modal.test.jsx @@ -2,6 +2,8 @@ import * as React from "react"; import {render, screen, fireEvent, within} from "@testing-library/react"; import {FilterModal} from "../Modals/filter_modal"; import Modal from "react-modal"; +import {ResultContext} from "../Result/result_context"; +import {renderInResultContext} from "./result_context_renderer"; jest.mock("@fortawesome/react-fontawesome", () => ({ FontAwesomeIcon: () => { @@ -52,7 +54,6 @@ describe("FilterModal", () => { onRequestClose: jest.fn().mockImplementation(() => (props.isOpen = false)), updateFilterData: jest.fn().mockImplementation(() => null), clearAllFilters: jest.fn().mockImplementation(() => null), - role: role, criterionSummaryData: [ {criterion: "a"}, {criterion: "b"}, @@ -64,7 +65,8 @@ describe("FilterModal", () => { // Set the app element for React Modal Modal.setAppElement("body"); - component = render(); + + component = renderInResultContext(, {role: role}); }); it("should close on submit", () => { diff --git a/app/javascript/Components/__tests__/markdown_editor.test.jsx b/app/javascript/Components/__tests__/markdown_editor.test.jsx index d4b919237a..14cdb75255 100644 --- a/app/javascript/Components/__tests__/markdown_editor.test.jsx +++ b/app/javascript/Components/__tests__/markdown_editor.test.jsx @@ -2,6 +2,8 @@ import {render, screen} from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import MarkdownEditor from "../markdown_editor"; +import {ResultContext} from "../Result/result_context"; +import {renderInResultContext} from "./result_context_renderer"; const basicProps = { content: "", @@ -20,7 +22,7 @@ describe("MarkdownEditor", () => { }); it("should properly handle the text input change", async () => { - render(); + renderInResultContext(); const inputBox = screen.getByRole("textbox"); await userEvent.type(inputBox, "Hello world"); @@ -31,7 +33,7 @@ describe("MarkdownEditor", () => { it("should show autocomplete if desired", async () => { props.show_autocomplete = true; props.annotation_text_id = "id"; - render(); + renderInResultContext(); const autocompleteList = screen.queryByTestId("markdown-editor-autocomplete-root"); expect(autocompleteList).toBeTruthy(); @@ -39,7 +41,7 @@ describe("MarkdownEditor", () => { it("should properly display and pass down props to the preview tab", async () => { props.content = "arma virumque cano"; - render(); + renderInResultContext(); await userEvent.click(screen.getByRole("tab", {name: "Preview"})); diff --git a/app/javascript/Components/__tests__/result_context_renderer.js b/app/javascript/Components/__tests__/result_context_renderer.js new file mode 100644 index 0000000000..cad2df88cf --- /dev/null +++ b/app/javascript/Components/__tests__/result_context_renderer.js @@ -0,0 +1,24 @@ +import {render} from "@testing-library/react"; +import {ResultContext} from "../Result/result_context"; + +export const DEFAULT_RESULT_CONTEXT_VALUE = { + result_id: 1, + submission_id: 1, + assignment_id: 1, + grouping_id: 1, + course_id: 1, + role: "user", + is_reviewer: true, +}; + +export function renderInResultContext(ui, contextValue = {}, renderOptions = {}) { + const mergedContextValue = { + ...DEFAULT_RESULT_CONTEXT_VALUE, + ...contextValue, + }; + + return render( + {ui}, + renderOptions + ); +} diff --git a/app/javascript/Components/__tests__/submission_selector.test.jsx b/app/javascript/Components/__tests__/submission_selector.test.jsx index 348ecd1479..73d0811a39 100644 --- a/app/javascript/Components/__tests__/submission_selector.test.jsx +++ b/app/javascript/Components/__tests__/submission_selector.test.jsx @@ -2,6 +2,8 @@ import {render, screen} from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import {SubmissionSelector} from "../Result/submission_selector"; +import {ResultContext} from "../Result/result_context"; +import {renderInResultContext} from "./result_context_renderer"; let props; const INITIAL_FILTER_MODAL_STATE = { @@ -27,13 +29,11 @@ const basicProps = { assignment_max_mark: 100, available_tags: [], can_release: false, - course_id: 1, criterionSummaryData: [], current_tags: [], filterData: INITIAL_FILTER_MODAL_STATE, fullscreen: false, group_name: "group", - is_reviewer: false, marking_state: "incomplete", marks: [], nextSubmission: jest.fn(), @@ -41,8 +41,6 @@ const basicProps = { num_marked: 0, previousSubmission: jest.fn(), released_to_students: false, - result_id: 1, - role: "user", sections: [], setReleasedToStudents: jest.fn().mockImplementation(() => (props.released_to_students = true)), toggleFullscreen: jest.fn().mockImplementation(() => (props.fullscreen = !props.fullscreen)), @@ -60,13 +58,12 @@ describe("SubmissionSelector", () => { }); it("should not show anything if it is being viewed by a non-reviewer student", () => { - props.role = "Student"; - render(); + renderInResultContext(, {role: "Student", is_reviewer: false}); expect(screen.queryByTestId("submission-selector-container")).toBeNull(); }); it("should call nextSubmission when the next-button is pressed", async () => { - render(); + renderInResultContext(); const button = screen.getByTitle(I18n.t("results.next_submission"), {exact: false}); await userEvent.click(button); @@ -74,7 +71,7 @@ describe("SubmissionSelector", () => { }); it("should call previousSubmission when the next-button is pressed", async () => { - render(); + renderInResultContext(); const button = screen.getByTitle(I18n.t("results.previous_submission"), {exact: false}); await userEvent.click(button); @@ -82,12 +79,12 @@ describe("SubmissionSelector", () => { }); it("should display the group name", () => { - render(); + renderInResultContext(); expect(screen.getByText(props.group_name)).toBeTruthy(); }); it("should show filter modal when filter-button is pressed", async () => { - render(); + renderInResultContext(); const button = screen.getByTitle(I18n.t("results.filter_submissions")); await userEvent.click(button); @@ -98,7 +95,7 @@ describe("SubmissionSelector", () => { it("should pass correct values to the progress meter", () => { props.num_marked = 50; props.num_collected = 100; - render(); + renderInResultContext(); const meter = screen.getByTestId("progress-bar"); expect(meter).toHaveAttribute("value", String(props.num_marked)); expect(meter).toHaveAttribute("min", "0"); @@ -112,7 +109,7 @@ describe("SubmissionSelector", () => { it("should display the total correctly", () => { props.total = 50; props.assignment_max_mark = 100; - render(); + renderInResultContext(); const expected_display = `${Math.round(props.total * 100) / 100} / ${ props.assignment_max_mark }`; @@ -120,7 +117,7 @@ describe("SubmissionSelector", () => { }); it("can toggle into fullscreen", async () => { - render(); + renderInResultContext(); const button = screen.getByTitle(I18n.t("results.fullscreen_enter"), {exact: false}); await userEvent.click(button); @@ -129,7 +126,7 @@ describe("SubmissionSelector", () => { it("can toggle out of fullscreen", async () => { props.fullscreen = true; - render(); + renderInResultContext(); const button = screen.getByTitle(I18n.t("results.fullscreen_exit"), {exact: false}); await userEvent.click(button); @@ -138,14 +135,14 @@ describe("SubmissionSelector", () => { it("should not allow release if can_release is false", () => { props.can_release = false; - render(); + renderInResultContext(); expect(screen.queryByText(I18n.t("submissions.release_marks"))).toBeNull(); }); it("should set the text as unrelease if it has already been released", () => { props.released_to_students = true; props.can_release = true; - render(); + renderInResultContext(); const element = screen.queryByRole("button", {name: I18n.t("submissions.unrelease_marks")}); expect(element).toBeTruthy(); }); @@ -153,7 +150,7 @@ describe("SubmissionSelector", () => { it("should set the text as release if it has not already been released and disable it if marking state is not complete", () => { props.released_to_students = false; props.can_release = true; - render(); + renderInResultContext(); const element = screen.getByRole("button", {name: I18n.t("submissions.release_marks")}); expect(element).toBeDisabled(); }); @@ -162,7 +159,7 @@ describe("SubmissionSelector", () => { props.released_to_students = false; props.can_release = true; props.marking_state = "complete"; - render(); + renderInResultContext(); const element = screen.getByRole("button", {name: I18n.t("submissions.release_marks")}); expect(element).toBeEnabled(); }); @@ -171,7 +168,7 @@ describe("SubmissionSelector", () => { props.marking_state = "complete"; props.released_to_students = false; props.can_release = true; - render(); + renderInResultContext(); const element = screen.getByRole("button", {name: I18n.t("submissions.release_marks")}); await userEvent.click(element); @@ -181,7 +178,7 @@ describe("SubmissionSelector", () => { it("should have the marking state button function as expected when marking_state is complete", () => { props.marking_state = "complete"; props.released_to_students = false; - render(); + renderInResultContext(); const button = screen.queryByRole("button", {name: I18n.t("results.set_to_incomplete")}); expect(button).toBeTruthy(); @@ -190,7 +187,7 @@ describe("SubmissionSelector", () => { it("should have the marking state button function as expected when marking_state is incomplete", () => { props.marking_state = "incomplete"; - render(); + renderInResultContext(); const button = screen.queryByRole("button", {name: I18n.t("results.set_to_complete")}); expect(button).toBeTruthy(); @@ -199,7 +196,7 @@ describe("SubmissionSelector", () => { it("should have the marking state button enabled if props.marks have no unmarked marks and marking_state is incomplete", () => { props.marking_state = "incomplete"; props.marks = [{mark: 5}]; - render(); + renderInResultContext(); const button = screen.queryByRole("button", {name: I18n.t("results.set_to_complete")}); expect(button).toBeEnabled(); @@ -208,7 +205,7 @@ describe("SubmissionSelector", () => { it("should have the marking state button disabled if props.marks has at least 1 mark with no mark and marking_state is incomplete", () => { props.marking_state = "incomplete"; props.marks = [{mark: 5}, {mark: null}]; - render(); + renderInResultContext(); const button = screen.queryByRole("button", {name: I18n.t("results.set_to_complete")}); expect(button).toBeDisabled();