diff --git a/backend/src/modules/filelist/controllers/index.ts b/backend/src/modules/filelist/controllers/index.ts index f7f852c3..c6262006 100644 --- a/backend/src/modules/filelist/controllers/index.ts +++ b/backend/src/modules/filelist/controllers/index.ts @@ -1,2 +1 @@ export * from "./create"; -export * from "./test"; diff --git a/backend/src/modules/filelist/controllers/test.ts b/backend/src/modules/filelist/controllers/test.ts deleted file mode 100644 index 4244085a..00000000 --- a/backend/src/modules/filelist/controllers/test.ts +++ /dev/null @@ -1,81 +0,0 @@ -import type { Request, Response } from "express"; -import { errorWrapper, HTTP_STATUS_CODES } from "@bcgov/citz-imb-express-utilities"; -import { type CreateFileListBody, createFileListBodySchema, type JsonFileList } from "../schemas"; -import type { Workbook } from "exceljs"; -import { createExcelWorkbook } from "../utils"; -import { createJsonFileList } from "../utils"; - -export const test = errorWrapper(async (req: Request, res: Response) => { - const { getStandardResponse, getZodValidatedBody } = req; - const body: CreateFileListBody = getZodValidatedBody(createFileListBodySchema); // Validate request body - - // Format folder rows - const folderRows = Object.entries(body.metadata.folders).map(([folder, metadata]) => ({ - folder, // Add the key as the "folder" property - ...metadata, // Spread the properties of the metadata - })); - - // Format file rows - const fileRows = Object.values(body.metadata.files).flat(); - - // Handle output file type - switch (body.outputFileType) { - case "excel": { - const fileName = `ARS662_File_List_${new Date().toLocaleDateString().replace(/\//g, "-")}.xlsx`; - - // Create Excel workbook - const workbook: Workbook = createExcelWorkbook({ - folderRows, - fileRows, - accession: body.metadata?.admin?.accession, - application: body.metadata?.admin?.application, - }); - - // Generate the Excel file as a buffer - const buffer = await workbook.xlsx.writeBuffer(); - - // Set response headers for file download - res.set({ - "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - "Content-Disposition": `attachment; filename="${fileName}"`, - "Content-Length": buffer.byteLength, - }); - - // Send the buffer as the response - return res.status(HTTP_STATUS_CODES.CREATED).send(buffer); - } - - case "json": { - const fileName = `ARS662_File_List_${new Date().toLocaleDateString().replace(/\//g, "-")}.json`; - - // Create JSON file list - const jsonFile: JsonFileList = createJsonFileList({ - accession: body.metadata?.admin?.accession, - application: body.metadata?.admin?.application, - folders: body.metadata.folders, - files: body.metadata.files, - }); - - // Generate JSON string and buffer - const jsonBuffer = Buffer.from(JSON.stringify(jsonFile, null, 2)); - - // Set response headers for file download - res.set({ - "Content-Type": "application/json", - "Content-Disposition": `attachment; filename="${fileName}"`, - "Content-Length": jsonBuffer.byteLength, - }); - - // Send the buffer as the response - return res.status(HTTP_STATUS_CODES.CREATED).send(jsonBuffer); - } - - default: { - const result = getStandardResponse({ - message: "Invalid output file type.", - success: false, - }); - return res.status(HTTP_STATUS_CODES.BAD_REQUEST).json(result); - } - } -}); diff --git a/backend/src/modules/filelist/router.ts b/backend/src/modules/filelist/router.ts index 869a37df..a50b42e9 100644 --- a/backend/src/modules/filelist/router.ts +++ b/backend/src/modules/filelist/router.ts @@ -1,9 +1,8 @@ import { Router } from "express"; -import { create, test } from "./controllers"; +import { create } from "./controllers"; const router = Router(); router.post("/", create); -router.post("/test", test); export default router; diff --git a/backend/tests/modules/filelist/controllers/test.test.ts b/backend/tests/modules/filelist/controllers/test.test.ts deleted file mode 100644 index 6d63bdde..00000000 --- a/backend/tests/modules/filelist/controllers/test.test.ts +++ /dev/null @@ -1,184 +0,0 @@ -import type { NextFunction, Request, Response } from "express"; -import { test } from "@/modules/filelist/controllers/test"; -import { - HTTP_STATUS_CODES, - type StandardResponseInput, - type StandardResponse, -} from "@bcgov/citz-imb-express-utilities"; -import { createExcelWorkbook } from "@/modules/filelist/utils"; -import { createJsonFileList } from "@/modules/filelist/utils"; -import type { Workbook } from "exceljs"; - -jest.mock("@/modules/filelist/utils", () => ({ - createExcelWorkbook: jest.fn(), - createJsonFileList: jest.fn(), -})); - -jest.mock("@bcgov/citz-imb-express-utilities", () => { - const originalModule = jest.requireActual("@bcgov/citz-imb-express-utilities"); - return { - ...originalModule, - errorWrapper: (fn: unknown) => fn, - }; -}); - -describe("Test suite for test function", () => { - let mockReq: Partial; - let mockRes: Partial; - let mockNext: NextFunction; - let statusMock: jest.Mock; - let jsonMock: jest.Mock; - let sendMock: jest.Mock; - - beforeEach(() => { - sendMock = jest.fn(); - jsonMock = jest.fn(); - statusMock = jest.fn().mockReturnValue({ - json: jsonMock, - send: sendMock, - }); // Proper chaining support for .status() - mockReq = { - getStandardResponse: ( - dataInput: StandardResponseInput, - ): StandardResponse => { - const { success = true, data, message } = dataInput; - return { - success, - data, - message: message ?? "", - } as StandardResponse; - }, - getZodValidatedBody: jest.fn(), - }; - mockRes = { - status: statusMock, - set: jest.fn(), - json: jsonMock, - send: sendMock, - } as Partial; - mockNext = jest.fn(); - jest.clearAllMocks(); - }); - - it('Test case: Should return a created Excel file when outputFileType is "excel"', async () => { - const body = { - outputFileType: "excel", - metadata: { - admin: { - application: "TestApp", - accession: "TestAccession", - }, - folders: { - folder1: { size: 100 }, - }, - files: { - file1: [{ name: "file1.txt", size: 50 }], - }, - }, - }; - - mockReq.getZodValidatedBody = jest.fn().mockReturnValue(body); - - const mockWorkbook = { - xlsx: { writeBuffer: jest.fn().mockResolvedValue(Buffer.from("test buffer")) }, - } as unknown as Workbook; - - (createExcelWorkbook as jest.Mock).mockReturnValue(mockWorkbook); - - await test(mockReq as Request, mockRes as Response, mockNext); - - expect(createExcelWorkbook).toHaveBeenCalledWith({ - folderRows: [ - { - folder: "folder1", - size: 100, - }, - ], - fileRows: [{ name: "file1.txt", size: 50 }], - application: "TestApp", - accession: "TestAccession", - }); - - expect(mockRes.set).toHaveBeenCalledWith({ - "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - "Content-Disposition": expect.stringContaining("attachment;"), - "Content-Length": expect.any(Number), - }); - - expect(mockRes.status).toHaveBeenCalledWith(HTTP_STATUS_CODES.CREATED); - expect(mockRes.send).toHaveBeenCalledWith(Buffer.from("test buffer")); - }); - - it('Test case: Should return a created JSON file when outputFileType is "json"', async () => { - const body = { - outputFileType: "json", - metadata: { - admin: { - application: "TestApp", - accession: "TestAccession", - }, - folders: { - folder1: { size: 100 }, - }, - files: { - file1: [{ name: "file1.txt", size: 50 }], - }, - }, - }; - - mockReq.getZodValidatedBody = jest.fn().mockReturnValue(body); - - const mockJsonFile = { - admin: { - lastRevised: expect.any(String), - application: "TestApp", - accession: "TestAccession", - ministry: "", - branch: "", - }, - folderList: body.metadata.folders, - metadata: body.metadata.files, - }; - - (createJsonFileList as jest.Mock).mockReturnValue(mockJsonFile); - - await test(mockReq as Request, mockRes as Response, mockNext); - - expect(createJsonFileList).toHaveBeenCalledWith({ - accession: body.metadata.admin.accession, - application: body.metadata.admin.application, - folders: body.metadata.folders, - files: body.metadata.files, - }); - - expect(mockRes.set).toHaveBeenCalledWith({ - "Content-Type": "application/json", - "Content-Disposition": expect.stringContaining("attachment;"), - "Content-Length": expect.any(Number), - }); - - expect(mockRes.status).toHaveBeenCalledWith(HTTP_STATUS_CODES.CREATED); - expect(mockRes.send).toHaveBeenCalledWith(Buffer.from(JSON.stringify(mockJsonFile, null, 2))); - }); - - it("Test case: Should return a bad request for an invalid outputFileType", async () => { - const body = { - outputFileType: "invalid", - metadata: { - folders: {}, - files: {}, - }, - }; - - mockReq.getZodValidatedBody = jest.fn().mockReturnValue(body); - - await test(mockReq as Request, mockRes as Response, mockNext); - - expect(mockRes.status).toHaveBeenCalledWith(HTTP_STATUS_CODES.BAD_REQUEST); - expect(mockRes.json).toHaveBeenCalledWith({ - success: false, - data: undefined, - message: "Invalid output file type.", - }); - }); -}); diff --git a/backend/tests/modules/filelist/router.test.ts b/backend/tests/modules/filelist/router.test.ts index 582f4466..b00270cc 100644 --- a/backend/tests/modules/filelist/router.test.ts +++ b/backend/tests/modules/filelist/router.test.ts @@ -2,12 +2,11 @@ import type { Request, Response } from "express"; import express from "express"; import request from "supertest"; import router from "@/modules/filelist/router"; -import { create, test } from "@/modules/filelist/controllers"; +import { create } from "@/modules/filelist/controllers"; // Mock the controller functions jest.mock("@/modules/filelist/controllers", () => ({ create: jest.fn((req: Request, res: Response) => res.status(201).send("Complete")), - test: jest.fn((req: Request, res: Response) => res.status(201).send("Complete")), })); // Helper function to create an Express app with the router @@ -24,11 +23,4 @@ describe("Filelist Router", () => { await request(app).post("/").expect(201, "Complete"); expect(create).toHaveBeenCalled(); }); - - // Test case for POST /test route - it("should call the create controller on POST /test route", async () => { - const app = createApp(); - await request(app).post("/test").expect(201, "Complete"); - expect(test).toHaveBeenCalled(); - }); }); diff --git a/desktop/src/renderer/src/components/file-list/FolderDisplayGrid.tsx b/desktop/src/renderer/src/components/file-list/FolderDisplayGrid.tsx index 4f255910..1cfb7951 100644 --- a/desktop/src/renderer/src/components/file-list/FolderDisplayGrid.tsx +++ b/desktop/src/renderer/src/components/file-list/FolderDisplayGrid.tsx @@ -100,21 +100,42 @@ export const FolderDisplayGrid = ({ rows, onFolderDelete, processRowUpdate, apiR ), }, - { field: "folder", headerName: "Folder", width: 200 }, - { field: "schedule", headerName: "Schedule", width: 110, editable: true }, - { field: "classification", headerName: "Primary/Secondary", width: 150, editable: true }, - { field: "file", headerName: "File ID", width: 90, editable: true }, + { field: "folder", headerName: "Folder", width: 200, description: "Automatically populated." }, + { + field: "schedule", + headerName: "Schedule", + description: "Information Schedule number (e.g. 100001 for ARCS).", + width: 110, + editable: true, + }, + { + field: "classification", + headerName: "Classification", + description: "Classification number (e.g. 201-40 for Cabinet Submissions).", + width: 150, + editable: true, + }, + { + field: "file", + headerName: "File ID", + description: + "File identifier to link multiple folders, if used (e.g. PEP for Provincial Emergency Program).", + width: 90, + editable: true, + }, { field: "opr", headerName: "OPR", type: "boolean", - description: "Office of Primary Responsibility", + description: + "Office of Primary Responsibility (OPR) maintains the official copy of the records.", width: 60, editable: true, }, { field: "startDate", headerName: "Start Date", + description: "Date the file was opened.", width: 125, editable: true, renderEditCell: (params) => , @@ -128,6 +149,7 @@ export const FolderDisplayGrid = ({ rows, onFolderDelete, processRowUpdate, apiR { field: "endDate", headerName: "End Date", + description: "Date the file was closed.", width: 125, editable: true, renderEditCell: (params) => , @@ -141,6 +163,7 @@ export const FolderDisplayGrid = ({ rows, onFolderDelete, processRowUpdate, apiR { field: "soDate", headerName: "SO Date", + description: "Date the file became Superseded or Obsolete (SO), if applicable.", width: 125, editable: true, renderEditCell: (params) => , @@ -154,6 +177,7 @@ export const FolderDisplayGrid = ({ rows, onFolderDelete, processRowUpdate, apiR { field: "fdDate", headerName: "FD Date", + description: "Date the file was eligible for Final Disposition (FD).", width: 125, editable: true, renderEditCell: (params) => ,