feat(测试计划): 测试计划详情用例批量添加缺陷&关联缺陷联调&填充缺陷内容
This commit is contained in:
parent
d20023989e
commit
2836631b29
|
@ -9,7 +9,9 @@ import {
|
|||
archivedPlanUrl,
|
||||
AssociatedBugToApiCaseUrl,
|
||||
AssociatedBugToScenarioCaseUrl,
|
||||
BatchAddBugToCaseUrl,
|
||||
BatchAddBugToApiCaseUrl,
|
||||
BatchAddBugToFunctionalCaseUrl,
|
||||
BatchAddBugToScenarioCaseUrl,
|
||||
batchArchivedPlanUrl,
|
||||
BatchAssociatedBugToCaseUrl,
|
||||
batchCopyPlanUrl,
|
||||
|
@ -19,6 +21,8 @@ import {
|
|||
BatchDisassociateCaseUrl,
|
||||
BatchEditTestPlanUrl,
|
||||
BatchExecutePlanUrl,
|
||||
BatchLinkBugToApiCaseUrl,
|
||||
BatchLinkBugToScenarioCaseUrl,
|
||||
BatchMoveApiCaseUrl,
|
||||
BatchMoveApiScenarioUrl,
|
||||
BatchMoveFeatureCaseUrl,
|
||||
|
@ -480,7 +484,23 @@ export function cancelBugFromScenarioCase(id: string) {
|
|||
export function batchAssociatedBugToCase(data: TableQueryParams) {
|
||||
return MSR.post({ url: BatchAssociatedBugToCaseUrl, data });
|
||||
}
|
||||
// 测试计划-详情-批量新建缺陷
|
||||
export function batchAddBugToCase(data: { request: BugEditFormObject; fileList: File[] }) {
|
||||
return MSR.uploadFile({ url: BatchAddBugToCaseUrl }, data, '', true);
|
||||
// 测试计划-详情-功能用例-批量新建缺陷
|
||||
export function batchAddBugToFunctionCase(data: { request: BugEditFormObject; fileList: File[] }) {
|
||||
return MSR.uploadFile({ url: BatchAddBugToFunctionalCaseUrl }, data, '', true);
|
||||
}
|
||||
// 测试计划-详情-接口用例-批量新建缺陷
|
||||
export function batchAddBugToApiCase(data: { request: BugEditFormObject; fileList: File[] }) {
|
||||
return MSR.uploadFile({ url: BatchAddBugToApiCaseUrl }, data, '', true);
|
||||
}
|
||||
// 测试计划-详情-场景用例-批量新建缺陷
|
||||
export function batchAddBugToScenarioCase(data: { request: BugEditFormObject; fileList: File[] }) {
|
||||
return MSR.uploadFile({ url: BatchAddBugToScenarioCaseUrl }, data, '', true);
|
||||
}
|
||||
// 测试计划-详情-接口用例-批量关联缺陷
|
||||
export function batchLinkBugToApiCase(data: TableQueryParams) {
|
||||
return MSR.post({ url: BatchLinkBugToApiCaseUrl, data });
|
||||
}
|
||||
// 测试计划-详情-接口用例-批量关联缺陷
|
||||
export function batchLinkBugToScenarioCase(data: TableQueryParams) {
|
||||
return MSR.post({ url: BatchLinkBugToScenarioCaseUrl, data });
|
||||
}
|
||||
|
|
|
@ -172,4 +172,12 @@ export const CancelBugFromScenarioCaseUrl = '/test-plan/api/scenario/disassociat
|
|||
// 测试计划-详情-用例列表-批量关联缺陷
|
||||
export const BatchAssociatedBugToCaseUrl = '/test-plan/functional/case/batch/associate-bug';
|
||||
// 测试计划-详情-用例列表-批量新建缺陷
|
||||
export const BatchAddBugToCaseUrl = '/test-plan/functional/case/batch/add-bug';
|
||||
export const BatchAddBugToFunctionalCaseUrl = '/test-plan/functional/case/batch/add-bug';
|
||||
// 测试计划-详情-接口列表-批量新建缺陷
|
||||
export const BatchAddBugToApiCaseUrl = '/test-plan/api/case/batch/add-bug';
|
||||
// 测试计划-详情-场景列表-批量新建缺陷
|
||||
export const BatchAddBugToScenarioCaseUrl = '/test-plan/api/scenario/batch/add-bug';
|
||||
// 测试计划-详情-接口用例列表-批量关联缺陷
|
||||
export const BatchLinkBugToApiCaseUrl = '/test-plan/api/case/batch/associate-bug';
|
||||
// 测试计划-详情-场景用例列表-批量关联缺陷
|
||||
export const BatchLinkBugToScenarioCaseUrl = '/test-plan/api/scenario/batch/associate-bug';
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
@load-list="loadList"
|
||||
/>
|
||||
<a-dropdown
|
||||
v-if="hasAllPermission(['PROJECT_BUG:READ', ...(props.linkBugPermission || [])])"
|
||||
v-if="hasAllPermission(['PROJECT_BUG:READ', ...(props.permission || [])])"
|
||||
position="bl"
|
||||
@select="handleSelect"
|
||||
@popup-visible-change="popupVisibleChange"
|
||||
|
@ -23,7 +23,7 @@
|
|||
</a-button>
|
||||
|
||||
<template #content>
|
||||
<a-doption v-if="hasAnyPermission(props.linkBugPermission || []) && props.existedDefect" value="linkBug">
|
||||
<a-doption v-if="hasAnyPermission(['PROJECT_BUG:READ']) && props.existedDefect" value="linkBug">
|
||||
{{ t('caseManagement.featureCase.linkDefect') }}
|
||||
</a-doption>
|
||||
<a-doption v-if="hasAnyPermission(['PROJECT_BUG:READ+ADD'])" value="newBug">
|
||||
|
@ -52,7 +52,7 @@
|
|||
caseType: CaseLinkEnum; // 用例类型
|
||||
canEdit: boolean;
|
||||
bugList?: CaseBugItem[];
|
||||
linkBugPermission?: string[];
|
||||
permission?: string[];
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
|
|
@ -31,6 +31,22 @@
|
|||
ol {
|
||||
list-style: decimal !important;
|
||||
}
|
||||
table {
|
||||
@apply overflow-x-auto;
|
||||
.ms-scroll-bar();
|
||||
tr {
|
||||
background: white !important;
|
||||
th,
|
||||
td {
|
||||
padding: 8px;
|
||||
min-width: 80px;
|
||||
}
|
||||
p {
|
||||
margin: 0 !important;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ms-comment-list {
|
||||
@apply h-full;
|
||||
|
|
|
@ -88,7 +88,7 @@
|
|||
import { BugManagementRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
const AddDefectDrawer = defineAsyncComponent(
|
||||
() => import('@/views/case-management/caseManagementFeature/components/tabContent/tabBug/addDefectDrawer.vue')
|
||||
() => import('@/views/case-management/components/addDefectDrawer/index.vue')
|
||||
);
|
||||
const LinkDefectDrawer = defineAsyncComponent(
|
||||
() => import('@/views/case-management/components/linkDefectDrawer.vue')
|
||||
|
|
|
@ -65,7 +65,12 @@
|
|||
</a-tooltip>
|
||||
<template #content>
|
||||
<div class="w-[440px] rounded bg-white p-[16px] shadow-[0_0_10px_rgba(0,0,0,0.05)]">
|
||||
<ExecuteSubmit :select-node="selectNode" :test-plan-id="props.planId" @done="handleExecuteDone" />
|
||||
<ExecuteSubmit
|
||||
:select-node="selectNode"
|
||||
:test-plan-id="props.planId"
|
||||
is-default-activate
|
||||
@done="handleExecuteDone"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</a-trigger>
|
||||
|
@ -184,8 +189,8 @@
|
|||
import useMinderBaseApi from '@/components/business/ms-minders/featureCaseMinder/useMinderBaseApi';
|
||||
import BugList from './bugList.vue';
|
||||
import AddStep from '@/views/case-management/caseManagementFeature/components/addStep.vue';
|
||||
import AddDefectDrawer from '@/views/case-management/caseManagementFeature/components/tabContent/tabBug/addDefectDrawer.vue';
|
||||
import ReviewCommentList from '@/views/case-management/caseManagementFeature/components/tabContent/tabComment/reviewCommentList.vue';
|
||||
import AddDefectDrawer from '@/views/case-management/components/addDefectDrawer/index.vue';
|
||||
import LinkDefectDrawer from '@/views/case-management/components/linkDefectDrawer.vue';
|
||||
import ExecuteForm from '@/views/test-plan/testPlan/detail/featureCase/components/executeForm.vue';
|
||||
import ExecuteSubmit from '@/views/test-plan/testPlan/detail/featureCase/detail/executeSubmit.vue';
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
ExtensionCode,
|
||||
ExtensionCodeBlock,
|
||||
ExtensionColor,
|
||||
ExtensionColumn,
|
||||
ExtensionColumns,
|
||||
ExtensionCommands,
|
||||
ExtensionDocument,
|
||||
ExtensionDraggable,
|
||||
|
@ -54,6 +56,7 @@
|
|||
ExtensionStrike,
|
||||
ExtensionSubscript,
|
||||
ExtensionSuperscript,
|
||||
ExtensionTable,
|
||||
ExtensionTaskList,
|
||||
ExtensionText,
|
||||
ExtensionTextAlign,
|
||||
|
@ -295,14 +298,15 @@
|
|||
ExtensionLink.configure({
|
||||
autolink: false,
|
||||
openOnClick: false,
|
||||
linkOnPaste: true,
|
||||
}),
|
||||
ExtensionTextAlign.configure({
|
||||
types: ['heading', 'paragraph'],
|
||||
}),
|
||||
ExtensionUnderline,
|
||||
// ExtensionTable.configure({
|
||||
// resizable: true,
|
||||
// }),
|
||||
ExtensionTable.configure({
|
||||
resizable: true,
|
||||
}),
|
||||
ExtensionSubscript,
|
||||
ExtensionSuperscript,
|
||||
ExtensionPlaceholder.configure({
|
||||
|
@ -394,7 +398,7 @@
|
|||
}) as Extension<any, any>,
|
||||
CharacterCount.configure({
|
||||
limit: props.limitLength || null,
|
||||
}),
|
||||
}) as Extension,
|
||||
],
|
||||
autofocus: props.autoFocus,
|
||||
editable: props.editable,
|
||||
|
@ -594,18 +598,18 @@
|
|||
:deep(.halo-rich-text-editor .ProseMirror) {
|
||||
padding: 16px !important;
|
||||
min-height: 130px;
|
||||
p:first-child {
|
||||
> p:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
:deep(.halo-rich-text-editor) {
|
||||
padding: 16px !important;
|
||||
padding: 2px 16px 16px !important;
|
||||
.editor-header {
|
||||
.ms-scroll-bar();
|
||||
|
||||
justify-content: start !important;
|
||||
}
|
||||
p:first-child {
|
||||
> p:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
@ -624,6 +628,24 @@
|
|||
:deep(.editor-content) {
|
||||
.ms-scroll-bar();
|
||||
}
|
||||
:deep(.tableWrapper) {
|
||||
.ms-scroll-bar();
|
||||
table {
|
||||
tr,
|
||||
th {
|
||||
background: white !important;
|
||||
}
|
||||
tr {
|
||||
th,
|
||||
td {
|
||||
padding: 8px;
|
||||
}
|
||||
p {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
import { FormTableColumn } from '@/components/pure/ms-form-table/index.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
export function generateTableHTML(columns: FormTableColumn[], data: Record<string, any>[]): string {
|
||||
const maxColWidth = 300; // 最大宽度
|
||||
let tableHTML = '<table style="width:100%;" table-layout="auto"><tbody>';
|
||||
|
||||
// 生成表头
|
||||
tableHTML += '<tr>';
|
||||
columns.forEach((column: FormTableColumn) => {
|
||||
const colWidth = Math.min(column.width || maxColWidth, maxColWidth);
|
||||
tableHTML += `<th colspan="1" rowspan="1" width="${colWidth}" colwidth="${colWidth}"><p style="max-width:${colWidth}px;">${t(
|
||||
column.title as string
|
||||
)}</p></th>`;
|
||||
});
|
||||
tableHTML += '</tr>';
|
||||
|
||||
// 生成数据行
|
||||
data.forEach((row) => {
|
||||
tableHTML += '<tr>';
|
||||
columns.forEach((column: FormTableColumn) => {
|
||||
// 生成单元格内容
|
||||
const colWidth = Math.min(column.width || maxColWidth, maxColWidth);
|
||||
const dataIndexKey = column.dataIndex as string;
|
||||
const cellContent = column.format ? column.format(row) : row[dataIndexKey] || '';
|
||||
|
||||
tableHTML += `<td colspan="1" rowspan="1" width="${colWidth}" colwidth="${colWidth}"><p style="max-width:${colWidth}px;">${
|
||||
cellContent || '-'
|
||||
}</p></td>`;
|
||||
});
|
||||
tableHTML += '</tr>';
|
||||
});
|
||||
|
||||
tableHTML += '</tbody></table>';
|
||||
return tableHTML;
|
||||
}
|
||||
|
||||
export default {};
|
|
@ -419,6 +419,7 @@ export interface PlanMinderNodeData extends MinderJsonNodeData {
|
|||
retryTimes: number;
|
||||
retryInterval: number;
|
||||
stopOnFail: boolean;
|
||||
tempCollectionNode?: boolean; // 关联用例到未保存的临时测试集节点标识
|
||||
}
|
||||
export interface PlanMinderNode extends MinderJsonNode {
|
||||
data: PlanMinderNodeData;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
:placeholder="t('editor.placeholder')"
|
||||
:upload-image="handleUploadImage"
|
||||
:preview-url="`${EditorPreviewFileUrl}/${appStore.currentProjectId}`"
|
||||
auto-height
|
||||
/>
|
||||
<div v-else v-dompurify-html="form?.description || '-'" class="markdown-body"></div>
|
||||
</div>
|
||||
|
|
|
@ -230,14 +230,22 @@
|
|||
import { TableQueryParams } from '@/models/common';
|
||||
import { SelectValue } from '@/models/projectManagement/menuManagement';
|
||||
import type { CustomField } from '@/models/setting/template';
|
||||
import { CaseLinkEnum } from '@/enums/caseEnum';
|
||||
|
||||
import { convertToFile } from '../case-management/caseManagementFeature/components/utils';
|
||||
import { convertToFileByBug } from './utils';
|
||||
import { getCaseTemplateContent } from '@/views/case-management/components/addDefectDrawer/utils';
|
||||
|
||||
const props = defineProps<{
|
||||
templateId: string; // 缺陷模板id
|
||||
bugId?: string; // 缺陷id,不传递为创建
|
||||
isDrawer?: boolean; // 是否是弹窗模式
|
||||
caseType?: CaseLinkEnum; // 用例类型
|
||||
fillConfig?: {
|
||||
isQuickFillContent: boolean; // 是否快速填充内容
|
||||
detailId: string; // 快填详情id
|
||||
name: string; // 用例明细名称
|
||||
};
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
@ -394,6 +402,18 @@
|
|||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 设置填充内容
|
||||
async function setFillContent() {
|
||||
if (props.fillConfig) {
|
||||
const { isQuickFillContent, detailId, name } = props.fillConfig;
|
||||
if (isQuickFillContent && props.caseType) {
|
||||
const fillContent = await getCaseTemplateContent(props.caseType, detailId);
|
||||
form.value.description = fillContent || '';
|
||||
form.value.title = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
const currentCustomFields = ref<CustomFieldItem[]>([]);
|
||||
// TODO:: Record<string, any>
|
||||
const templateChange = async (v: SelectValue, request?: BugTemplateRequest) => {
|
||||
|
@ -421,6 +441,8 @@
|
|||
res.systemFields.forEach((item: CustomField) => {
|
||||
form.value[item.fieldId] = item.defaultValue;
|
||||
});
|
||||
|
||||
setFillContent();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
|
|
|
@ -25,8 +25,10 @@
|
|||
.bug-content-popover {
|
||||
.arco-popover-content {
|
||||
overflow: auto;
|
||||
padding-right: 8px;
|
||||
max-height: 400px;
|
||||
.ms-scroll-bar();
|
||||
@apply overflow-x-auto overflow-y-auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -129,10 +129,10 @@
|
|||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import AddDefectDrawer from './addDefectDrawer.vue';
|
||||
import BugList from './bugList.vue';
|
||||
import BugNamePopover from '@/views/case-management/caseManagementFeature/components/tabContent/tabBug/bugNamePopover.vue';
|
||||
import TableFilter from '@/views/case-management/caseManagementFeature/components/tableFilter.vue';
|
||||
import AddDefectDrawer from '@/views/case-management/components/addDefectDrawer/index.vue';
|
||||
import LinkDefectDrawer from '@/views/case-management/components/linkDefectDrawer.vue';
|
||||
|
||||
import { getBugList, getCustomOptionHeader } from '@/api/modules/bug-management';
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
v-model:template-id="bugTemplateId"
|
||||
is-drawer
|
||||
:bug-id="bugId"
|
||||
:case-type="props.caseType"
|
||||
:fill-config="props.fillConfig"
|
||||
@save-params="saveParams"
|
||||
/>
|
||||
</div>
|
||||
|
@ -49,11 +51,16 @@
|
|||
import BugDetail from '@/views/bug-management/edit.vue';
|
||||
|
||||
import { createOrUpdateBug, getTemplateOption } from '@/api/modules/bug-management';
|
||||
import { batchAddBugToCase } from '@/api/modules/test-plan/testPlan';
|
||||
import {
|
||||
batchAddBugToApiCase,
|
||||
batchAddBugToFunctionCase,
|
||||
batchAddBugToScenarioCase,
|
||||
} from '@/api/modules/test-plan/testPlan';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { useAppStore } from '@/store';
|
||||
|
||||
import { BugEditFormObject } from '@/models/bug-management';
|
||||
import { CaseLinkEnum } from '@/enums/caseEnum';
|
||||
|
||||
const { t } = useI18n();
|
||||
const appStore = useAppStore();
|
||||
|
@ -67,6 +74,12 @@
|
|||
bugId?: string;
|
||||
extraParams?: Record<string, any> | (() => Record<string, any>);
|
||||
isBatch?: boolean;
|
||||
caseType?: CaseLinkEnum; // 批量添加到的用例类型
|
||||
fillConfig?: {
|
||||
isQuickFillContent: boolean; // 是否快速填充内容
|
||||
detailId: string; // 功能用例为用例详情id, 注意:接口用例为最后执行报告id来获取执行详情展示断言,场景也为执行报告id查看报告详情
|
||||
name: string; // 用例明细名称
|
||||
};
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
@ -112,16 +125,22 @@
|
|||
showBugDrawer.value = false;
|
||||
}
|
||||
|
||||
const batchAddApiMap: Record<string, (params: { request: BugEditFormObject; fileList: File[] }) => Promise<any>> = {
|
||||
[CaseLinkEnum.FUNCTIONAL]: batchAddBugToFunctionCase,
|
||||
[CaseLinkEnum.API]: batchAddBugToApiCase,
|
||||
[CaseLinkEnum.SCENARIO]: batchAddBugToScenarioCase,
|
||||
};
|
||||
|
||||
async function saveParams(isContinue: boolean, params: { request: BugEditFormObject; fileList: File[] }) {
|
||||
try {
|
||||
drawerLoading.value = true;
|
||||
const { request, fileList } = params;
|
||||
if (props.isBatch) {
|
||||
const extraParam =
|
||||
props.extraParams && typeof props.extraParams === 'function' ? await props.extraParams() : props.extraParams;
|
||||
await batchAddBugToCase({ request: { ...request, ...extraParam }, fileList });
|
||||
const extraParam =
|
||||
props.extraParams && typeof props.extraParams === 'function' ? await props.extraParams() : props.extraParams;
|
||||
if (props.isBatch && props.caseType) {
|
||||
await batchAddApiMap[props.caseType]({ request: { ...request, ...extraParam }, fileList });
|
||||
} else {
|
||||
await createOrUpdateBug({ request: { ...request, ...props.extraParams }, fileList });
|
||||
await createOrUpdateBug({ request: { ...request, ...extraParam }, fileList });
|
||||
}
|
||||
|
||||
Message.success(props.bugId ? t('common.updateSuccess') : t('common.createSuccess'));
|
|
@ -0,0 +1,221 @@
|
|||
import { statusCodeOptions } from '@/components/pure/ms-advance-filter/index';
|
||||
import { generateTableHTML } from '@/components/pure/ms-rich-text/utils';
|
||||
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
|
||||
import { getApiCaseReport, getApiCaseReportStep, getCaseDetail } from '@/api/modules/test-plan/testPlan';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { ResponseAssertionTableItem } from '@/models/apiTest/common';
|
||||
import { FullResponseAssertionType } from '@/enums/apiEnum';
|
||||
import { CaseLinkEnum } from '@/enums/caseEnum';
|
||||
|
||||
import { responseAssertionTypeMap } from '@/views/api-test/components/config';
|
||||
import { executionResultMap } from '@/views/case-management/caseManagementFeature/components/utils';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
export const detailContentMap: Record<string, string> = {
|
||||
[CaseLinkEnum.FUNCTIONAL]: '',
|
||||
[CaseLinkEnum.API]: '',
|
||||
[CaseLinkEnum.SCENARIO]: '',
|
||||
};
|
||||
// 获取步骤表格
|
||||
function getStepsTable(steps: string) {
|
||||
const templateFieldColumns = [
|
||||
{
|
||||
title: 'system.orgTemplate.numberIndex',
|
||||
dataIndex: 'num',
|
||||
slotName: 'num',
|
||||
width: 30,
|
||||
},
|
||||
{
|
||||
title: 'system.orgTemplate.useCaseStep',
|
||||
slotName: 'caseStep',
|
||||
dataIndex: 'step',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'system.orgTemplate.expectedResult',
|
||||
dataIndex: 'expected',
|
||||
slotName: 'expectedResult',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'system.orgTemplate.actualResult',
|
||||
dataIndex: 'actualResult',
|
||||
slotName: 'actualResult',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'system.orgTemplate.stepExecutionResult',
|
||||
dataIndex: 'executeResult',
|
||||
slotName: 'lastExecResult',
|
||||
width: 120,
|
||||
},
|
||||
];
|
||||
const stepsData = JSON.parse(steps).map((item: any, index: number) => {
|
||||
return {
|
||||
num: index + 1,
|
||||
id: item.id,
|
||||
step: item.desc,
|
||||
expected: item.result,
|
||||
actualResult: item.actualResult,
|
||||
executeResult: item.executeResult ? executionResultMap[item.executeResult].statusText : '-',
|
||||
};
|
||||
});
|
||||
if (!stepsData.length) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
return generateTableHTML(templateFieldColumns, stepsData);
|
||||
}
|
||||
// 获取断言表格
|
||||
function getAssertTable(assertions: ResponseAssertionTableItem[]) {
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'apiTestDebug.assertionItem',
|
||||
dataIndex: 'assertionItem',
|
||||
slotName: 'assertionItem',
|
||||
width: 200,
|
||||
format: (rows: ResponseAssertionTableItem) => {
|
||||
return `<span> [${t(
|
||||
responseAssertionTypeMap[(rows as ResponseAssertionTableItem).assertionType] || 'apiTestDebug.responseBody'
|
||||
)}] ${rows.expectedValue || ''}</span>`;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'apiTestDebug.actualValue',
|
||||
dataIndex: 'actualValue',
|
||||
slotName: 'actualValue',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'apiTestDebug.condition',
|
||||
dataIndex: 'condition',
|
||||
slotName: 'condition',
|
||||
width: 100,
|
||||
format: (rows: ResponseAssertionTableItem) => {
|
||||
return `<span> ${
|
||||
rows.assertionType === FullResponseAssertionType.RESPONSE_TIME
|
||||
? t('advanceFilter.operator.le')
|
||||
: t(statusCodeOptions.find((item) => item.value === rows.condition)?.label || '-')
|
||||
}</span>`;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'apiTestDebug.expectedValue',
|
||||
dataIndex: 'expectedValue',
|
||||
slotName: 'expectedValue',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: 'apiTestDebug.status',
|
||||
dataIndex: 'pass',
|
||||
slotName: 'status',
|
||||
width: 100,
|
||||
format: (rows: ResponseAssertionTableItem) => {
|
||||
return rows.pass === true
|
||||
? `<span style="color:#0f0"> ${t('common.success')} </span>`
|
||||
: `<span style="color:#f00"> ${t('common.fail')} </span>`;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'apiTestDebug.reason',
|
||||
dataIndex: 'message',
|
||||
slotName: 'message',
|
||||
width: 300,
|
||||
},
|
||||
];
|
||||
return generateTableHTML(columns, assertions);
|
||||
}
|
||||
|
||||
// 获取功能用例自动填充内容
|
||||
export async function getCaseQuickContent(id: string) {
|
||||
try {
|
||||
const result = await getCaseDetail(id);
|
||||
const { prerequisite, steps, expectedResult } = result;
|
||||
|
||||
const stepData = getStepsTable(steps);
|
||||
// 处理步骤
|
||||
const caseContent = `
|
||||
<p style=""><strong>${t('system.orgTemplate.precondition')}</strong></p>
|
||||
<p style="">${prerequisite || '-'}</p>
|
||||
<p style=""><strong>${t('system.orgTemplate.stepDescription')}</strong></p>
|
||||
${stepData}
|
||||
<p style=""><strong>${t('system.orgTemplate.expectedResult')}</strong></p>
|
||||
<p style="">${expectedResult || '-'}</p>
|
||||
<p style=""><strong>${t('system.orgTemplate.actualResult')}</strong></p>
|
||||
<p style="">-</p>`;
|
||||
detailContentMap[CaseLinkEnum.FUNCTIONAL] = caseContent;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取接口用例快速填充内容
|
||||
export async function getApiQuickContent(lastReportId: string) {
|
||||
let assertTable = '-';
|
||||
let reportHref = '-';
|
||||
if (!lastReportId) {
|
||||
const apiContent = `
|
||||
<p style=""><strong>${t('apiTestDebug.assertion')}</strong></p>
|
||||
${assertTable}
|
||||
<p style="/"><strong>${t('testPlan.testPlanDetail.reportLink')}</strong></p>
|
||||
<p><span style="color:#2563eb" color="#2563eb">${reportHref}</span></p>`;
|
||||
detailContentMap[CaseLinkEnum.API] = apiContent;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const result = await getApiCaseReport(lastReportId);
|
||||
if (result) {
|
||||
const [steps] = result.children;
|
||||
const lastExecuteDetail = await getApiCaseReportStep(lastReportId, steps.stepId);
|
||||
const [stepContent] = lastExecuteDetail;
|
||||
|
||||
if (stepContent?.content?.responseResult?.assertions.length) {
|
||||
assertTable = getAssertTable(stepContent?.content?.responseResult?.assertions || []);
|
||||
}
|
||||
const { origin } = window.location;
|
||||
|
||||
reportHref = `${origin}/#/api-test/report?type=API_CASE&id=${lastReportId}`;
|
||||
|
||||
const apiContent = `
|
||||
<p style=""><strong>${t('apiTestDebug.assertion')}</strong></p>
|
||||
${assertTable}
|
||||
<p style="/"><strong>${t('testPlan.testPlanDetail.reportLink')}</strong></p>
|
||||
<p><span><a href="${reportHref}" link="${reportHref}">${reportHref}</a></span></p>`;
|
||||
detailContentMap[CaseLinkEnum.API] = apiContent;
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
// 获取快速填充场景内容
|
||||
async function getScenarioQuickContent(lastReportId: string) {
|
||||
const reportHref = lastReportId ? `${origin}/#/api-test/report?type=API_SCENARIO&id=${lastReportId}` : '-';
|
||||
const apiContent = `
|
||||
<p style=""><strong>${t('testPlan.testPlanDetail.reportLink')}</strong></p>
|
||||
<p><span><a href="${reportHref}" link="${reportHref}">${reportHref}</a></span></p>`;
|
||||
detailContentMap[CaseLinkEnum.SCENARIO] = apiContent;
|
||||
}
|
||||
|
||||
// 获取接口用例自动填充内容
|
||||
export async function getCaseTemplateContent(type: CaseLinkEnum, detailId: string): Promise<string | undefined> {
|
||||
switch (type) {
|
||||
case CaseLinkEnum.FUNCTIONAL:
|
||||
await getCaseQuickContent(detailId);
|
||||
return detailContentMap[CaseLinkEnum.FUNCTIONAL];
|
||||
case CaseLinkEnum.API:
|
||||
await getApiQuickContent(detailId);
|
||||
return detailContentMap[CaseLinkEnum.API];
|
||||
case CaseLinkEnum.SCENARIO:
|
||||
await getScenarioQuickContent(detailId);
|
||||
return detailContentMap[CaseLinkEnum.SCENARIO];
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export default {};
|
|
@ -45,6 +45,7 @@
|
|||
:resource-id="record.id"
|
||||
:bug-count="record.bugCount || 0"
|
||||
:existed-defect="existedDefect"
|
||||
:permission="['PROJECT_TEST_PLAN:READ+EXECUTE']"
|
||||
@load-list="refreshDetailAndList()"
|
||||
@associated="associateAndCreateDefect(true, false, record)"
|
||||
@create="associateAndCreateDefect(false, false, record)"
|
||||
|
@ -109,11 +110,16 @@
|
|||
:batch-move="batchMoveApiCase"
|
||||
@load-list="resetCaseList"
|
||||
/>
|
||||
<!-- TODO 等待联调快填 -->
|
||||
<AddDefectDrawer
|
||||
v-model:visible="showCreateBugDrawer"
|
||||
:extra-params="getApiBugParams"
|
||||
:is-batch="isBatchAssociateOrCreate"
|
||||
:case-type="CaseLinkEnum.API"
|
||||
:fill-config="{
|
||||
isQuickFillContent: !isBatchAssociateOrCreate,
|
||||
detailId: lastExecuteReportId,
|
||||
name: caseTitle,
|
||||
}"
|
||||
@success="refreshDetailAndList()"
|
||||
/>
|
||||
<LinkDefectDrawer
|
||||
|
@ -149,14 +155,14 @@
|
|||
import apiStatus from '@/views/api-test/components/apiStatus.vue';
|
||||
import CaseAndScenarioReportDrawer from '@/views/api-test/components/caseAndScenarioReportDrawer.vue';
|
||||
import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue';
|
||||
import AddDefectDrawer from '@/views/case-management/caseManagementFeature/components/tabContent/tabBug/addDefectDrawer.vue';
|
||||
import AddDefectDrawer from '@/views/case-management/components/addDefectDrawer/index.vue';
|
||||
import LinkDefectDrawer from '@/views/case-management/components/linkDefectDrawer.vue';
|
||||
import BatchApiMoveModal from '@/views/test-plan/testPlan/components/batchApiMoveModal.vue';
|
||||
|
||||
import {
|
||||
associateBugToApiCase,
|
||||
batchAssociatedBugToCase,
|
||||
batchDisassociateApiCase,
|
||||
batchLinkBugToApiCase,
|
||||
batchMoveApiCase,
|
||||
batchRunApiCase,
|
||||
disassociateApiCase,
|
||||
|
@ -286,7 +292,7 @@
|
|||
title: 'testPlan.featureCase.bugCount',
|
||||
dataIndex: 'bugCount',
|
||||
slotName: 'bugCount',
|
||||
width: 150,
|
||||
width: 100,
|
||||
showDrag: true,
|
||||
showInTable: true,
|
||||
},
|
||||
|
@ -558,6 +564,7 @@
|
|||
}
|
||||
|
||||
function refreshDetailAndList() {
|
||||
resetSelector();
|
||||
emit('refresh');
|
||||
loadCaseList();
|
||||
}
|
||||
|
@ -668,18 +675,22 @@
|
|||
}
|
||||
|
||||
const showLinkBugDrawer = ref(false);
|
||||
const associatedCaseId = ref<string>();
|
||||
const testPlanCaseId = ref<string>();
|
||||
const associatedCaseId = ref<string>('');
|
||||
const testPlanCaseId = ref<string>('');
|
||||
const lastExecuteReportId = ref<string>('');
|
||||
const existedDefect = inject<Ref<number>>('existedDefect', ref(0));
|
||||
const showCreateBugDrawer = ref<boolean>(false);
|
||||
const isBatchAssociateOrCreate = ref(false);
|
||||
const caseTitle = ref<string>('');
|
||||
// 关联缺陷
|
||||
function associateAndCreateDefect(isAssociate: boolean, isBatch: boolean, record?: PlanDetailApiCaseItem) {
|
||||
isBatchAssociateOrCreate.value = isBatch;
|
||||
if (record) {
|
||||
const { id, apiTestCaseId } = record;
|
||||
const { id, apiTestCaseId, lastExecReportId, name } = record;
|
||||
associatedCaseId.value = apiTestCaseId;
|
||||
lastExecuteReportId.value = lastExecReportId;
|
||||
testPlanCaseId.value = id;
|
||||
caseTitle.value = name;
|
||||
}
|
||||
if (isAssociate) {
|
||||
showLinkBugDrawer.value = true;
|
||||
|
@ -696,7 +707,7 @@
|
|||
drawerLoading.value = true;
|
||||
const tableParams = await getTableParams(true);
|
||||
if (isBatchAssociateOrCreate.value) {
|
||||
await batchAssociatedBugToCase({
|
||||
await batchLinkBugToApiCase({
|
||||
selectIds: tableSelected.value as string[],
|
||||
selectAll: batchParams.value.selectAll,
|
||||
excludeIds: batchParams.value?.excludeIds || [],
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
:resource-id="record.id"
|
||||
:bug-count="record.bugCount || 0"
|
||||
:existed-defect="existedDefect"
|
||||
:permission="['PROJECT_TEST_PLAN:READ+EXECUTE']"
|
||||
@load-list="refreshListAndDetail()"
|
||||
@associated="associateAndCreateDefect(true, false, record)"
|
||||
@create="associateAndCreateDefect(false, false, record)"
|
||||
|
@ -109,11 +110,16 @@
|
|||
@load-list="resetCaseList"
|
||||
/>
|
||||
|
||||
<!-- TODO 等待联调快填 -->
|
||||
<AddDefectDrawer
|
||||
v-model:visible="showCreateBugDrawer"
|
||||
:extra-params="getScenarioBugParams"
|
||||
:is-batch="isBatchAssociateOrCreate"
|
||||
:case-type="CaseLinkEnum.SCENARIO"
|
||||
:fill-config="{
|
||||
isQuickFillContent: !isBatchAssociateOrCreate,
|
||||
detailId: lastExecuteReportId,
|
||||
name: caseTitle,
|
||||
}"
|
||||
@success="refreshListAndDetail()"
|
||||
/>
|
||||
<LinkDefectDrawer
|
||||
|
@ -148,14 +154,14 @@
|
|||
import apiStatus from '@/views/api-test/components/apiStatus.vue';
|
||||
import CaseAndScenarioReportDrawer from '@/views/api-test/components/caseAndScenarioReportDrawer.vue';
|
||||
import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue';
|
||||
import AddDefectDrawer from '@/views/case-management/caseManagementFeature/components/tabContent/tabBug/addDefectDrawer.vue';
|
||||
import AddDefectDrawer from '@/views/case-management/components/addDefectDrawer/index.vue';
|
||||
import LinkDefectDrawer from '@/views/case-management/components/linkDefectDrawer.vue';
|
||||
import BatchApiMoveModal from '@/views/test-plan/testPlan/components/batchApiMoveModal.vue';
|
||||
|
||||
import {
|
||||
associateBugToScenarioCase,
|
||||
batchAssociatedBugToCase,
|
||||
batchDisassociateApiScenario,
|
||||
batchLinkBugToScenarioCase,
|
||||
batchMoveApiScenario,
|
||||
batchRunApiScenario,
|
||||
disassociateApiScenario,
|
||||
|
@ -314,7 +320,7 @@
|
|||
title: 'testPlan.featureCase.bugCount',
|
||||
dataIndex: 'bugCount',
|
||||
slotName: 'bugCount',
|
||||
width: 150,
|
||||
width: 100,
|
||||
showDrag: true,
|
||||
showInTable: true,
|
||||
},
|
||||
|
@ -500,6 +506,7 @@
|
|||
}
|
||||
|
||||
function refreshListAndDetail() {
|
||||
resetSelector();
|
||||
loadCaseList();
|
||||
emit('refresh');
|
||||
}
|
||||
|
@ -659,18 +666,22 @@
|
|||
const existedDefect = inject<Ref<number>>('existedDefect', ref(0));
|
||||
const isBatchAssociateOrCreate = ref(false);
|
||||
const showLinkBugDrawer = ref<boolean>(false);
|
||||
const associatedCaseId = ref<string>();
|
||||
const testPlanCaseId = ref<string>();
|
||||
const associatedCaseId = ref<string>('');
|
||||
const testPlanCaseId = ref<string>('');
|
||||
const showCreateBugDrawer = ref<boolean>(false);
|
||||
const lastExecuteReportId = ref<string>('');
|
||||
const caseTitle = ref<string>('');
|
||||
|
||||
const drawerLoading = ref<boolean>(false);
|
||||
// 关联缺陷
|
||||
function associateAndCreateDefect(isAssociate: boolean, isBatch: boolean, record?: PlanDetailApiScenarioItem) {
|
||||
isBatchAssociateOrCreate.value = isBatch;
|
||||
if (record) {
|
||||
const { id, apiScenarioId } = record;
|
||||
const { id, apiScenarioId, lastExecReportId, name } = record;
|
||||
associatedCaseId.value = apiScenarioId;
|
||||
testPlanCaseId.value = id;
|
||||
lastExecuteReportId.value = lastExecReportId;
|
||||
caseTitle.value = name;
|
||||
}
|
||||
if (isAssociate) {
|
||||
showLinkBugDrawer.value = true;
|
||||
|
@ -685,7 +696,7 @@
|
|||
drawerLoading.value = true;
|
||||
const tableParams = await getTableParams(true);
|
||||
if (isBatchAssociateOrCreate.value) {
|
||||
await batchAssociatedBugToCase({
|
||||
await batchLinkBugToScenarioCase({
|
||||
selectIds: tableSelected.value as string[],
|
||||
selectAll: batchParams.value.selectAll,
|
||||
excludeIds: batchParams.value?.excludeIds || [],
|
||||
|
|
|
@ -86,6 +86,7 @@
|
|||
:resource-id="record.id"
|
||||
:bug-count="record.bugCount || 0"
|
||||
:existed-defect="existedDefect"
|
||||
:permission="['PROJECT_TEST_PLAN:READ+EXECUTE']"
|
||||
@load-list="refreshList()"
|
||||
@associated="associateAndCreateDefect(true, false, record)"
|
||||
@create="associateAndCreateDefect(false, false, record)"
|
||||
|
@ -174,12 +175,16 @@
|
|||
:batch-move="batchMoveFeatureCase"
|
||||
@load-list="resetCaseList"
|
||||
/>
|
||||
<!-- TODO 等待联调快填 -->
|
||||
|
||||
<AddDefectDrawer
|
||||
v-model:visible="showCreateBugDrawer"
|
||||
:extra-params="getBugParams"
|
||||
:is-batch="isBatchAssociateOrCreate"
|
||||
:case-type="CaseLinkEnum.FUNCTIONAL"
|
||||
:fill-config="{
|
||||
isQuickFillContent: !isBatchAssociateOrCreate,
|
||||
detailId: testPlanCaseId,
|
||||
name: caseTitle,
|
||||
}"
|
||||
@success="refreshList()"
|
||||
/>
|
||||
<LinkDefectDrawer
|
||||
|
@ -217,7 +222,7 @@
|
|||
import ExecuteResult from '@/components/business/ms-case-associate/executeResult.vue';
|
||||
import { getMinderOperationParams } from '@/components/business/ms-minders/caseReviewMinder/utils';
|
||||
import MsTestPlanFeatureCaseMinder from '@/components/business/ms-minders/testPlanFeatureCaseMinder/index.vue';
|
||||
import AddDefectDrawer from '@/views/case-management/caseManagementFeature/components/tabContent/tabBug/addDefectDrawer.vue';
|
||||
import AddDefectDrawer from '@/views/case-management/components/addDefectDrawer/index.vue';
|
||||
import LinkDefectDrawer from '@/views/case-management/components/linkDefectDrawer.vue';
|
||||
import BatchApiMoveModal from '@/views/test-plan/testPlan/components/batchApiMoveModal.vue';
|
||||
import BatchUpdateExecutorModal from '@/views/test-plan/testPlan/components/batchUpdateExecutorModal.vue';
|
||||
|
@ -803,8 +808,8 @@
|
|||
}
|
||||
|
||||
const showLinkBugDrawer = ref<boolean>(false);
|
||||
const associatedCaseId = ref<string>();
|
||||
const testPlanCaseId = ref<string>();
|
||||
const associatedCaseId = ref<string>('');
|
||||
const testPlanCaseId = ref<string>('');
|
||||
const drawerLoading = ref<boolean>(false);
|
||||
const isBatchAssociateOrCreate = ref(false);
|
||||
|
||||
|
@ -865,14 +870,16 @@
|
|||
}
|
||||
|
||||
const showCreateBugDrawer = ref<boolean>(false);
|
||||
const caseTitle = ref<string>('');
|
||||
|
||||
// 关联/关联缺陷
|
||||
function associateAndCreateDefect(isAssociate: boolean, isBatch: boolean, record?: PlanDetailFeatureCaseItem) {
|
||||
isBatchAssociateOrCreate.value = isBatch;
|
||||
if (record) {
|
||||
const { id, caseId } = record;
|
||||
const { id, caseId, name } = record;
|
||||
associatedCaseId.value = caseId;
|
||||
testPlanCaseId.value = id;
|
||||
caseTitle.value = name;
|
||||
}
|
||||
if (isAssociate) {
|
||||
showLinkBugDrawer.value = true;
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
id?: string;
|
||||
selectNode?: MinderJsonNode;
|
||||
stepExecutionResult?: StepExecutionResult[];
|
||||
isDefaultActivate?: boolean; // 是否默认激活状态
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
@ -102,7 +103,7 @@
|
|||
}
|
||||
);
|
||||
|
||||
const achievedForm = ref<boolean>(false);
|
||||
const achievedForm = ref<boolean>(props.isDefaultActivate);
|
||||
|
||||
function cancel(e: Event) {
|
||||
// 点击取消/关闭,弹窗关闭,富文本内容都清空;点击空白处,弹窗关闭,将弹窗内容填入下面富文本内容里
|
||||
|
|
|
@ -255,9 +255,9 @@
|
|||
import MsStatusTag from '@/components/business/ms-status-tag/index.vue';
|
||||
import BugList from './bug/index.vue';
|
||||
import ExecuteSubmit from './executeSubmit.vue';
|
||||
import AddDefectDrawer from '@/views/case-management/caseManagementFeature/components/tabContent/tabBug/addDefectDrawer.vue';
|
||||
import CaseTabDetail from '@/views/case-management/caseManagementFeature/components/tabContent/tabDetail.vue';
|
||||
import EditCaseDetailDrawer from '@/views/case-management/caseReview/components/editCaseDetailDrawer.vue';
|
||||
import AddDefectDrawer from '@/views/case-management/components/addDefectDrawer/index.vue';
|
||||
import LinkDefectDrawer from '@/views/case-management/components/linkDefectDrawer.vue';
|
||||
import ExecutionHistory from '@/views/test-plan/testPlan/detail/featureCase/detail/executionHistory/index.vue';
|
||||
|
||||
|
|
|
@ -97,6 +97,7 @@ export default {
|
|||
'testPlan.testPlanDetail.executed': 'Executed',
|
||||
'testPlan.testPlanDetail.generateReport': 'Generate report',
|
||||
'testPlan.testPlanDetail.successfullyGenerated': 'Successfully generated',
|
||||
'testPlan.testPlanDetail.reportLink': 'The report link',
|
||||
'testPlan.bugManagement.bug': 'Defect list',
|
||||
'testPlan.bugManagement.bugName': 'name',
|
||||
'testPlan.bugManagement.defectState': 'Defect state',
|
||||
|
|
|
@ -93,6 +93,7 @@ export default {
|
|||
'testPlan.testPlanDetail.executed': '已执行',
|
||||
'testPlan.testPlanDetail.generateReport': '生成报告',
|
||||
'testPlan.testPlanDetail.successfullyGenerated': '生成成功',
|
||||
'testPlan.testPlanDetail.reportLink': '报告链接',
|
||||
'testPlan.bugManagement.bug': '缺陷列表',
|
||||
'testPlan.bugManagement.bugName': '名称',
|
||||
'testPlan.bugManagement.defectState': '缺陷状态',
|
||||
|
|
Loading…
Reference in New Issue