include RunCases on testCase property

This commit is contained in:
Takeshi Kimata
2024-07-15 23:07:50 +09:00
parent 569887c9cd
commit 1c436b7db6
7 changed files with 258 additions and 100 deletions

View File

@@ -22,12 +22,18 @@ import { Save, ArrowLeft, Folder, ChevronDown, CopyPlus, CopyMinus, RotateCw } f
import RunProgressChart from './RunPregressDonutChart';
import TestCaseSelector from './TestCaseSelector';
import { testRunStatus } from '@/config/selection';
import { RunType, RunCaseType, RunStatusCountType, RunMessages } from '@/types/run';
import { RunType, RunStatusCountType, RunMessages } from '@/types/run';
import { CaseType } from '@/types/case';
import { FolderType } from '@/types/folder';
import { fetchRun, updateRun, fetchRunCases, updateRunCases, processRunCases } from '../runsControl';
import {
fetchRun,
updateRun,
updateRunCases,
processRunCases,
fetchProjectCases,
processTestCases,
} from '../runsControl';
import { fetchFolders } from '../../folders/foldersControl';
import { fetchCases } from '@/utils/caseControl';
import { TokenContext } from '@/utils/TokenProvider';
import { useTheme } from 'next-themes';
import { useFormGuard } from '@/utils/formGuard';
@@ -55,11 +61,11 @@ export default function RunEditor({ projectId, runId, messages, locale }: Props)
const { theme, setTheme } = useTheme();
const [testRun, setTestRun] = useState<RunType>(defaultTestRun);
const [folders, setFolders] = useState<FolderType[]>([]);
const [runCases, setRunCases] = useState<RunCaseType[]>([]);
const [runStatusCounts, setRunStatusCounts] = useState<RunStatusCountType[]>();
const [selectedKeys, setSelectedKeys] = useState<Selection>(new Set([]));
const [selectedFolder, setSelectedFolder] = useState<FolderType | null>(null);
const [testcases, setTestCases] = useState<CaseType[]>([]);
const [testCases, setTestCases] = useState<CaseType[]>([]);
const [filteredTestCases, setFilteredTestCases] = useState<CaseType[]>([]);
const [isNameInvalid, setIsNameInvalid] = useState<boolean>(false);
const [isUpdating, setIsUpdating] = useState<boolean>(false);
const [isDirty, setIsDirty] = useState(false);
@@ -80,9 +86,17 @@ export default function RunEditor({ projectId, runId, messages, locale }: Props)
try {
await fetchRunAndStatusCount();
const foldersData = await fetchFolders(context.token.access_token, projectId);
const foldersData = await fetchFolders(context.token.access_token, Number(projectId));
setFolders(foldersData);
setSelectedFolder(foldersData[0]);
const casesData = await fetchProjectCases(context.token.access_token, Number(projectId));
casesData.forEach((testCase: CaseType) => {
if (testCase.RunCases && testCase.RunCases.length > 0) {
testCase.RunCases[0].editState = 'notChanged';
}
});
setTestCases(casesData);
} catch (error: any) {
console.error('Error in effect:', error.message);
}
@@ -92,39 +106,19 @@ export default function RunEditor({ projectId, runId, messages, locale }: Props)
}, [context]);
useEffect(() => {
async function fetchCasesData() {
function onFilter() {
if (selectedFolder && selectedFolder.id) {
try {
const latestRunCases: RunCaseType[] = await fetchRunCases(context.token.access_token, Number(runId));
latestRunCases.forEach((runCase: RunCaseType) => {
runCase.editState = 'notChanged';
});
setRunCases(latestRunCases);
const testCasesData: CaseType[] = await fetchCases(context.token.access_token, selectedFolder.id);
// Check if each testCase has an association with testRun
// and add "isIncluded" property
const updatedTestCasesData = testCasesData.map((testCase) => {
const runCase = latestRunCases.find((runCase) => runCase.caseId === testCase.id);
const isIncluded = runCase ? true : false;
const runStatus = runCase ? runCase.status : 0;
return {
...testCase,
isIncluded,
runStatus,
};
});
setTestCases(updatedTestCasesData);
const filteredData = testCases.filter((testCase) => testCase.folderId === selectedFolder.id);
setFilteredTestCases(filteredData);
} catch (error: any) {
console.error('Error fetching cases data:', error.message);
}
}
}
fetchCasesData();
}, [selectedFolder]);
onFilter();
}, [selectedFolder, testCases]);
const handleChangeStatus = async (changeCaseId: number, status: number) => {
setIsDirty(true);
@@ -141,38 +135,21 @@ export default function RunEditor({ projectId, runId, messages, locale }: Props)
const handleIncludeExcludeCase = async (isInclude: boolean, clickedTestCaseId: number) => {
setIsDirty(true);
const keys = [clickedTestCaseId];
const newRunCases = processRunCases(isInclude, keys, Number(runId), runCases);
setRunCases(newRunCases);
const updatedTestCases = testcases.map((testcase) => {
if (testcase.id === clickedTestCaseId) {
return { ...testcase, isIncluded: isInclude };
}
return testcase;
});
setTestCases(updatedTestCases);
const newTestCases = processTestCases(isInclude, keys, Number(runId), testCases);
setTestCases(newTestCases);
};
const handleBulkIncludeExcludeCases = async (isInclude: boolean) => {
setIsDirty(true);
let keys: number[] = [];
if (selectedKeys === 'all') {
keys = testcases.map((item) => item.id);
keys = testCases.map((item) => item.id);
} else {
keys = Array.from(selectedKeys).map(Number);
}
const newRunCases = processRunCases(isInclude, keys, Number(runId), runCases);
setRunCases(newRunCases);
const updatedTestCases = testcases.map((testcase) => {
if (keys.includes(testcase.id)) {
return { ...testcase, isIncluded: isInclude };
}
return testcase;
});
setTestCases(updatedTestCases);
const newTestCases = processTestCases(isInclude, keys, Number(runId), testCases);
setTestCases(newTestCases);
setSelectedKeys(new Set([]));
};
@@ -204,7 +181,7 @@ export default function RunEditor({ projectId, runId, messages, locale }: Props)
onPress={async () => {
setIsUpdating(true);
await updateRun(context.token.access_token, testRun);
await updateRunCases(context.token.access_token, Number(runId), runCases);
await updateRunCases(context.token.access_token, Number(runId), testCases);
setIsUpdating(false);
setIsDirty(false);
}}
@@ -335,7 +312,7 @@ export default function RunEditor({ projectId, runId, messages, locale }: Props)
</div>
<div className="w-9/12">
<TestCaseSelector
cases={testcases}
cases={filteredTestCases}
isDisabled={!context.isProjectReporter(Number(projectId))}
selectedKeys={selectedKeys}
onSelectionChange={setSelectedKeys}

View File

@@ -27,7 +27,7 @@ import {
} from 'lucide-react';
import { priorities, testRunCaseStatus } from '@/config/selection';
import { CaseType } from '@/types/case';
import { RunsMessages } from '@/types/run';
import { RunMessages } from '@/types/run';
type Props = {
cases: CaseType[];
@@ -37,7 +37,7 @@ type Props = {
onStatusChange: (changeCaseId: number, status: number) => {};
onIncludeCase: (includeCaseId: number) => {};
onExcludeCase: (excludeCaseId: number) => {};
messages: RunsMessages;
messages: RunMessages;
};
export default function TestCaseSelector({
@@ -106,9 +106,22 @@ export default function TestCaseSelector({
}
};
const isCaseIncluded = (testCase: CaseType) => {
let isIncluded = false;
if (testCase.RunCases && testCase.RunCases.length > 0) {
if (testCase.RunCases[0].editState !== 'deleted') {
// Even if RunCase[0] exists, if 'deleted' it will be as not included.
isIncluded = true;
}
}
return isIncluded;
};
const renderCell = (testCase: CaseType, columnKey: Key) => {
const cellValue = testCase[columnKey as keyof CaseType];
const isIncluded = testCase.isIncluded;
const isIncluded = isCaseIncluded(testCase);
const runStatus = testCase.RunCases && testCase.RunCases.length > 0 ? testCase.RunCases[0].status : 0;
switch (columnKey) {
case 'priority':
@@ -130,10 +143,10 @@ export default function TestCaseSelector({
size="sm"
variant="light"
isDisabled={!isIncluded}
startContent={isIncluded && renderStatusIcon(testRunCaseStatus[cellValue].uid)}
startContent={isIncluded && renderStatusIcon(testRunCaseStatus[runStatus].uid)}
endContent={isIncluded && <ChevronDown size={16} />}
>
<span className="w-12">{isIncluded && messages[testRunCaseStatus[cellValue].uid]}</span>
<span className="w-12">{isIncluded && messages[testRunCaseStatus[runStatus].uid]}</span>
</Button>
</DropdownTrigger>
<DropdownMenu disabledKeys={disabledStatusKeys} aria-label="test case actions">
@@ -143,7 +156,7 @@ export default function TestCaseSelector({
startContent={renderStatusIcon(runCaseStatus.uid)}
onPress={() => onStatusChange(testCase.id, index)}
>
{messages[runCaseStatus.uid]}
{messages[runStatus.uid]}
</DropdownItem>
))}
</DropdownMenu>
@@ -162,7 +175,7 @@ export default function TestCaseSelector({
key="include"
startContent={<CopyPlus size={16} />}
onPress={() => {
if (testCase.isIncluded) {
if (isIncluded) {
return;
}
onIncludeCase(testCase.id);
@@ -174,7 +187,7 @@ export default function TestCaseSelector({
key="exclude"
startContent={<CopyMinus size={16} />}
onPress={() => {
if (!testCase.isIncluded) {
if (!isIncluded) {
return;
}
onExcludeCase(testCase.id);
@@ -239,8 +252,8 @@ export default function TestCaseSelector({
</TableHeader>
<TableBody emptyContent={messages.noCasesFound} items={sortedItems}>
{(item) => (
<TableRow key={item.id} className={!item.isIncluded ? notIncludedCaseClass : ''}>
{(columnKey) => <TableCell>{renderCell(item, columnKey)}</TableCell>}
<TableRow key={item.id} className={isCaseIncluded(item) ? '' : notIncludedCaseClass}>
{(columnKey) => <TableCell key={`${item.id} + ${columnKey}`}>{renderCell(item, columnKey)}</TableCell>}
</TableRow>
)}
</TableBody>