From d6cba787e26b9b19698b6dda59e41bea1fccc302 Mon Sep 17 00:00:00 2001 From: Takeshi Kimata <117462761+kimatata@users.noreply.github.com> Date: Sun, 21 Apr 2024 12:28:30 +0900 Subject: [PATCH] Implemented test run editor's bulk test case selection --- backend/index.js | 4 ++ backend/routes/runcases/bulkDelete.js | 49 +++++++++++++++ backend/routes/runcases/bulkNew.js | 47 ++++++++++++++ backend/routes/runcases/new.js | 12 ++++ .../[projectId]/runs/[runId]/RunEditor.tsx | 28 ++++++--- .../projects/[projectId]/runs/runsControl.ts | 61 ++++++++++++++++++- frontend/types/run.ts | 7 ++- 7 files changed, 197 insertions(+), 11 deletions(-) create mode 100644 backend/routes/runcases/bulkDelete.js create mode 100644 backend/routes/runcases/bulkNew.js diff --git a/backend/index.js b/backend/index.js index 98a7737..a713ea2 100644 --- a/backend/index.js +++ b/backend/index.js @@ -92,10 +92,14 @@ app.use("/runs", runDeleteRoute); // "/runcases" const runCaseIndexRoute = require("./routes/runcases/index")(sequelize); const runCaseNewRoute = require("./routes/runcases/new")(sequelize); +const runCaseBuldNewRoute = require("./routes/runcases/bulkNew")(sequelize); const runCaseDeleteRoute = require("./routes/runcases/delete")(sequelize); +const runCaseBulkDeleteRoute = require("./routes/runcases/bulkDelete")(sequelize); app.use("/runcases", runCaseIndexRoute); app.use("/runcases", runCaseNewRoute); +app.use("/runcases", runCaseBuldNewRoute); app.use("/runcases", runCaseDeleteRoute); +app.use("/runcases", runCaseBulkDeleteRoute); const PORT = process.env.PORT || 3001; app.listen(PORT, () => { diff --git a/backend/routes/runcases/bulkDelete.js b/backend/routes/runcases/bulkDelete.js new file mode 100644 index 0000000..b7bfa1b --- /dev/null +++ b/backend/routes/runcases/bulkDelete.js @@ -0,0 +1,49 @@ +const express = require("express"); +const router = express.Router(); +const defineRunCase = require("../../models/runCases"); +const { DataTypes, Op } = require("sequelize"); + +module.exports = function (sequelize) { + const RunCase = defineRunCase(sequelize, DataTypes); + + router.post("/bulkdelete", async (req, res) => { + const recordsToDelete = req.body; + + try { + const existingRunCases = await RunCase.findAll({ + where: { + [Op.or]: recordsToDelete.map((condition) => ({ + runId: condition.runId, + caseId: condition.caseId, + })), + }, + }); + + console.log("######bulkDelete") + console.log(recordsToDelete) + + if (existingRunCases.length === 0) { + return res.status(400).send("No records found to delete"); + } + + // await RunCase.destroy({ + // where: recordsToDelete, + // }); + await RunCase.destroy({ + where: { + [Op.or]: recordsToDelete.map((condition) => ({ + runId: condition.runId, + caseId: condition.caseId, + })), + }, + }); + + res.status(200).send("Records deleted successfully"); + } catch (error) { + console.error("Error deleting run cases:", error); + res.status(500).send("Internal Server Error"); + } + }); + + return router; +}; diff --git a/backend/routes/runcases/bulkNew.js b/backend/routes/runcases/bulkNew.js new file mode 100644 index 0000000..0ce81d5 --- /dev/null +++ b/backend/routes/runcases/bulkNew.js @@ -0,0 +1,47 @@ +const express = require("express"); +const router = express.Router(); +const defineRunCase = require("../../models/runCases"); +const { DataTypes, Op } = require("sequelize"); + +module.exports = function (sequelize) { + const RunCase = defineRunCase(sequelize, DataTypes); + + router.post("/bulknew", async (req, res) => { + const recordsToCreate = req.body; + + try { + const existingRunCases = await RunCase.findAll({ + where: { + [Op.or]: recordsToCreate.map((record) => ({ + runId: record.runId, + caseId: record.caseId, + })), + }, + }); + + // Filter out records that already exist + const recordsToCreateFiltered = recordsToCreate.filter((record) => { + return !existingRunCases.some( + (existingRecord) => + existingRecord.runId === record.runId && + existingRecord.caseId === record.caseId + ); + }); + console.log(recordsToCreateFiltered) + const newRunCases = await RunCase.bulkCreate( + recordsToCreateFiltered.map((record) => ({ + runId: record.runId, + caseId: record.caseId, + status: 0, + })) + ); + + res.json(newRunCases); + } catch (error) { + console.error(error); + res.status(500).send("Internal Server Error"); + } + }); + + return router; +}; diff --git a/backend/routes/runcases/new.js b/backend/routes/runcases/new.js index f8c7cd0..5af0b37 100644 --- a/backend/routes/runcases/new.js +++ b/backend/routes/runcases/new.js @@ -11,6 +11,18 @@ module.exports = function (sequelize) { const caseId = req.query.caseId; try { + // Check if the record already exists + const existingRunCase = await RunCase.findOne({ + where: { + runId: runId, + caseId: caseId + } + }); + + if (existingRunCase) { + return res.status(400).send("Record already exists"); + } + const newRunCase = await RunCase.create( { runId: runId, diff --git a/frontend/app/projects/[projectId]/runs/[runId]/RunEditor.tsx b/frontend/app/projects/[projectId]/runs/[runId]/RunEditor.tsx index 096dfbe..4292f98 100644 --- a/frontend/app/projects/[projectId]/runs/[runId]/RunEditor.tsx +++ b/frontend/app/projects/[projectId]/runs/[runId]/RunEditor.tsx @@ -28,7 +28,7 @@ import { } from "lucide-react"; import TestCaseSelector from "./TestCaseSelector"; import { testRunStatus } from "@/config/selection"; -import { RunType, RunCaseType } from "@/types/run"; +import { RunType, RunCaseType, RunCaseInfoType } from "@/types/run"; import { CaseType } from "@/types/case"; import { FolderType } from "@/types/folder"; import { @@ -36,7 +36,9 @@ import { updateRun, fetchRunCases, createRunCase, + bulkCreateRunCases, deleteRunCase, + bulkDeleteRunCases } from "../runsControl"; import { fetchFolders } from "../../folders/foldersControl"; import { fetchCases } from "../../folders/[folderId]/cases/caseControl"; @@ -148,13 +150,23 @@ export default function RunEditor({ projectId, runId }: Props) { } }; - const onIncludeExcludeClick = async (mode: string) => { - console.log(mode); + const onIncludeExcludeClick = async (isInclude: boolean) => { + let keys = []; if (selectedKeys === "all") { - const allKeys = testcases.map((item) => item.id); - console.log(allKeys); + keys = testcases.map((item) => item.id); } else { - console.log([...selectedKeys]); + keys = testcases + } + + const runCaseInfo: RunCaseInfoType[] = keys.map((caseId) => ({ + runId: runId, + caseId: caseId, + })); + if (isInclude) { + const createdRunCases = await bulkCreateRunCases(runCaseInfo); + console.log(createdRunCases) + } else { + await bulkDeleteRunCases(runCaseInfo); } setSelectedKeys(new Set([])); @@ -263,13 +275,13 @@ export default function RunEditor({ projectId, runId }: Props) { } - onClick={() => onIncludeExcludeClick("include")} + onClick={() => onIncludeExcludeClick(true)} > Include selected cases in run } - onClick={() => onIncludeExcludeClick("exclude")} + onClick={() => onIncludeExcludeClick(false)} > Exclude selected cases from run diff --git a/frontend/app/projects/[projectId]/runs/runsControl.ts b/frontend/app/projects/[projectId]/runs/runsControl.ts index b60484f..a9f26c0 100644 --- a/frontend/app/projects/[projectId]/runs/runsControl.ts +++ b/frontend/app/projects/[projectId]/runs/runsControl.ts @@ -1,6 +1,6 @@ import Config from "@/config/config"; const apiServer = Config.apiServer; -import { RunType } from "@/types/run"; +import { RunType, RunCaseInfoType } from "@/types/run"; async function fetchRun(runId: string) { const url = `${apiServer}/runs/${runId}`; @@ -168,6 +168,30 @@ async function createRunCase(runId: string, caseId: number) { } } +async function bulkCreateRunCases(runCaseInfo: RunCaseInfoType[]) { + const fetchOptions = { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(runCaseInfo) + }; + + const url = `${apiServer}/runcases/bulknew`; + + try { + const response = await fetch(url, fetchOptions); + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + const data = await response.json(); + return data; + } catch (error: any) { + console.error("Error creating new runcase:", error); + throw error; + } +} + async function deleteRunCase(runId: string, caseId: number) { const fetchOptions = { method: "DELETE", @@ -189,4 +213,37 @@ async function deleteRunCase(runId: string, caseId: number) { } } -export { fetchRun, fetchRuns, createRun, updateRun, deleteRun, fetchRunCases, createRunCase, deleteRunCase }; +async function bulkDeleteRunCases(runCaseInfo: RunCaseInfoType[]) { + const fetchOptions = { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(runCaseInfo) + }; + + const url = `${apiServer}/runcases/bulkdelete`; + + try { + const response = await fetch(url, fetchOptions); + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + } catch (error: any) { + console.error("Error deleting runcase:", error); + throw error; + } +} + +export { + fetchRun, + fetchRuns, + createRun, + updateRun, + deleteRun, + fetchRunCases, + createRunCase, + bulkCreateRunCases, + deleteRunCase, + bulkDeleteRunCases, +}; diff --git a/frontend/types/run.ts b/frontend/types/run.ts index f20d2d3..f4ea648 100644 --- a/frontend/types/run.ts +++ b/frontend/types/run.ts @@ -16,4 +16,9 @@ type RunCaseType = { status: number; }; -export { RunType, RunCaseType }; +type RunCaseInfoType = { + runId: number; + caseId: number; +} + +export { RunType, RunCaseType, RunCaseInfoType };