测试计划详情

This commit is contained in:
chenjianxing 2020-04-02 14:30:05 +08:00
parent 192b4fe457
commit c6e384cdbd
10 changed files with 507 additions and 9 deletions

View File

@ -0,0 +1,14 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.base.domain.TestCase;
import io.metersphere.controller.request.ReportRequest;
import io.metersphere.controller.request.testcase.QueryTestCaseRequest;
import io.metersphere.dto.ReportDTO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtTestCaseMapper {
List<TestCase> getTestCaseNames(@Param("request") QueryTestCaseRequest request);
}

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="io.metersphere.base.mapper.ext.ExtTestCaseMapper">
<select id="getTestCaseNames" resultType="io.metersphere.base.domain.TestCase">
select test_case.id, test_case.name
from test_case
<where>
<if test="request.projectId != null">
AND test_case.project_id = #{request.projectId}
</if>
<if test="request.nodeIds != null and request.nodeIds.size() > 0">
AND test_case.node_id IN
<foreach collection="request.nodeIds" open="(" close=")" separator="," item="nodeId">
#{nodeId}
</foreach>
</if>
</where>
ORDER BY test_case.update_time DESC
</select>
</mapper>

View File

@ -38,6 +38,11 @@ public class TestCaseController {
return testCaseService.getTestCaseByNodeId(nodeIds);
}
@PostMapping("/name/all")
public List<TestCase> getTestCaseNames(@RequestBody QueryTestCaseRequest request){
return testCaseService.getTestCaseNames(request);
}
@PostMapping("/get/{testCaseId}")
public List<TestCaseWithBLOBs> getTestCase(@PathVariable String testCaseId){
return testCaseService.getTestCase(testCaseId);

View File

@ -8,6 +8,16 @@ public class QueryTestCaseRequest extends TestCase {
List<Integer> nodeIds;
String planId;
public String getPlanId() {
return planId;
}
public void setPlanId(String planId) {
this.planId = planId;
}
public List<Integer> getNodeIds() {
return nodeIds;
}

View File

@ -4,7 +4,10 @@ package io.metersphere.service;
import io.metersphere.base.domain.TestCase;
import io.metersphere.base.domain.TestCaseExample;
import io.metersphere.base.domain.TestCaseWithBLOBs;
import io.metersphere.base.domain.TestPlan;
import io.metersphere.base.mapper.TestCaseMapper;
import io.metersphere.base.mapper.TestPlanMapper;
import io.metersphere.base.mapper.ext.ExtTestCaseMapper;
import io.metersphere.controller.request.testcase.QueryTestCaseRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
@ -21,6 +24,12 @@ public class TestCaseService {
@Resource
TestCaseMapper testCaseMapper;
@Resource
ExtTestCaseMapper extTestCaseMapper;
@Resource
TestPlanMapper testPlanMapper;
public void addTestCase(TestCaseWithBLOBs testCase) {
testCase.setId(UUID.randomUUID().toString());
testCase.setCreateTime(System.currentTimeMillis());
@ -63,4 +72,13 @@ public class TestCaseService {
}
return testCaseMapper.selectByExampleWithBLOBs(testCaseExample);
}
public List<TestCase> getTestCaseNames(QueryTestCaseRequest request) {
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(request.getPlanId());
request.setProjectId(testPlan.getProjectId());
return extTestCaseMapper.getTestCaseNames(request);
}
}

View File

@ -5,7 +5,7 @@
<el-menu :unique-opened="true" mode="horizontal" active-text-color="write"
class="project_menu">
<el-submenu index="1" popper-class="submenu" v-permission="['test_user', 'test_viewer']">
<el-submenu index="1" popper-class="submenu">
<template slot="title">
{{currentProject == null ? '' : currentProject.name}}
</template>
@ -78,8 +78,6 @@
},
watch: {
'$route'(to, from) {
console.log(from);
console.log(to);
if (from.name.indexOf("Project") > 0){
this.getProjects();
}

View File

@ -7,11 +7,20 @@
:plan-id="planId"
class="node_tree"
@nodeSelectEvent="get"
@refresh="refresh"
ref="nodeTree"></plan-node-tree>
</el-aside>
<el-main>
<test-case-plan-list
@openTestCaseRelevanceDialog="openTestCaseRelevanceDialog"
ref="testCasePlanList"></test-case-plan-list>
</el-main>
</el-container>
<test-case-relevance
@refresh="getCaseByNodeIds"
ref="testCaseRelevance"></test-case-relevance>
</div>
</template>
@ -19,10 +28,12 @@
<script>
import PlanNodeTree from "./components/PlanNodeTree";
import TestCasePlanList from "./components/TestCasePlanList";
import TestCaseRelevance from "./components/TestCaseRelevance";
export default {
name: "TestPlanView",
components: {PlanNodeTree},
components: {PlanNodeTree, TestCasePlanList, TestCaseRelevance},
data() {
return {
currentProject: {}
@ -39,6 +50,13 @@
},
get() {
},
openTestCaseRelevanceDialog(data) {
this.$refs.testCaseRelevance.dialogFormVisible = true;
this.$refs.testCaseRelevance.getCaseNames(this.planId);
},
getCaseByNodeIds() {
}
}
}

View File

@ -12,12 +12,10 @@
:expand-on-click-node="false"
draggable
ref="tree">
<span class="custom-tree-node father" slot-scope="{node}" @click="selectNode(node)">
{{node.label}}
</span>
</el-tree>
</div>
@ -56,8 +54,6 @@
}
},
created() {
console.log("created");
console.log(this.planId);
this.getNodeTree();
},
methods: {

View File

@ -0,0 +1,215 @@
<template>
<el-main>
<el-card>
<div slot="header">
<el-row type="flex" justify="space-between" align="middle">
<el-col :span="5">
<span class="title">{{$t('test_track.test_case')}}</span>
</el-col>
<el-col :span="1" :offset="8">
<el-button icon="el-icon-circle-plus-outline" size="small" round
@click="$emit('openTestCaseRelevanceDialog')" >{{$t('commons.create')}}</el-button>
</el-col>
<el-col :span="1" >
<el-button
icon="el-icon-refresh" size="small" round
@click="initTableData()">{{$t('commons.refresh')}}</el-button>
</el-col>
<el-col :span="5">
<span class="search">
<el-input type="text" size="small" :placeholder="$t('load_test.search_by_name')"
prefix-icon="el-icon-search"
maxlength="60"
v-model="condition" @change="search" clearable/>
</span>
</el-col>
</el-row>
</div>
<el-table
:data="tableData"
class="test-content">
<el-table-column
prop="name"
:label="$t('commons.name')"
width="120"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="priority"
:label="$t('test_track.priority')"
width="120"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="type"
:label="$t('test_track.type')"
width="120"
show-overflow-tooltip>
<template slot-scope="scope">
<span v-if="scope.row.type == 'functional'">{{$t('commons.functional')}}</span>
<span v-if="scope.row.type == 'performance'">{{$t('commons.performance')}}</span>
<span v-if="scope.row.type == 'interface'">{{$t('commons.interface')}}</span>
</template>
</el-table-column>
<el-table-column
prop="method"
:label="$t('test_track.method')"
width="120"
show-overflow-tooltip>
<template slot-scope="scope">
<span v-if="scope.row.method == 'manual'">{{$t('test_track.manual')}}</span>
<span v-if="scope.row.method == 'auto'">{{$t('test_track.auto')}}</span>
</template>
</el-table-column>
<el-table-column
prop="nodePath"
:label="$t('test_track.module')"
width="160"
show-overflow-tooltip>
</el-table-column>
<el-table-column
width="160"
:label="$t('commons.create_time')">
<template slot-scope="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column
width="160"
:label="$t('commons.update_time')">
<template slot-scope="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column
width="100"
:label="$t('commons.operating')">
<template slot-scope="scope">
<el-button @click="handleEdit(scope.row)" type="primary" icon="el-icon-edit" size="mini" circle/>
<el-button @click="handleDelete(scope.row)" type="danger" icon="el-icon-delete" size="mini" circle/>
</template>
</el-table-column>
</el-table>
<div>
<el-row>
<el-col :span="22" :offset="1">
<div class="table-page">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page.sync="currentPage"
:page-sizes="[5, 10, 20, 50, 100]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</el-col>
</el-row>
</div>
</el-card>
</el-main>
</template>
<script>
import {CURRENT_PROJECT} from '../../../../../common/constants';
import PlanNodeTree from './PlanNodeTree';
export default {
name: "TestCaseList",
components: {PlanNodeTree},
data() {
return {
result: {},
deletePath: "/test/case/delete",
condition: "",
tableData: [],
multipleSelection: [],
currentPage: 1,
pageSize: 5,
total: 0,
loadingRequire: {project: true, testCase: true},
testId: null
}
},
created: function () {
this.initTableData();
},
methods: {
initTableData(nodeIds) {
let param = {
name: this.condition,
};
param.nodeIds = nodeIds;
if(localStorage.getItem(CURRENT_PROJECT)) {
param.projectId = JSON.parse(localStorage.getItem(CURRENT_PROJECT)).id;
}
this.$post(this.buildPagePath('/test/case/list'), param, response => {
this.loadingRequire.testCase = false;
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
});
},
search() {
this.initTableData();
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
handleSizeChange(size) {
this.pageSize = size;
this.initTableData();
},
handleCurrentChange(current) {
this.currentPage = current;
this.initTableData();
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
handleEdit(testCase) {
this.$emit('testCaseRelevance', testCase);
},
handleDelete(testCase) {
this.$alert(this.$t('load_test.delete_confirm') + testCase.name + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this._handleDelete(testCase);
}
}
});
},
_handleDelete(testCase) {
let testCaseId = testCase.id;
this.$post('/test/case/delete/' + testCaseId, {}, () => {
this.initTableData();
this.$message({
message: this.$t('commons.delete_success'),
type: 'success'
});
});
}
}
}
</script>
<style scoped>
.table-page {
padding-top: 20px;
margin-right: -9px;
float: right;
}
</style>

View File

@ -0,0 +1,201 @@
<template>
<div>
<el-dialog :title="$t('test_track.create')"
:visible.sync="dialogFormVisible"
width="65%">
<el-container style="min-height: 350px">
<el-aside class="node_tree" width="200px" style="background-color: rgb(238, 241, 246)">
<plan-node-tree></plan-node-tree>
</el-aside>
<el-container>
<el-header >
<el-checkbox ></el-checkbox>
</el-header>
<el-main style="height: 100px;">
<el-scrollbar style="height:100%">
<el-table
:data="testCases">
<el-table-column
prop="name"
style="width: 100%">
<template slot="header">
<el-checkbox v-model="checkAll"></el-checkbox>
用例名称
</template>
<template slot-scope="scope">
<el-checkbox v-model="scope.row.checked"></el-checkbox>
{{scope.row.name}}
</template>
</el-table-column>
</el-table>
</el-scrollbar>
</el-main>
</el-container>
</el-container>
<div slot="footer" class="dialog-footer">
<el-button
@click="dialogFormVisible = false">
{{$t('test_track.cancel')}}
</el-button>
<el-button
type="primary"
@click="saveCase">
{{$t('test_track.confirm')}}
</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {CURRENT_PROJECT} from '../../../../../common/constants';
import PlanNodeTree from './PlanNodeTree';
export default {
name: "TestCaseEdit",
components: {PlanNodeTree},
data() {
return {
dialogFormVisible: false,
count: 6,
checkAll: false,
testCases: [],
form: {
name: '',
}
};
},
methods: {
openTestCaseEditDialog(testCase) {
this.resetForm();
this.operationType = 'add';
if(testCase){
//
this.operationType = 'edit';
let tmp = {};
Object.assign(tmp, testCase);
tmp.steps = JSON.parse(testCase.steps);
Object.assign(this.form, tmp);
this.form.module = testCase.nodeId;
}
this.dialogFormVisible = true;
},
handleAddStep(index, data) {
let step = {};
step.num = data.num + 1;
step.desc = null;
step.result = null;
this.form.steps.forEach(step => {
if(step.num > data.num){
step.num ++;
}
});
this.form.steps.push(step);
},
handleDeleteStep(index, data) {
this.form.steps.splice(index, 1);
this.form.steps.forEach(step => {
if(step.num > data.num){
step.num --;
}
});
},
saveCase(){
this.$refs['relevanceFrom'].validate((valid) => {
if (valid) {
let param = {};
Object.assign(param, this.form);
param.steps = JSON.stringify(this.form.steps);
param.nodeId = this.form.module;
this.moduleOptions.forEach(item => {
if(this.form.module === item.id){
param.nodePath = item.path;
}
});
if(localStorage.getItem(CURRENT_PROJECT)) {
param.projectId = JSON.parse(localStorage.getItem(CURRENT_PROJECT)).id;
}
this.$post('/test/case/' + this.operationType, param, () => {
this.$message.success(this.$t('commons.save_success'));
this.dialogFormVisible = false;
this.$emit("refresh");
});
} else {
return false;
}
});
}
,
resetForm() {
if (this.$refs['relevanceFrom']) {
this.$refs['relevanceFrom'].resetFields();
}
},
load () {
this.count += 2
},
getCaseNames(planId) {
if(planId){
let param = {};
param.planId = planId;
this.$post('/test/case/name/all', param, response => {
this.testCases = response.data;
this.testCases.forEach(item => {
item.checked = false;
});
});
}
}
}
}
</script>
<style scoped>
.tb-edit .el-input {
display: none;
color: black;
}
.tb-edit .current-row .el-input {
display: block;
}
.tb-edit .current-row .el-input+span {
display: none;
}
.node_tree{
/*border-radius: 1px;*/
/*padding-top: 5px ;*/
/*height: 100%;*/
/*box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);*/
}
.el-header {
background-color: darkgrey;
color: #333;
line-height: 60px;
height: 1%;
}
.el-aside {
color: #333;
}
</style>