From 01a051a498f2349e0ef1e239c7d02451ff411e51 Mon Sep 17 00:00:00 2001 From: Takeshi Kimata <117462761+kimatata@users.noreply.github.com> Date: Sun, 14 Apr 2024 14:09:16 +0900 Subject: [PATCH] Implemented test run editor's test case selection --- .../[projectId]/runs/[runId]/RunEditor.tsx | 79 +++++++- .../runs/[runId]/TestCaseSelector.tsx | 179 ++++++++++++++++++ 2 files changed, 250 insertions(+), 8 deletions(-) create mode 100644 frontend/app/projects/[projectId]/runs/[runId]/TestCaseSelector.tsx diff --git a/frontend/app/projects/[projectId]/runs/[runId]/RunEditor.tsx b/frontend/app/projects/[projectId]/runs/[runId]/RunEditor.tsx index 68dd9c8..70f38ae 100644 --- a/frontend/app/projects/[projectId]/runs/[runId]/RunEditor.tsx +++ b/frontend/app/projects/[projectId]/runs/[runId]/RunEditor.tsx @@ -1,6 +1,7 @@ "use client"; import React from "react"; import { useState, useEffect } from "react"; +import { useRouter } from "next/navigation"; import { Button, Input, @@ -11,9 +12,21 @@ import { Listbox, ListboxItem, Divider, + Selection, + DropdownTrigger, + Dropdown, + DropdownMenu, + DropdownItem, } from "@nextui-org/react"; -import { useRouter } from "next/navigation"; -import { Save, ArrowLeft, Folder } from "lucide-react"; +import { + Save, + ArrowLeft, + Folder, + ChevronDown, + CopyPlus, + CopyMinus, +} from "lucide-react"; +import TestCaseSelector from "./TestCaseSelector"; import { testRunStatus } from "@/config/selection"; import { RunType } from "@/types/run"; import { FolderType } from "@/types/folder"; @@ -38,8 +51,9 @@ type Props = { export default function RunEditor({ projectId, runId }: Props) { const [testRun, setTestRun] = useState(defaultTestRun); const [folders, setFolders] = useState([]); - const [testcases, setTestCases] = useState([]); + const [selectedKeys, setSelectedKeys] = useState(new Set([])); const [selectedFolder, setSelectedFolder] = useState({}); + const [testcases, setTestCases] = useState([]); const [isNameInvalid, setIsNameInvalid] = useState(false); const [isUpdating, setIsUpdating] = useState(false); const router = useRouter(); @@ -74,6 +88,18 @@ export default function RunEditor({ projectId, runId }: Props) { fetchCasesData(); }, [selectedFolder]); + const onIncludeExcludeClick = async (mode: string) => { + console.log(mode) + if (selectedKeys === "all") { + const allKeys = testcases.map((item) => item.id); + console.log(allKeys); + } else { + console.log([...selectedKeys]); + } + + setSelectedKeys(new Set([])); + }; + const baseClass = ""; const selectedClass = `${baseClass} bg-neutral-200 dark:bg-neutral-700`; @@ -160,7 +186,41 @@ export default function RunEditor({ projectId, runId }: Props) { -
Select test cases
+
+
Select test cases
+
+ {selectedKeys.size > 0 || selectedKeys === "all" ? ( + + + + + + } + onClick={() => onIncludeExcludeClick("include")} + > + Include in run + + } + onClick={() => onIncludeExcludeClick("exclude")} + > + Exclude from run + + + + ) : ( + <> + )} +
+
+
@@ -182,10 +242,13 @@ export default function RunEditor({ projectId, runId }: Props) { ))}
-
- {testcases.map((testcase, index) => ( -
{testcase.title}
- ))} +
+
diff --git a/frontend/app/projects/[projectId]/runs/[runId]/TestCaseSelector.tsx b/frontend/app/projects/[projectId]/runs/[runId]/TestCaseSelector.tsx new file mode 100644 index 0000000..f8f5708 --- /dev/null +++ b/frontend/app/projects/[projectId]/runs/[runId]/TestCaseSelector.tsx @@ -0,0 +1,179 @@ +import { useState, useMemo, useCallback } from "react"; +import { + Table, + TableHeader, + TableColumn, + TableBody, + TableRow, + TableCell, + Button, + Chip, + DropdownTrigger, + Dropdown, + DropdownMenu, + DropdownItem, + Selection, + SortDescriptor, + Link, +} from "@nextui-org/react"; +import { Plus, MoreVertical, Trash } from "lucide-react"; + +const headerColumns = [ + { name: "ID", uid: "id", sortable: true }, + { name: "Title", uid: "title", sortable: true }, + { name: "Priority", uid: "priority", sortable: true }, + { name: "Actions", uid: "actions" }, +]; + +import { priorities } from "@/config/selection"; + +type Case = { + id: number; + title: string; + state: number; + priority: number; + type: number; + automationStatus: number; + description: string; + template: number; + preConditions: string; + expectedResults: string; + folderId: number; + createdAt: string; + updatedAt: string; +}; + +type Props = { + projectId: string; + cases: Case[]; + selectedKeys: Selection; + onSelectionChange: React.Dispatch>; +}; + +export default function TestCaseSelector({ + projectId, + cases, + selectedKeys, + onSelectionChange, +}: Props) { + const [sortDescriptor, setSortDescriptor] = useState({ + column: "id", + direction: "ascending", + }); + + const sortedItems = useMemo(() => { + return [...cases].sort((a: Case, b: Case) => { + const first = a[sortDescriptor.column as keyof Case] as number; + const second = b[sortDescriptor.column as keyof Case] as number; + const cmp = first < second ? -1 : first > second ? 1 : 0; + + return sortDescriptor.direction === "descending" ? -cmp : cmp; + }); + }, [sortDescriptor, cases]); + const renderCell = useCallback((testCase: Case, columnKey: Key) => { + const cellValue = testCase[columnKey as keyof Case]; + + switch (columnKey) { + case "id": + return {cellValue}; + case "title": + return ( + + {cellValue} + + ); + case "priority": + return ( + + {priorities[cellValue].name} + + ); + case "actions": + return ( + + + + + + {}}> + status change + + + + ); + default: + return cellValue; + } + }, []); + + const classNames = useMemo( + () => ({ + wrapper: ["min-w-3xl"], + th: ["bg-transparent", "text-default-500", "border-b", "border-divider"], + td: [ + // changing the rows border radius + // first + "group-data-[first=true]:first:before:rounded-none", + "group-data-[first=true]:last:before:rounded-none", + // middle + "group-data-[middle=true]:before:rounded-none", + // last + "group-data-[last=true]:first:before:rounded-none", + "group-data-[last=true]:last:before:rounded-none", + ], + }), + [] + ); + + const handleSelectionChange = (keys: Selection) => { + onSelectionChange(keys) + } + + return ( + <> + + + {(column) => ( + + {column.name} + + )} + + + {(item) => ( + + {(columnKey) => ( + {renderCell(item, columnKey)} + )} + + )} + +
+ + ); +}