Implemented test run editor's test case selection

This commit is contained in:
Takeshi Kimata
2024-04-20 23:26:47 +09:00
parent c5091833d0
commit e8376c07ef
6 changed files with 111 additions and 31 deletions

View File

@@ -90,8 +90,10 @@ app.use("/runs", runsEditRoute);
app.use("/runs", runDeleteRoute); app.use("/runs", runDeleteRoute);
// "/runcases" // "/runcases"
const runCaseIndexRoute = require("./routes/runcases/index")(sequelize);
const runCaseNewRoute = require("./routes/runcases/new")(sequelize); const runCaseNewRoute = require("./routes/runcases/new")(sequelize);
const runCaseDeleteRoute = require("./routes/runcases/delete")(sequelize); const runCaseDeleteRoute = require("./routes/runcases/delete")(sequelize);
app.use("/runcases", runCaseIndexRoute);
app.use("/runcases", runCaseNewRoute); app.use("/runcases", runCaseNewRoute);
app.use("/runcases", runCaseDeleteRoute); app.use("/runcases", runCaseDeleteRoute);

View File

@@ -0,0 +1,30 @@
const express = require("express");
const router = express.Router();
const defineRunCase = require("../../models/runCases");
const { DataTypes } = require('sequelize');
module.exports = function(sequelize) {
const RunCase = defineRunCase(sequelize, DataTypes)
router.get("/", async (req, res) => {
const { runId } = req.query;
if (!runId) {
return res.status(400).json({ error: 'run is required' });
}
try {
const runCases = await RunCase.findAll({
where: {
runId: runId
}
});
res.json(runCases);
} catch (error) {
console.error(error);
res.status(500).send("Internal Server Error");
}
});
return router;
};

View File

@@ -1,14 +1,14 @@
const express = require("express"); const express = require("express");
const router = express.Router(); const router = express.Router();
const defineRun = require("../../models/runs"); const defineRun = require("../../models/runs");
const defineCase = require("../../models/cases"); // const defineCase = require("../../models/cases");
const { DataTypes } = require("sequelize"); const { DataTypes } = require("sequelize");
module.exports = function (sequelize) { module.exports = function (sequelize) {
const Run = defineRun(sequelize, DataTypes); const Run = defineRun(sequelize, DataTypes);
const Case = defineCase(sequelize, DataTypes); // const Case = defineCase(sequelize, DataTypes);
Run.belongsToMany(Case, { through: "runCases" }); // Run.belongsToMany(Case, { through: "runCases" });
Case.belongsToMany(Run, { through: "runCases" }); // Case.belongsToMany(Run, { through: "runCases" });
router.get("/:runId", async (req, res) => { router.get("/:runId", async (req, res) => {
const runId = req.params.runId; const runId = req.params.runId;
@@ -18,14 +18,15 @@ module.exports = function (sequelize) {
} }
try { try {
const project = await Run.findByPk(runId, { // const project = await Run.findByPk(runId, {
include: [ // include: [
{ // {
model: Case, // model: Case,
through: { attributes: ["status"] }, // through: { attributes: ["status"] },
}, // },
], // ],
}); // });
const project = await Run.findByPk(runId);
if (!project) { if (!project) {
return res.status(404).send("Run not found"); return res.status(404).send("Run not found");
} }

View File

@@ -28,12 +28,13 @@ import {
} from "lucide-react"; } from "lucide-react";
import TestCaseSelector from "./TestCaseSelector"; import TestCaseSelector from "./TestCaseSelector";
import { testRunStatus } from "@/config/selection"; import { testRunStatus } from "@/config/selection";
import { RunType } from "@/types/run"; import { RunType, RunCaseType } from "@/types/run";
import { CaseType } from "@/types/case"; import { CaseType } from "@/types/case";
import { FolderType } from "@/types/folder"; import { FolderType } from "@/types/folder";
import { import {
fetchRun, fetchRun,
updateRun, updateRun,
fetchRunCases,
createRunCase, createRunCase,
deleteRunCase, deleteRunCase,
} from "../runsControl"; } from "../runsControl";
@@ -57,6 +58,7 @@ type Props = {
export default function RunEditor({ projectId, runId }: Props) { export default function RunEditor({ projectId, runId }: Props) {
const [testRun, setTestRun] = useState<RunType>(defaultTestRun); const [testRun, setTestRun] = useState<RunType>(defaultTestRun);
const [folders, setFolders] = useState([]); const [folders, setFolders] = useState([]);
const [runCases, setRunCases] = useState<RunCaseType[]>([]);
const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set([])); const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set([]));
const [selectedFolder, setSelectedFolder] = useState<FolderType>({}); const [selectedFolder, setSelectedFolder] = useState<FolderType>({});
const [testcases, setTestCases] = useState<CaseType[]>([]); const [testcases, setTestCases] = useState<CaseType[]>([]);
@@ -71,6 +73,8 @@ export default function RunEditor({ projectId, runId }: Props) {
setTestRun(runData); setTestRun(runData);
const foldersData = await fetchFolders(projectId); const foldersData = await fetchFolders(projectId);
setFolders(foldersData); setFolders(foldersData);
const runCasesData = await fetchRunCases(runId);
setRunCases(runCasesData);
} catch (error: any) { } catch (error: any) {
console.error("Error in effect:", error.message); console.error("Error in effect:", error.message);
} }
@@ -82,25 +86,23 @@ export default function RunEditor({ projectId, runId }: Props) {
const fetchAndUpdateCases = async () => { const fetchAndUpdateCases = async () => {
try { try {
const testCasesData = await fetchCases(selectedFolder.id); const testCasesData = await fetchCases(selectedFolder.id);
// Check if each testCase has an association with testRun // Check if each testCase has an association with testRun
// and add "isIncluded" property // and add "isIncluded" property
testCasesData.forEach((caseItr: CaseType) => { const updatedTestCasesData = testCasesData.map((testCase) => {
let isIncluded: boolean = false; const runCase = runCases.find(
let runStatus: number = 0; (runCase) => runCase.caseId === testCase.id
);
testRun.Cases.forEach((runCaseItr: CaseType) => { const isIncluded = runCase ? true : false;
if (runCaseItr.id === caseItr.id) { const runStatus = runCase ? runCase.status : 0;
isIncluded = true; return {
runStatus = runCaseItr.runCases.status; ...testCase,
} isIncluded,
runStatus,
};
}); });
caseItr.isIncluded = isIncluded; setTestCases(updatedTestCasesData);
caseItr.runStatus = runStatus;
});
setTestCases(testCasesData);
} catch (error: any) { } catch (error: any) {
console.error("Error fetching cases data:", error.message); console.error("Error fetching cases data:", error.message);
} }
@@ -109,6 +111,7 @@ export default function RunEditor({ projectId, runId }: Props) {
useEffect(() => { useEffect(() => {
async function fetchCasesData() { async function fetchCasesData() {
if (selectedFolder && selectedFolder.id) { if (selectedFolder && selectedFolder.id) {
console.log("fetchCasesData")
await fetchAndUpdateCases(); await fetchAndUpdateCases();
} }
} }
@@ -120,8 +123,9 @@ export default function RunEditor({ projectId, runId }: Props) {
isInclude: boolean, isInclude: boolean,
clickedTestCaseId: number clickedTestCaseId: number
) => { ) => {
let createdRunCase: RunCaseType;
if (isInclude) { if (isInclude) {
await createRunCase(runId, clickedTestCaseId); createdRunCase = await createRunCase(runId, clickedTestCaseId);
} else { } else {
await deleteRunCase(runId, clickedTestCaseId); await deleteRunCase(runId, clickedTestCaseId);
} }
@@ -134,6 +138,18 @@ export default function RunEditor({ projectId, runId }: Props) {
return testCase; return testCase;
}); });
}); });
if (isInclude) {
setRunCases((prevRunCases) => {
return [...prevRunCases, createdRunCase];
});
} else {
setRunCases((prevRunCases) => {
return prevRunCases.filter(
(runCase) => runCase.caseId !== clickedTestCaseId
);
});
}
}; };
const onIncludeExcludeClick = async (mode: string) => { const onIncludeExcludeClick = async (mode: string) => {

View File

@@ -123,6 +123,28 @@ async function deleteRun(runId: number) {
} }
} }
async function fetchRunCases(runId: string) {
const url = `${apiServer}/runcases?runId=${runId}`;
try {
const response = await fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error: any) {
console.error("Error fetching data:", error.message);
}
}
async function createRunCase(runId: string, caseId: number) { async function createRunCase(runId: string, caseId: number) {
const fetchOptions = { const fetchOptions = {
method: "POST", method: "POST",
@@ -167,4 +189,4 @@ async function deleteRunCase(runId: string, caseId: number) {
} }
} }
export { fetchRun, fetchRuns, createRun, updateRun, deleteRun, createRunCase, deleteRunCase }; export { fetchRun, fetchRuns, createRun, updateRun, deleteRun, fetchRunCases, createRunCase, deleteRunCase };

View File

@@ -1,4 +1,4 @@
export type RunType = { type RunType = {
id: number; id: number;
name: string; name: string;
configurations: number; configurations: number;
@@ -8,3 +8,12 @@ export type RunType = {
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
}; };
type RunCaseType = {
id: number;
runId: number;
caseId: number;
status: number;
};
export { RunType, RunCaseType };