feat: test case detail dialog

This commit is contained in:
Takeshi Kimata
2024-07-21 10:44:41 +09:00
parent dda2f6feb9
commit cbdd0cb1d4
9 changed files with 225 additions and 10 deletions

View File

@@ -201,12 +201,12 @@
"template": "Template", "template": "Template",
"test_detail": "Test detail", "test_detail": "Test detail",
"preconditions": "Preconditions", "preconditions": "Preconditions",
"expected_result": "Expected result",
"step": "Step", "step": "Step",
"text": "Text", "text": "Text",
"steps": "Steps", "steps": "Steps",
"new_step": "New Step", "new_step": "New Step",
"details_of_the_step": "Details of the step", "details_of_the_step": "Details of the step",
"expected_result": "Expected result",
"delete_this_step": "Delete this step", "delete_this_step": "Delete this step",
"insert_step": "Insert step", "insert_step": "Insert step",
"attachments": "Attachments", "attachments": "Attachments",
@@ -272,7 +272,25 @@
"include_in_run": "Include in run", "include_in_run": "Include in run",
"exclude_from_run": "Exclude from run", "exclude_from_run": "Exclude from run",
"no_cases_found": "No cases found", "no_cases_found": "No cases found",
"are_you_sure_leave": "Are you sure you want to leave the page?" "are_you_sure_leave": "Are you sure you want to leave the page?",
"type": "Type",
"other": "Other",
"security": "Security",
"performance": "Performance",
"accessibility": "Accessibility",
"functional": "Functional",
"acceptance": "Acceptance",
"usability": "Usability",
"smoke_sanity": "Smoke&Sanity",
"compatibility": "Compatibility",
"destructive": "Destructive",
"regression": "Regression",
"automated": "Automated",
"manual": "Manual",
"preconditions": "Preconditions",
"expected_result": "Expected result",
"details_of_the_step": "Details of the step",
"close": "Close"
}, },
"Members": { "Members": {
"member_management": "Member Management", "member_management": "Member Management",

View File

@@ -201,12 +201,12 @@
"template": "テンプレート", "template": "テンプレート",
"test_detail": "テスト詳細", "test_detail": "テスト詳細",
"preconditions": "前提条件", "preconditions": "前提条件",
"expected_result": "期待結果",
"step": "ステップ", "step": "ステップ",
"text": "テキスト", "text": "テキスト",
"steps": "ステップ", "steps": "ステップ",
"new_step": "新規ステップ", "new_step": "新規ステップ",
"details_of_the_step": "ステップ詳細", "details_of_the_step": "ステップ詳細",
"expected_result": "期待結果",
"delete_this_step": "このステップを削除", "delete_this_step": "このステップを削除",
"insert_step": "ステップを挿入", "insert_step": "ステップを挿入",
"attachments": "添付ファイル", "attachments": "添付ファイル",
@@ -272,7 +272,26 @@
"include_in_run": "テストランに含める", "include_in_run": "テストランに含める",
"exclude_from_run": "テストランから除外する", "exclude_from_run": "テストランから除外する",
"no_cases_found": "テストケースが見つかりません", "no_cases_found": "テストケースが見つかりません",
"are_you_sure_leave": "ページを離れてもよろしいですか" "are_you_sure_leave": "ページを離れてもよろしいですか",
"type": "タイプ",
"other": "その他",
"security": "セキュリティ",
"performance": "パフォーマンス",
"accessibility": "アクセシビリティ",
"functional": "機能",
"acceptance": "受け入れ",
"usability": "ユーザビリティ",
"smoke_sanity": "スモーク/サニティ",
"compatibility": "互換性",
"destructive": "破壊",
"regression": "回帰",
"automated": "自動",
"manual": "手動",
"test_detail": "テスト詳細",
"preconditions": "前提条件",
"expected_result": "期待結果",
"details_of_the_step": "ステップ詳細",
"close": "閉じる"
}, },
"Members": { "Members": {
"member_management": "メンバー管理", "member_management": "メンバー管理",

View File

@@ -43,12 +43,12 @@ export default function Page({
template: t('template'), template: t('template'),
testDetail: t('test_detail'), testDetail: t('test_detail'),
preconditions: t('preconditions'), preconditions: t('preconditions'),
expectedResult: t('expected_result'),
step: t('step'), step: t('step'),
text: t('text'), text: t('text'),
steps: t('steps'), steps: t('steps'),
newStep: t('new_step'), newStep: t('new_step'),
detailsOfTheStep: t('details_of_the_step'), detailsOfTheStep: t('details_of_the_step'),
expectedResult: t('expected_result'),
deleteThisStep: t('delete_this_step'), deleteThisStep: t('delete_this_step'),
insertStep: t('insert_step'), insertStep: t('insert_step'),
attachments: t('attachments'), attachments: t('attachments'),

View File

@@ -310,7 +310,7 @@ export default function RunEditor({ projectId, runId, messages, locale }: Props)
isDisabled={!context.isProjectReporter(Number(projectId))} isDisabled={!context.isProjectReporter(Number(projectId))}
selectedKeys={selectedKeys} selectedKeys={selectedKeys}
onSelectionChange={setSelectedKeys} onSelectionChange={setSelectedKeys}
onStatusChange={handleChangeStatus} onChangeStatus={handleChangeStatus}
onIncludeCase={(includeTestId) => handleIncludeExcludeCase(true, includeTestId)} onIncludeCase={(includeTestId) => handleIncludeExcludeCase(true, includeTestId)}
onExcludeCase={(excludeCaseId) => handleIncludeExcludeCase(false, excludeCaseId)} onExcludeCase={(excludeCaseId) => handleIncludeExcludeCase(false, excludeCaseId)}
messages={messages} messages={messages}

View File

@@ -0,0 +1,98 @@
import { Button, Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Chip, Divider } from '@nextui-org/react';
import { RunMessages } from '@/types/run';
import { CaseType } from '@/types/case';
import { Circle } from 'lucide-react';
import { priorities, testTypes, templates } from '@/config/selection';
type Props = {
isOpen: boolean;
testCase: CaseType;
onCancel: () => void;
onChangeStatus: (changeCaseId: number, status: number) => {};
messages: RunMessages;
};
export default function showTestCaseDetailDialog({ isOpen, testCase, onCancel, onChangeStatus, messages }: Props) {
return (
<Modal
isOpen={isOpen}
size="3xl"
onOpenChange={() => {
onCancel();
}}
classNames={{
header: 'border-b-[1px] border-[#e5e5e5]',
body: 'border-b-[1px] border-[#e5e5e5]',
}}
>
<ModalContent>
<ModalHeader className="flex flex-col gap-1">{testCase.title}</ModalHeader>
<ModalBody>
<p className={'font-bold mt-2'}>{messages.description}</p>
<div>{testCase.description}</div>
<div className="flex my-2">
<div className="w-1/2">
<p className={'font-bold'}>{messages.priority}</p>
<Chip
startContent={
<Circle
size={8}
className="me-2"
color={priorities[testCase.priority].color}
fill={priorities[testCase.priority].color}
/>
}
variant="light"
>
{messages[priorities[testCase.priority].uid]}
</Chip>
</div>
<div className="w-1/2">
<p className={'font-bold'}>{messages.type}</p>
<div>{messages[testTypes[testCase.type].uid]}</div>
</div>
</div>
</ModalBody>
<ModalBody>
{templates[testCase.template].uid === 'text' ? (
<div className="flex my-2">
<div className="w-1/2">
<p className={'font-bold'}>{messages.preconditions}</p>
<div>{testCase.preConditions}</div>
</div>
<div className="w-1/2">
<p className={'font-bold'}>{messages.expectedResult}</p>
<div>{testCase.expectedResults}</div>
</div>
</div>
) : (
<div className="flex my-2">
{testCase.Steps &&
testCase.Steps.map((step, index) => (
<>
<div className="w-1/2">
<p className={'font-bold'}>{messages.preconditions}</p>
<div>{step.step}</div>
</div>
<div className="w-1/2">
<p className={'font-bold'}>{messages.expectedResult}</p>
<div>{step.result}</div>
</div>
</>
))}
</div>
)}
</ModalBody>
<ModalFooter>
<Button variant="light" onPress={onCancel}>
{messages.close}
</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
}

View File

@@ -25,16 +25,18 @@ import {
CircleX, CircleX,
CircleSlash2, CircleSlash2,
} from 'lucide-react'; } from 'lucide-react';
import { NextUiLinkClasses } from '@/src/navigation';
import { priorities, testRunCaseStatus } from '@/config/selection'; import { priorities, testRunCaseStatus } from '@/config/selection';
import { CaseType } from '@/types/case'; import { CaseType } from '@/types/case';
import { RunMessages } from '@/types/run'; import { RunMessages } from '@/types/run';
import TestCaseDetailDialog from './TestCaseDetailDialog';
type Props = { type Props = {
cases: CaseType[]; cases: CaseType[];
isDisabled: boolean; isDisabled: boolean;
selectedKeys: Selection; selectedKeys: Selection;
onSelectionChange: React.Dispatch<React.SetStateAction<Selection>>; onSelectionChange: React.Dispatch<React.SetStateAction<Selection>>;
onStatusChange: (changeCaseId: number, status: number) => {}; onChangeStatus: (changeCaseId: number, status: number) => {};
onIncludeCase: (includeCaseId: number) => {}; onIncludeCase: (includeCaseId: number) => {};
onExcludeCase: (excludeCaseId: number) => {}; onExcludeCase: (excludeCaseId: number) => {};
messages: RunMessages; messages: RunMessages;
@@ -45,7 +47,7 @@ export default function TestCaseSelector({
isDisabled, isDisabled,
selectedKeys, selectedKeys,
onSelectionChange, onSelectionChange,
onStatusChange, onChangeStatus,
onIncludeCase, onIncludeCase,
onExcludeCase, onExcludeCase,
messages, messages,
@@ -124,6 +126,17 @@ export default function TestCaseSelector({
const runStatus = testCase.RunCases && testCase.RunCases.length > 0 ? testCase.RunCases[0].status : 0; const runStatus = testCase.RunCases && testCase.RunCases.length > 0 ? testCase.RunCases[0].status : 0;
switch (columnKey) { switch (columnKey) {
case 'title':
return (
<Button
size="sm"
variant="light"
className="data-[hover=true]:bg-transparent"
onPress={() => showTestCaseDetailDialog(testCase)}
>
<span className={NextUiLinkClasses}>{cellValue}</span>
</Button>
);
case 'priority': case 'priority':
return ( return (
<div className={isIncluded ? chipBaseClass : chipBaseClass + notIncludedCaseClass}> <div className={isIncluded ? chipBaseClass : chipBaseClass + notIncludedCaseClass}>
@@ -154,7 +167,7 @@ export default function TestCaseSelector({
<DropdownItem <DropdownItem
key={runCaseStatus.uid} key={runCaseStatus.uid}
startContent={renderStatusIcon(runCaseStatus.uid)} startContent={renderStatusIcon(runCaseStatus.uid)}
onPress={() => onStatusChange(testCase.id, index)} onPress={() => onChangeStatus(testCase.id, index)}
> >
{messages[runCaseStatus.uid]} {messages[runCaseStatus.uid]}
</DropdownItem> </DropdownItem>
@@ -226,6 +239,29 @@ export default function TestCaseSelector({
onSelectionChange(keys); onSelectionChange(keys);
}; };
// Test Case Detail
const [isTestCaseDetailDialogOpen, setIsTestCaseDetailDialogOpen] = useState(false);
const [showingTestCase, setShowingTestCase] = useState<CaseType>({
id: 0,
title: '',
state: 0,
priority: 0,
type: 0,
automationStatus: 0,
description: '',
template: 0,
preConditions: '',
expectedResults: '',
folderId: 0,
});
const showTestCaseDetailDialog = (showTestCase: CaseType) => {
setIsTestCaseDetailDialogOpen(true);
setShowingTestCase(showTestCase);
};
const hideTestCaseDetailDialog = () => {
setIsTestCaseDetailDialogOpen(false);
};
return ( return (
<> <>
<Table <Table
@@ -260,6 +296,14 @@ export default function TestCaseSelector({
))} ))}
</TableBody> </TableBody>
</Table> </Table>
<TestCaseDetailDialog
isOpen={isTestCaseDetailDialogOpen}
testCase={showingTestCase}
onCancel={hideTestCaseDetailDialog}
onChangeStatus={(showingCaseId, newStatus) => onChangeStatus(showingCaseId, newStatus)}
messages={messages}
/>
</> </>
); );
} }

View File

@@ -37,6 +37,24 @@ export default function Page({ params }: { params: { projectId: string; runId: s
excludeFromRun: t('exclude_from_run'), excludeFromRun: t('exclude_from_run'),
noCasesFound: t('no_cases_found'), noCasesFound: t('no_cases_found'),
areYouSureLeave: t('are_you_sure_leave'), areYouSureLeave: t('are_you_sure_leave'),
type: t('type'),
other: t('other'),
security: t('security'),
performance: t('performance'),
accessibility: t('accessibility'),
functional: t('functional'),
acceptance: t('acceptance'),
usability: t('usability'),
smokeSanity: t('smoke_sanity'),
compatibility: t('compatibility'),
destructive: t('destructive'),
regression: t('regression'),
automated: t('automated'),
manual: t('manual'),
preconditions: t('preconditions'),
expectedResult: t('expected_result'),
detailsOfTheStep: t('details_of_the_step'),
close: t('close'),
}; };
return <RunEditor projectId={params.projectId} runId={params.runId} messages={messages} locale={params.locale} />; return <RunEditor projectId={params.projectId} runId={params.runId} messages={messages} locale={params.locale} />;

View File

@@ -123,12 +123,12 @@ type CaseMessages = {
template: string; template: string;
testDetail: string; testDetail: string;
preconditions: string; preconditions: string;
expectedResult: string;
step: string; step: string;
text: string; text: string;
steps: string; steps: string;
newStep: string; newStep: string;
detailsOfTheStep: string; detailsOfTheStep: string;
expectedResult: string;
deleteThisStep: string; deleteThisStep: string;
insertStep: string; insertStep: string;
attachments: string; attachments: string;

View File

@@ -83,6 +83,24 @@ type RunMessages = {
excludeFromRun: string; excludeFromRun: string;
noCasesFound: string; noCasesFound: string;
areYouSureLeave: string; areYouSureLeave: string;
type: string;
other: string;
security: string;
performance: string;
accessibility: string;
functional: string;
acceptance: string;
usability: string;
smokeSanity: string;
compatibility: string;
destructive: string;
regression: string;
automated: string;
manual: string;
preconditions: string;
expectedResult: string;
detailsOfTheStep: string;
close: string;
}; };
export type { RunType, RunCaseType, RunStatusCountType, ProgressSeriesType, RunsMessages, RunMessages }; export type { RunType, RunCaseType, RunStatusCountType, ProgressSeriesType, RunsMessages, RunMessages };