Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -129,14 +129,15 @@ describe("mongo testService", (): void => {
const unarchivedTest = await testService.unarchiveTest(test.id);
assertResponseMatchesExpected(mockTestWithId, unarchivedTest, true);
expect(test.id).not.toEqual(unarchivedTest.id);
expect(`${test.name} [COPY]`).toEqual(unarchivedTest.name);
expect(test.name).toEqual(unarchivedTest.name);

const originalTest = await MgTest.findById(test.id);
expect(originalTest?.status).toBe(AssessmentStatus.DELETED);
});

it("archiveTest", async () => {
const test = await MgTest.create(mockTestWithId);
await testService.publishTest(test.id);

const archivedTest = await testService.archiveTest(test.id);
assertResponseMatchesExpected(mockArchivedTest, archivedTest);
Expand Down Expand Up @@ -188,7 +189,7 @@ describe("mongo testService", (): void => {
await expect(async () => {
await testService.archiveTest(notFoundId);
}).rejects.toThrowError(
`Test with ID ${notFoundId} is not found or not in draft / published status`,
`Test with ID ${notFoundId} is not found or not in published status`,
);
});
});
Expand Down Expand Up @@ -219,7 +220,7 @@ describe("mongo testService", (): void => {
await expect(async () => {
await testService.archiveTest(test.id);
}).rejects.toThrowError(
`Test with ID ${test.id} is not found or not in draft / published status`,
`Test with ID ${test.id} is not found or not in published status`,
);
});
});
Expand Down
20 changes: 15 additions & 5 deletions backend/services/implementations/testService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ class TestService implements ITestService {
throw new Error(`Test ID ${id} not found`);
}

if (oldTest.status !== AssessmentStatus.DRAFT) {
throw new Error(`Test with ID ${id} cannot be edited`);
}

// Delete all images that are not in the new test
const oldImages = oldTest.questions
.flat()
Expand Down Expand Up @@ -258,7 +262,7 @@ class TestService implements ITestService {
return (await this.mapTestsToTestResponseDTOs([test]))[0];
}

async duplicateTest(id: string): Promise<TestResponseDTO> {
async duplicateTest(id: string, renameTest = true): Promise<TestResponseDTO> {
let test: Test | null;

try {
Expand All @@ -268,7 +272,9 @@ class TestService implements ITestService {
}
// eslint-disable-next-line no-underscore-dangle
test._id = new mongoose.Types.ObjectId();
test.name += " [COPY]";
if (renameTest) {
test.name += " [COPY]";
}
test.isNew = true;
test.status = AssessmentStatus.DRAFT;
test.save();
Expand All @@ -294,7 +300,11 @@ class TestService implements ITestService {
if (test.status !== AssessmentStatus.ARCHIVED) {
throw new Error(`Test with ID ${id} is not in archived status`);
}
unarchivedTest = await this.duplicateTest(id);

// We have to duplicate the test and soft-delete the archived one
// rather than just updating the status because old test sessions
// will still refer to the archived test in their session results
unarchivedTest = await this.duplicateTest(id, false);

try {
await this.deleteTest(id);
Expand Down Expand Up @@ -330,7 +340,7 @@ class TestService implements ITestService {
test = await MgTest.findOneAndUpdate(
{
_id: id,
status: { $in: [AssessmentStatus.DRAFT, AssessmentStatus.PUBLISHED] },
status: AssessmentStatus.PUBLISHED,
},
{
$set: {
Expand All @@ -345,7 +355,7 @@ class TestService implements ITestService {

if (!test) {
throw new Error(
`Test with ID ${id} is not found or not in draft / published status`,
`Test with ID ${id} is not found or not in published status`,
);
}
} catch (error: unknown) {
Expand Down
2 changes: 1 addition & 1 deletion backend/services/interfaces/testService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export interface ITestService {
* @returns a TestResponseDTO with the duplicated test
* @throws Error if Test with given id not found
*/
duplicateTest(id: string): Promise<TestResponseDTO>;
duplicateTest(id: string, renameTest?: boolean): Promise<TestResponseDTO>;

/**
* unarchive a Test given the id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import useActionFormHandler from "../../common/modal/useActionFormHandler";
import BackButton from "../../common/navigation/BackButton";
import Popover from "../../common/popover/Popover";
import PopoverButton from "../../common/popover/PopoverButton";
import ArchiveAssessmentModal from "../assessment-status/EditStatusModals/ArchiveAssessmentModal";
import DeleteAssessmentModal from "../assessment-status/EditStatusModals/DeleteAssessmentModal";
import PublishAssessmentModal from "../assessment-status/EditStatusModals/PublishAssessmentModal";

Expand All @@ -45,7 +44,6 @@ interface AssessmentEditorHeaderProps {
const AssessmentEditorHeader = ({
name,
isEditing,
onConfirmArchive,
onConfirmPublish,
onDelete,
onSave,
Expand All @@ -67,11 +65,6 @@ const AssessmentEditorHeader = ({
isOpen: isDeleteModalOpen,
onClose: onDeleteModalClose,
} = useDisclosure();
const {
onOpen: onArchiveModalOpen,
isOpen: isArchiveModalOpen,
onClose: onArchiveModalClose,
} = useDisclosure();

const onPreview = () => {
validateForm();
Expand All @@ -96,7 +89,6 @@ const AssessmentEditorHeader = ({

// We don't need validation on these actions.
const handleConfirmPublish = async () => onConfirmPublish(getValues());
const handleConfirmArchive = async () => onConfirmArchive(getValues());
const handleDelete = async () => onDelete(getValues());

return (
Expand Down Expand Up @@ -155,7 +147,6 @@ const AssessmentEditorHeader = ({
{isEditing && (
<Popover>
<VStack divider={<Divider />} spacing="0em">
<PopoverButton name="Archive" onClick={onArchiveModalOpen} />
<PopoverButton name="Delete" onClick={onDeleteModalOpen} />
</VStack>
</Popover>
Expand All @@ -168,11 +159,6 @@ const AssessmentEditorHeader = ({
onClose={onPublishModalClose}
publishAssessment={handleConfirmPublish}
/>
<ArchiveAssessmentModal
archiveAssessment={handleConfirmArchive}
isOpen={isArchiveModalOpen}
onClose={onArchiveModalClose}
/>
<DeleteAssessmentModal
deleteAssessment={handleDelete}
isOpen={isDeleteModalOpen}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,12 @@ const EditStatusPopover = ({
<UnarchiveButton assessmentId={assessmentId} />
) : (
<>
<ArchiveButton assessmentId={assessmentId} />
<Divider />
{assessmentStatus === Status.PUBLISHED && (
<>
<ArchiveButton assessmentId={assessmentId} />
<Divider />
Copy link
Collaborator

@carissa-tang carissa-tang Nov 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have both <Divider /> here and in line 24? Does it not work for nested?

</>
)}
<DuplicateButton assessmentId={assessmentId} />
</>
)}
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/components/pages/admin/AssessmentEditorPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import { Box } from "@chakra-ui/react";

import { GET_TEST } from "../../../../APIClients/queries/TestQueries";
import type { TestResponse } from "../../../../APIClients/types/TestClientTypes";
import * as Routes from "../../../../constants/Routes";
import { Status } from "../../../../types/AssessmentTypes";
import { formatQuestionsResponse } from "../../../../utils/QuestionUtils";
import RedirectTo from "../../../auth/RedirectTo";
import QueryStateHandler from "../../../common/QueryStateHandler";

import AssessmentEditor from "./AssessmentEditor";
Expand Down Expand Up @@ -37,6 +40,14 @@ const AssessmentEditorPage = () => {
[test],
);

if (!!state?.status && state.status !== Status.DRAFT) {
return (
<RedirectTo
pathname={Routes.ASSESSMENT_EDITOR_PREVIEW_PAGE({ assessmentId })}
/>
);
}

return (
<Box mx={4}>
<QueryStateHandler error={error} loading={loading}>
Expand Down