feat: view customize on run and runCases pane

This commit is contained in:
Takeshi Kimata
2024-06-08 11:10:36 +09:00
parent 2b5a806aee
commit 37f97f97b5
4 changed files with 60 additions and 16 deletions

View File

@@ -105,7 +105,7 @@ function verifyEditableMiddleware(sequelize) {
// find project id from folderId // find project id from folderId
const folder = await Folder.findByPk(folderId); const folder = await Folder.findByPk(folderId);
const projectId = folder && folder.id; const projectId = folder && folder.projectId;
if (!projectId) { if (!projectId) {
return res.status(404).send('failed to find projectId'); return res.status(404).send('failed to find projectId');
} }
@@ -229,7 +229,7 @@ function verifyEditableMiddleware(sequelize) {
// find project id from runId // find project id from runId
const run = await Run.findByPk(runId); const run = await Run.findByPk(runId);
const projectId = run && run.id; const projectId = run && run.projectId;
if (!projectId) { if (!projectId) {
return res.status(404).send('failed to find projectId'); return res.status(404).send('failed to find projectId');
} }

View File

@@ -1,4 +1,4 @@
import { useState, useMemo, useCallback } from 'react'; import { useState, useEffect, useMemo } from 'react';
import { import {
Table, Table,
TableHeader, TableHeader,
@@ -28,6 +28,15 @@ type Props = {
}; };
export default function RunsTable({ projectId, isDisabled, runs, onDeleteRun, messages, locale }: Props) { export default function RunsTable({ projectId, isDisabled, runs, onDeleteRun, messages, locale }: Props) {
const [disabledKeys, setDisabledKeys] = useState<string[]>([]);
useEffect(() => {
if (isDisabled) {
setDisabledKeys(['delete']);
} else {
setDisabledKeys([]);
}
}, [isDisabled]);
const headerColumns = [ const headerColumns = [
{ name: messages.id, uid: 'id', sortable: true }, { name: messages.id, uid: 'id', sortable: true },
{ name: messages.name, uid: 'name', sortable: true }, { name: messages.name, uid: 'name', sortable: true },
@@ -54,7 +63,7 @@ export default function RunsTable({ projectId, isDisabled, runs, onDeleteRun, me
return text.length > maxLength ? text.slice(0, maxLength) + '...' : text; return text.length > maxLength ? text.slice(0, maxLength) + '...' : text;
}; };
const renderCell = useCallback((run: RunType, columnKey: Key) => { const renderCell = (run: RunType, columnKey: Key) => {
const cellValue = run[columnKey as keyof RunType]; const cellValue = run[columnKey as keyof RunType];
switch (columnKey) { switch (columnKey) {
@@ -83,8 +92,8 @@ export default function RunsTable({ projectId, isDisabled, runs, onDeleteRun, me
<MoreVertical size={16} /> <MoreVertical size={16} />
</Button> </Button>
</DropdownTrigger> </DropdownTrigger>
<DropdownMenu aria-label="run actions"> <DropdownMenu aria-label="run actions" disabledKeys={disabledKeys}>
<DropdownItem className="text-danger" isDisabled={isDisabled} onClick={() => onDeleteRun(run.id)}> <DropdownItem className="text-danger" key="delete" onClick={() => onDeleteRun(run.id)}>
{messages.deleteRun} {messages.deleteRun}
</DropdownItem> </DropdownItem>
</DropdownMenu> </DropdownMenu>
@@ -93,7 +102,7 @@ export default function RunsTable({ projectId, isDisabled, runs, onDeleteRun, me
default: default:
return cellValue; return cellValue;
} }
}, []); };
const classNames = useMemo( const classNames = useMemo(
() => ({ () => ({

View File

@@ -221,6 +221,7 @@ export default function RunEditor({ projectId, runId, messages, locale }: Props)
<Button <Button
startContent={<Save size={16} />} startContent={<Save size={16} />}
size="sm" size="sm"
isDisabled={!context.isProjectReporter(Number(projectId))}
color="primary" color="primary"
isLoading={isUpdating} isLoading={isUpdating}
onPress={async () => { onPress={async () => {
@@ -310,7 +311,12 @@ export default function RunEditor({ projectId, runId, messages, locale }: Props)
{(selectedKeys.size > 0 || selectedKeys === 'all') && ( {(selectedKeys.size > 0 || selectedKeys === 'all') && (
<Dropdown> <Dropdown>
<DropdownTrigger> <DropdownTrigger>
<Button size="sm" color="primary" endContent={<ChevronDown size={16} />}> <Button
size="sm"
isDisabled={!context.isProjectReporter(Number(projectId))}
color="primary"
endContent={<ChevronDown size={16} />}
>
{messages.testCaseSelection} {messages.testCaseSelection}
</Button> </Button>
</DropdownTrigger> </DropdownTrigger>
@@ -351,6 +357,7 @@ export default function RunEditor({ projectId, runId, messages, locale }: Props)
<div className="w-9/12"> <div className="w-9/12">
<TestCaseSelector <TestCaseSelector
cases={testcases} cases={testcases}
isDisabled={!context.isProjectReporter(Number(projectId))}
selectedKeys={selectedKeys} selectedKeys={selectedKeys}
onSelectionChange={setSelectedKeys} onSelectionChange={setSelectedKeys}
onStatusChange={handleChangeStatus} onStatusChange={handleChangeStatus}

View File

@@ -1,4 +1,4 @@
import { useState, useMemo } from 'react'; import { useState, useEffect, useMemo } from 'react';
import { import {
Table, Table,
TableHeader, TableHeader,
@@ -31,6 +31,7 @@ import { RunsMessages } from '@/types/run';
type Props = { type Props = {
cases: CaseType[]; cases: CaseType[];
isDisabled: boolean;
selectedKeys: Selection; selectedKeys: Selection;
onSelectionChange: React.Dispatch<React.SetStateAction<Selection>>; onSelectionChange: React.Dispatch<React.SetStateAction<Selection>>;
onStatusChange: (changeCaseId: number, status: number) => {}; onStatusChange: (changeCaseId: number, status: number) => {};
@@ -41,6 +42,7 @@ type Props = {
export default function TestCaseSelector({ export default function TestCaseSelector({
cases, cases,
isDisabled,
selectedKeys, selectedKeys,
onSelectionChange, onSelectionChange,
onStatusChange, onStatusChange,
@@ -56,6 +58,22 @@ export default function TestCaseSelector({
{ name: messages.actions, uid: 'actions' }, { name: messages.actions, uid: 'actions' },
]; ];
const [disabledStatusKeys, setDisabledStatusKeys] = useState<string[]>([]);
const [disabledIncludeExcludeKeys, setDisabledIncludeExcludeKeys] = useState<string[]>([]);
useEffect(() => {
if (isDisabled) {
setDisabledStatusKeys(
testRunCaseStatus.map((entry) => {
return entry.uid;
})
);
setDisabledIncludeExcludeKeys(['include', 'exclude']);
} else {
setDisabledStatusKeys([]);
setDisabledIncludeExcludeKeys([]);
}
}, [isDisabled]);
const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({ const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({
column: 'id', column: 'id',
direction: 'ascending', direction: 'ascending',
@@ -118,10 +136,10 @@ export default function TestCaseSelector({
<span className="w-12">{isIncluded && messages[testRunCaseStatus[cellValue].uid]}</span> <span className="w-12">{isIncluded && messages[testRunCaseStatus[cellValue].uid]}</span>
</Button> </Button>
</DropdownTrigger> </DropdownTrigger>
<DropdownMenu aria-label="test case actions"> <DropdownMenu disabledKeys={disabledStatusKeys} aria-label="test case actions">
{testRunCaseStatus.map((runCaseStatus, index) => ( {testRunCaseStatus.map((runCaseStatus, index) => (
<DropdownItem <DropdownItem
key={index} key={runCaseStatus.uid}
startContent={renderStatusIcon(runCaseStatus.uid)} startContent={renderStatusIcon(runCaseStatus.uid)}
onPress={() => onStatusChange(testCase.id, index)} onPress={() => onStatusChange(testCase.id, index)}
> >
@@ -139,18 +157,28 @@ export default function TestCaseSelector({
<MoreVertical size={16} /> <MoreVertical size={16} />
</Button> </Button>
</DropdownTrigger> </DropdownTrigger>
<DropdownMenu aria-label="include or exclude actions"> <DropdownMenu disabledKeys={disabledIncludeExcludeKeys} aria-label="include or exclude actions">
<DropdownItem <DropdownItem
key="include"
startContent={<CopyPlus size={16} />} startContent={<CopyPlus size={16} />}
isDisabled={testCase.isIncluded} onPress={() => {
onPress={() => onIncludeCase(testCase.id)} if (testCase.isIncluded) {
return;
}
onIncludeCase(testCase.id);
}}
> >
{messages.includeInRun} {messages.includeInRun}
</DropdownItem> </DropdownItem>
<DropdownItem <DropdownItem
key="exclude"
startContent={<CopyMinus size={16} />} startContent={<CopyMinus size={16} />}
isDisabled={!testCase.isIncluded} onPress={() => {
onPress={() => onExcludeCase(testCase.id)} if (!testCase.isIncluded) {
return;
}
onExcludeCase(testCase.id);
}}
> >
{messages.excludeFromRun} {messages.excludeFromRun}
</DropdownItem> </DropdownItem>