diff --git a/Changelog.md b/Changelog.md index ff0ad39f09..1fa4bbbbe8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -16,6 +16,7 @@ - Add pagination to Admin Users table for performance (#7997) ### 🐛 Bug fixes +- Fixed bug where clicking a file link in the annotations tab would show a blank or oversized error for PDF files (#8017) - Fixed bug where clicking MarkUs logo in navbar on mobile would open the sidebar instead of redirecting to courses page (#7990) - Fixed bug where merge commits were incorrectly flagged as making a new assignment submission when no assignment files were changed (#7988) - Fixed shift+up/shift+down keybinding being suppressed when a criterion input had focus; active criterion now scrolls into view when navigated to via keyboard (#7989) diff --git a/app/javascript/Components/Result/left_pane.jsx b/app/javascript/Components/Result/left_pane.jsx index a86b11582d..47162ebe0b 100644 --- a/app/javascript/Components/Result/left_pane.jsx +++ b/app/javascript/Components/Result/left_pane.jsx @@ -55,11 +55,24 @@ export class LeftPane extends React.Component { } } + getFileTypeById = (fileData, id) => { + for (const file of fileData.files) { + if (file[1] === id) return file[2]; + } + for (const dir of Object.values(fileData.directories || {})) { + const type = this.getFileTypeById(dir, id); + if (type !== null) return type; + } + return null; + }; + // Display a given file. Used to changes files from the annotations panel. selectFile = (file, submission_file_id, focus_line, annotation_focus) => { + const type = this.getFileTypeById(this.props.submission_files, submission_file_id); this.submissionFilePanel.current.selectFile( file, submission_file_id, + type, focus_line, annotation_focus ); diff --git a/app/javascript/Components/__tests__/left_pane.test.jsx b/app/javascript/Components/__tests__/left_pane.test.jsx new file mode 100644 index 0000000000..a83d3cb9fd --- /dev/null +++ b/app/javascript/Components/__tests__/left_pane.test.jsx @@ -0,0 +1,140 @@ +import React from "react"; +import {render} from "@testing-library/react"; +import {LeftPane} from "../Result/left_pane"; +import {renderInResultContext} from "./result_context_renderer"; + +jest.mock("../Result/annotation_panel", () => ({AnnotationPanel: () =>
})); +jest.mock("../Result/feedback_file_panel", () => ({FeedbackFilePanel: () =>
})); +jest.mock("../Result/remark_panel", () => ({RemarkPanel: () =>
})); +jest.mock("../test_run_table", () => ({TestRunTable: () =>
})); +jest.mock("../Result/submission_file_panel", () => { + const React = require("react"); + const MockSubmissionFilePanel = React.forwardRef((props, ref) => { + React.useImperativeHandle(ref, () => ({selectFile: jest.fn()})); + return null; + }); + MockSubmissionFilePanel.displayName = "MockSubmissionFilePanel"; + return {SubmissionFilePanel: MockSubmissionFilePanel}; +}); + +const flatFileData = { + files: [ + ["report.pdf", 1, "pdf"], + ["notes.txt", 2, "text"], + ], + directories: {}, + name: "", + path: [], +}; + +const nestedFileData = { + files: [["root.pdf", 1, "pdf"]], + directories: { + subdir: { + files: [["image.png", 2, "image"]], + directories: { + nested: { + files: [["data.csv", 3, "text"]], + directories: {}, + name: "nested", + path: ["subdir", "nested"], + }, + }, + name: "subdir", + path: ["subdir"], + }, + }, + name: "", + path: [], +}; + +const basicProps = { + loading: false, + allow_remarks: false, + annotation_categories: [], + annotations: [], + assignment_remark_message: "", + update_overall_comment: jest.fn(), + can_run_tests: false, + detailed_annotations: false, + enable_test: false, + feedback_files: [], + instructor_run: false, + overall_comment: "", + past_remark_due_date: false, + released_to_students: false, + remark_due_date: null, + remark_overall_comment: "", + remark_request_text: "", + remark_request_timestamp: null, + remark_submitted: false, + revision_identifier: "1", + submission_files: flatFileData, + student_view: false, + newAnnotation: jest.fn(), + addExistingAnnotation: jest.fn(), + editAnnotation: jest.fn(), + removeAnnotation: jest.fn(), + rmd_convert_enabled: false, +}; + +describe("LeftPane", () => { + describe("getFileTypeById", () => { + let leftPaneRef; + + beforeEach(() => { + leftPaneRef = React.createRef(); + renderInResultContext(); + }); + + it("returns the file type for a file at the root level", () => { + expect(leftPaneRef.current.getFileTypeById(flatFileData, 1)).toBe("pdf"); + expect(leftPaneRef.current.getFileTypeById(flatFileData, 2)).toBe("text"); + }); + + it("returns the file type for a file in a nested subdirectory", () => { + expect(leftPaneRef.current.getFileTypeById(nestedFileData, 2)).toBe("image"); + expect(leftPaneRef.current.getFileTypeById(nestedFileData, 3)).toBe("text"); + }); + + it("returns null when no file with the given id exists", () => { + expect(leftPaneRef.current.getFileTypeById(flatFileData, 3)).toBeNull(); + }); + }); + + describe("selectFile", () => { + it("passes the file type for a file at the root level", () => { + const leftPaneRef = React.createRef(); + renderInResultContext( + + ); + + leftPaneRef.current.selectFile("report.pdf", 1, undefined, 42); + + expect(leftPaneRef.current.submissionFilePanel.current.selectFile).toHaveBeenCalledWith( + "report.pdf", + 1, + "pdf", + undefined, + 42 + ); + }); + + it("passes the correct file type for a file in a nested subdirectory", () => { + const leftPaneRef = React.createRef(); + renderInResultContext( + + ); + + leftPaneRef.current.selectFile("subdir/image.png", 2, undefined, 42); + + expect(leftPaneRef.current.submissionFilePanel.current.selectFile).toHaveBeenCalledWith( + "subdir/image.png", + 2, + "image", + undefined, + 42 + ); + }); + }); +});