feat: view customize on run and runCases pane
This commit is contained in:
@@ -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');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
() => ({
|
() => ({
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user