feat(测试计划): 测试计划详情用例批量添加缺陷&关联缺陷联调&填充缺陷内容

This commit is contained in:
xinxin.wu 2024-09-03 16:43:07 +08:00 committed by Craftsman
parent d20023989e
commit 2836631b29
22 changed files with 459 additions and 49 deletions

View File

@ -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 });
}

View File

@ -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';

View File

@ -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<{

View File

@ -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;

View File

@ -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')

View File

@ -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';

View File

@ -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">

View File

@ -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 {};

View File

@ -419,6 +419,7 @@ export interface PlanMinderNodeData extends MinderJsonNodeData {
retryTimes: number;
retryInterval: number;
stopOnFail: boolean;
tempCollectionNode?: boolean; // 关联用例到未保存的临时测试集节点标识
}
export interface PlanMinderNode extends MinderJsonNode {
data: PlanMinderNodeData;

View File

@ -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>

View File

@ -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);

View File

@ -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>

View File

@ -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';

View File

@ -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 idid
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'));

View File

@ -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 {};

View File

@ -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 || [],

View File

@ -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 || [],

View File

@ -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;

View File

@ -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) {
// /

View File

@ -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';

View File

@ -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',

View File

@ -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': '缺陷状态',