Merge branch 'pr_38' into Develop_Branch

# Conflicts:
#	src/main/resources/static/ruoyi/js/common.js
This commit is contained in:
seagull 2022-01-27 09:41:32 +08:00
commit 6d0dff1e5d
20 changed files with 870 additions and 68 deletions

View File

@ -32,7 +32,7 @@
<commons.io.version>2.5</commons.io.version>
<commons.fileupload.version>1.3.3</commons.fileupload.version>
<bitwalker.version>1.19</bitwalker.version>
<lombok.version>1.16.18</lombok.version>
<!-- <lombok.version>1.16.18</lombok.version>-->
<velocity.version>1.7</velocity.version>
<kaptcha.version>2.3.2</kaptcha.version>
<swagger.version>2.7.0</swagger.version>
@ -286,7 +286,11 @@
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
</dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<!--排除版本管理警告-->
<dependencyManagement>
<dependencies>

View File

@ -17,8 +17,11 @@ public class WebDebugCaseEntity implements Serializable {
*/
private static final long serialVersionUID = 1L;
private Integer caseId;
private Integer userId;
private String loadpath;
private Integer userId;
private String loadpath;
//修改点
private Integer caseType;
private Integer browserType;
public Integer getCaseId() {
return caseId;
@ -39,4 +42,15 @@ public class WebDebugCaseEntity implements Serializable {
this.loadpath = loadpath;
}
//修改点
public Integer getCaseType(){return caseType; }
public void setCaseType(Integer caseType) {
this.caseType = caseType;
}
public Integer getBrowserType() {
return browserType;
}
public void setBrowserType(Integer browserType) {
this.browserType = browserType;
}
}

View File

@ -297,7 +297,7 @@ public class OpenPostApiController
tce.setCaseId(projectCase.getCaseId());
TaskCaseExecute taskCaseExecute = taskCaseExecuteService.selectTaskCaseExecuteByTaskIdAndCaseId(tce);
List<TaskCaseLog> loglist = taskCaseLogService.selectTaskCaseLogListByTaskCaseId(taskCaseExecute.getTaskCaseId());
List<TaskCaseLog> loglist = taskCaseLogService.selectTaskCaseLogListByTaskCaseId(taskCaseExecute.getTaskCaseId(),null);
for(TaskCaseLog tcl:loglist){
if(tcl.getLogDetail().contains("测试结果:")){
result = tcl.getLogDetail();

View File

@ -54,13 +54,15 @@ public class TaskCaseLogController extends BaseController
response.setCharacterEncoding("utf-8");
PrintWriter pw = response.getWriter();
String taskCaseIdStr = request.getParameter("taskCaseId");
String errorInfo = request.getParameter("errorInfo");
int taskCaseId = 0;
// 得到客户端传递的查询参数
if (StringUtils.isNotEmpty(taskCaseIdStr)) {
taskCaseId = Integer.parseInt(taskCaseIdStr);
}
List<TaskCaseLog> loglist = taskCaseLogService.selectTaskCaseLogListByTaskCaseId(taskCaseId);
List<TaskCaseLog> loglist = taskCaseLogService.selectTaskCaseLogListByTaskCaseId(taskCaseId,errorInfo);
// 转换成json字符串
JSONArray recordJson= JSONArray.parseArray(JSON.toJSONString(loglist));
pw.print(recordJson);

View File

@ -35,7 +35,7 @@ public interface ITaskCaseLogService
* @author Seagull
* @date 2019年4月11日
*/
List<TaskCaseLog> selectTaskCaseLogListByTaskCaseId(Integer taskCaseId);
List<TaskCaseLog> selectTaskCaseLogListByTaskCaseId(Integer taskCaseId,String errorInfo);
/**
* 新增用例日志明细

View File

@ -2,6 +2,7 @@ package com.luckyframe.project.testexecution.taskCaseLog.service;
import java.util.List;
import com.luckyframe.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -53,10 +54,13 @@ public class TaskCaseLogServiceImpl implements ITaskCaseLogService
* @date 2019年4月11日
*/
@Override
public List<TaskCaseLog> selectTaskCaseLogListByTaskCaseId(Integer taskCaseId)
public List<TaskCaseLog> selectTaskCaseLogListByTaskCaseId(Integer taskCaseId,String errorInfo)
{
TaskCaseLog taskCaseLog = new TaskCaseLog();
taskCaseLog.setTaskCaseId(taskCaseId);
if(StringUtils.isNotEmpty(errorInfo)) {
taskCaseLog.setLogGrade(errorInfo);
}
return taskCaseLogMapper.selectTaskCaseLogList(taskCaseLog);
}

View File

@ -2,6 +2,7 @@ package com.luckyframe.project.testmanagmt.projectCase.controller;
import java.util.List;
import cn.hutool.json.JSONUtil;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@ -62,6 +63,8 @@ public class ProjectCaseDebugController extends BaseController
ProjectCaseDebug projectCaseDebug=new ProjectCaseDebug();
projectCaseDebug.setCaseId(caseId);
projectCaseDebug.setUserId(ShiroUtils.getUserId().intValue());
//修改点
projectCaseDebug.setCaseType(projectCase.getCaseType());
List<Client> clients=clientService.selectClientsByProjectId(projectCase.getProjectId());
if(clients.size()>0){
List<String> driverPathList=clientService.selectClientDriverListById(clients.get(0).getClientId());
@ -95,6 +98,9 @@ public class ProjectCaseDebugController extends BaseController
}
webDebugCaseEntity.setCaseId(projectCaseDebug.getCaseId());
webDebugCaseEntity.setUserId(ShiroUtils.getUserId().intValue());
//修改点
webDebugCaseEntity.setCaseType(projectCaseDebug.getCaseType());
webDebugCaseEntity.setBrowserType(projectCaseDebug.getBrowserType());
Client client = clientService.selectClientById(projectCaseDebug.getClientId());
String url= "http://"+client.getClientIp()+":"+ClientConstants.CLIENT_MONITOR_PORT+"/webDebugCase";
String result=HttpRequest.httpClientPost(url, client,JSONObject.toJSONString(webDebugCaseEntity),3000);

View File

@ -7,17 +7,13 @@ import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.luckyframe.common.utils.poi.ExcelUtil;
import com.luckyframe.project.testmanagmt.projectCaseModule.domain.ProjectCaseModule;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
@ -162,4 +158,36 @@ public class ProjectCaseStepsController extends BaseController
return toAjax(projectCaseStepsService.updateProjectCaseSteps(projectCaseSteps));
}
/**
* @author lifengyang
* 用例步骤导出
*/
@RequiresPermissions("testmanagmt:projectCase:exportcasestep")
@PostMapping("/export")
@ResponseBody
public AjaxResult export(@RequestParam("caseId") Integer caseId)
{
ProjectCase projectCase=projectCaseService.selectProjectCaseById(caseId);
ProjectCaseSteps projectCaseSteps = new ProjectCaseSteps();
projectCaseSteps.setCaseId(caseId);
List<ProjectCaseSteps> stepsList=projectCaseStepsService.selectProjectCaseStepsList(projectCaseSteps);
if(stepsList.size()==0){
projectCaseSteps.setAction("");
projectCaseSteps.setExpectedResult("");
projectCaseSteps.setExtend("");
projectCaseSteps.setProjectId(projectCase.getProjectId());
projectCaseSteps.setStepId(0);
projectCaseSteps.setStepOperation("");
projectCaseSteps.setStepParameters("");
projectCaseSteps.setStepPath("");
projectCaseSteps.setStepSerialNumber(1);
projectCaseSteps.setStepType(projectCase.getCaseType());
stepsList.add(projectCaseSteps);
}
ExcelUtil<ProjectCaseSteps> util = new ExcelUtil<>(ProjectCaseSteps.class);
return util.exportExcel(stepsList, "测试用例步骤");
}
}

View File

@ -0,0 +1,123 @@
package com.luckyframe.project.testmanagmt.projectCase.controller;
import com.luckyframe.common.utils.poi.ExcelUtil;
import com.luckyframe.project.testmanagmt.projectCase.domain.ProjectCaseSteps;
import com.luckyframe.project.testmanagmt.projectCase.service.IProjectCaseService;
import com.luckyframe.project.testmanagmt.projectCase.service.IProjectCaseStepsService;
import com.luckyframe.project.testmanagmt.projectCaseModule.domain.ProjectCaseModule;
import com.luckyframe.rc.ReadTxt;
import com.luckyframe.rc.Readfile;
import com.luckyframe.rc.entity.ElementAction;
import com.luckyframe.rc.entity.RcWebCaseSteps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
/**
* @Author lifengyang
* @Date 2021-11-05
* @Version 2.0
*/
@Slf4j
@RestController
@RequestMapping("/testmanagmt/RecordController")
public class RecordController {
@Autowired
private IProjectCaseStepsService projectCaseStepsService;
/**
* 脚本录入插入数据
* StepType默认类型 0 HTTP接口 1 Web UI 2 API驱动 3移动端
*/
@PostMapping(value = "/insert",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity insertRecord(@RequestParam("file") MultipartFile file,@RequestParam("projectId") int ProjectId,@RequestParam("caseId") int CaseId,@RequestParam("stepType") int StepType) throws Exception {
log.info("StepType:"+StepType);
if (file.getOriginalFilename().toLowerCase().endsWith(".xlsx")){
ExcelUtil<ProjectCaseSteps> util = new ExcelUtil<>(ProjectCaseSteps.class);
List<ProjectCaseSteps> caseStepsList = util.importExcel(file.getInputStream());
int stepSerialNumber=1;
log.info("开始插入步骤");
for(ProjectCaseSteps projectCaseSteps:caseStepsList){
projectCaseSteps.setProjectId(ProjectId);
projectCaseSteps.setCaseId(CaseId);
projectCaseSteps.setStepSerialNumber(stepSerialNumber);
projectCaseSteps.setCreateTime(new Date());
log.info(""+stepSerialNumber+"步骤为:\n"+projectCaseSteps);
projectCaseStepsService.insertProjectCaseSteps(projectCaseSteps);
stepSerialNumber++;
}
}else {
if (StepType==3){
//解析app自动化测试录制的脚本
ReadTxt readTxt = new ReadTxt();
List<ElementAction> stepList = readTxt.Extract(file);
for (ElementAction elementAction : stepList) {
log.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"+ elementAction);
}
List<ProjectCaseSteps> listSteps = new ArrayList<>();
for (ElementAction elementAction : stepList) {
ProjectCaseSteps projectCaseSteps = new ProjectCaseSteps();
projectCaseSteps.setCaseId(CaseId);
projectCaseSteps.setProjectId(ProjectId);
projectCaseSteps.setStepPath(elementAction.getAccess());
projectCaseSteps.setStepParameters(elementAction.getActionValue());
projectCaseSteps.setStepOperation(elementAction.getAction());
projectCaseSteps.setStepType(StepType);
projectCaseSteps.setCreateBy("admin");
projectCaseSteps.setCreateTime(new Date());
listSteps.add(projectCaseSteps);
}
int stepSerialNumber=1;
for(ProjectCaseSteps projectCaseSteps:listSteps){
projectCaseSteps.setStepSerialNumber(stepSerialNumber);
log.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~2"+projectCaseSteps);
projectCaseStepsService.insertProjectCaseSteps(projectCaseSteps);
stepSerialNumber++;
}
}else if(StepType==1){
//解析web自动化测试录制的脚本
Readfile Readfile = new Readfile();
LinkedList<RcWebCaseSteps> stepList = Readfile.saveAction(file);
for (RcWebCaseSteps rcWebCaseSteps : stepList) {
log.info("设置包|定位路径:"+ rcWebCaseSteps.getStepPath());
}
List<ProjectCaseSteps> listSteps = new ArrayList<>();
for (RcWebCaseSteps rcWebCaseSteps : stepList) {
ProjectCaseSteps projectCaseSteps = new ProjectCaseSteps();
projectCaseSteps.setCaseId(CaseId);
projectCaseSteps.setProjectId(ProjectId);
projectCaseSteps.setStepPath(rcWebCaseSteps.getStepPath());
projectCaseSteps.setStepParameters(rcWebCaseSteps.getStepParameters());
projectCaseSteps.setStepOperation(rcWebCaseSteps.getStepOperation());
projectCaseSteps.setStepType(StepType);
projectCaseSteps.setCreateBy("admin");
projectCaseSteps.setCreateTime(new Date());
listSteps.add(projectCaseSteps);
}
int stepSerialNumber=1;
log.info("开始插入步骤");
for(ProjectCaseSteps projectCaseSteps:listSteps){
projectCaseSteps.setStepSerialNumber(stepSerialNumber);
log.info(""+stepSerialNumber+"步骤为:\n"+projectCaseSteps);
projectCaseStepsService.insertProjectCaseSteps(projectCaseSteps);
stepSerialNumber++;
}
}
}
return ResponseEntity.ok().build();
}
}

View File

@ -1,5 +1,6 @@
package com.luckyframe.project.testmanagmt.projectCase.domain;
import com.luckyframe.framework.aspectj.lang.annotation.Excel;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.luckyframe.framework.web.domain.BaseEntity;
@ -30,6 +31,28 @@ public class ProjectCaseDebug extends BaseEntity
private Integer clientId;
/** 客户端驱动路径 */
private String driverPath;
//修改点
/** 默认类型 0 HTTP接口 1 Web UI 2 API驱动 3移动端 */
private Integer caseType;
/** web UI自动化浏览器类型 0 IE 1 火狐 2 谷歌 3 Edge */
private Integer browserType;
public void setCaseType(Integer caseType) {
this.caseType = caseType;
}
public Integer getCaseType() {
return caseType;
}
public Integer getBrowserType() {
return browserType;
}
public void setBrowserType(Integer browserType) {
this.browserType = browserType;
}
public Integer getClientId() {
return clientId;
@ -102,14 +125,15 @@ public class ProjectCaseDebug extends BaseEntity
return logDetail;
}
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("debugId", getDebugId())
.append("caseId", getCaseId())
.append("userId", getUserId())
.append("debugIsend", getDebugIsend())
.append("logLevel", getLogLevel())
.append("logDetail", getLogDetail())
.toString();
}
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("debugId", getDebugId())
.append("caseId", getCaseId())
.append("userId", getUserId())
.append("caseType",getCaseType())
.append("debugIsend", getDebugIsend())
.append("logLevel", getLogLevel())
.append("logDetail", getLogDetail())
.toString();
}
}

View File

@ -1,5 +1,6 @@
package com.luckyframe.project.testmanagmt.projectCase.domain;
import com.luckyframe.framework.aspectj.lang.annotation.Excel;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@ -18,26 +19,36 @@ public class ProjectCaseSteps extends BaseEntity
/** 步骤ID */
private Integer stepId;
/** 用例ID */
@Excel(name = "用例ID")
private Integer caseId;
/** 项目ID */
@Excel(name = "项目ID")
private Integer projectId;
/** 步骤序号 */
private Integer stepSerialNumber;
/** 包路径|定位路径 */
@Excel(name = "包路径|定位路径")
private String stepPath;
/** 方法名|操作 */
@Excel(name = "方法名|操作")
private String stepOperation;
/** 参数 */
@Excel(name = "参数")
private String stepParameters;
/** 步骤动作 */
@Excel(name = "步骤动作")
private String action;
/** 预期结果 */
@Excel(name = "预期结果")
private String expectedResult;
/** 默认类型 0 HTTP接口 1 Web UI 2 API驱动 3移动端 */
@Excel(name = "默认类型")
private Integer stepType;
/** 扩展字段可用于备注、存储HTTP模板等 */
@Excel(name = "扩展字段")
private String extend;
/** 备注字段,用于接口类型的步骤的备注 */
@Excel(name = "备注字段")
private String stepRemark;
public void setStepId(Integer stepId)

View File

@ -0,0 +1,118 @@
package com.luckyframe.rc;
import com.luckyframe.rc.entity.ElementAction;
import org.springframework.web.multipart.MultipartFile;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class ReadTxt {
//存放控件集合
ArrayList<ElementAction> elementActionArrayList;
//剪贴板字符串
String clipStr = "";
public void getStrFromClip(){
Clipboard clipct = Toolkit.getDefaultToolkit().getSystemClipboard();
// 获取剪切板中的内容
Transferable clipTf= clipct.getContents(null);
if (clipTf!=null)
{
// 检查内容是否是文本类型
if (clipTf.isDataFlavorSupported(DataFlavor.stringFlavor)) {
try {
clipStr = (String) clipTf.getTransferData(DataFlavor.stringFlavor);
} catch (Exception e) {
e.printStackTrace();
}
}
}
System.out.println(clipStr);
}
/**
*
* @param file 录制好的app自动化测试脚本文件
* @return
* @throws IOException
*/
public List<ElementAction> Extract(MultipartFile file) throws IOException {
//初始化
elementActionArrayList=new ArrayList<>();
//读取脚本
if (file.isEmpty() || file.getSize() <= 0) {
file = null;
}
BufferedReader reader=new BufferedReader(new InputStreamReader(file.getInputStream(),"utf-8"));
String line;
//按行读取如果还没有读到尾一直执行
while ((line= reader.readLine())!=null){
//如果读到的这行是获取控件
if(line.contains("findElementBy")) {
if (!line.contains("//")) {
ElementAction elementAction = new ElementAction();
//按格式截取相应的值存入集合
int index = line.indexOf("findElement");
String way;
//判断获取控件的方式
if (line.substring(index, line.indexOf("\"") - 1).contains("Id"))
way = "id";
else
way = "xpath";
way = way + "=" + line.substring(line.indexOf("\"") + 1, line.lastIndexOf("\""));
elementAction.setAccess(way);
//获取控件之后必为操作
line = reader.readLine();
elementAction.setAction(line.substring(line.indexOf(".") + 1, line.indexOf("(")));
if (line.contains("\""))
elementAction.setActionValue(line.substring(line.indexOf("\"") + 1, line.lastIndexOf("\"")));
elementActionArrayList.add(elementAction);
}
}
//如果读到这行是滑动操作
else if(line.contains("(new TouchAction(driver))")) {
line =reader.readLine();
if(line.contains(".press"))
{
String actionValue="";
actionValue=actionValue+line.substring(line.indexOf(":")+2,line.indexOf(",")+1)+line.substring(line.lastIndexOf(":")+2,line.lastIndexOf("}"))+"|";
line =reader.readLine();
if(line.contains(".moveto"))
{
line=line.substring(line.indexOf(":")+2);
actionValue=actionValue+line.substring(0,line.indexOf(":"))+","+line.substring(line.lastIndexOf(":")+2,line.lastIndexOf("}"));
ElementAction elementAction=new ElementAction();
elementAction.setAction("moveto");
elementAction.setActionValue(actionValue);
elementActionArrayList.add(elementAction);
}
}
}
}
for (ElementAction elementAction : elementActionArrayList) {
System.out.println(elementAction);
}
reader.close();
return elementActionArrayList;
}
}

View File

@ -0,0 +1,127 @@
package com.luckyframe.rc;
import com.luckyframe.rc.entity.RcWebCaseSteps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
@Slf4j
public class Readfile {
/**
* 保存活动
* @param file 读取录制好的web自动化测试脚本文件内容
* @throws IOException
*/
public LinkedList<RcWebCaseSteps> saveAction(MultipartFile file) throws IOException {
LinkedList<RcWebCaseSteps> rcWebCaseStepsLinkedList=new LinkedList<>();
if (file.isEmpty() || file.getSize() <= 0) {
file = null;
}
BufferedReader reader=new BufferedReader(new InputStreamReader(file.getInputStream(),"utf-8"));
StringBuilder StringBuilder=new StringBuilder();//用来显示内容用的StringBuilder
String line;
//按行读取内容如果还没有读到尾一直执行
StringBuilder.append("\n");
int n=0;
int start = 99999999;
int end;
LinkedList<String> var=new LinkedList<>();
while ((line= reader.readLine())!=null){
StringBuilder.append(line).append("\n");
var.add(line);
n++;
line=line.trim();
if(line.startsWith("driver.get(")) {
//打开网页
log.info("打开网页");
RcWebCaseSteps rcWebCaseSteps=new RcWebCaseSteps();
rcWebCaseSteps.setStepOperation("open");
//解析网页路径
String url;
url=line.substring(line.indexOf("driver.get(")+"driver.get(".length(),line.indexOf(")")).replace("\"","");
rcWebCaseSteps.setStepParameters(url);
rcWebCaseStepsLinkedList.add(rcWebCaseSteps);
rcWebCaseSteps=null;
}else if (line.startsWith("driver.findElement(")){
//查找元素
if (line.contains("By.id(")){
//通过id查找
log.info("通过id查找");
String id;
id=line.substring(line.indexOf("By.id(")+"By.id(".length(),line.indexOf(")")).replace("\"","");
log.info("id="+id);
RcWebCaseSteps rcWebCaseSteps=new RcWebCaseSteps();
rcWebCaseSteps.setStepPath("id="+id);
//解析操作
if(line.endsWith(".click();")) {
rcWebCaseSteps.setStepOperation("click");
}else if(line.contains(".sendKeys(")){
rcWebCaseSteps.setStepOperation("sendkeys");
//解析SendKeys的value
String param=line.substring(line.indexOf(".sendKeys(")+".sendKeys(".length(),line.indexOf(";")-1).replace("\"","");
rcWebCaseSteps.setStepParameters(param);
}
rcWebCaseStepsLinkedList.add(rcWebCaseSteps);
rcWebCaseSteps=null;
}else if (line.contains("By.cssSelector")){
//通过css选择器查找
log.info("通过css查找");
String id;
id=line.substring(line.indexOf("By.cssSelector(")+"By.cssSelector(".length(),line.indexOf("\"",("driver.findElement(By.cssSelector(\"").length())).replace("\"","");
log.info("cssselector="+id);
RcWebCaseSteps rcWebCaseSteps=new RcWebCaseSteps();
rcWebCaseSteps.setStepPath("cssselector="+id);
//解析操作
if(line.endsWith(".click();")) {
rcWebCaseSteps.setStepOperation("click");
}else if(line.contains(".sendKeys(")){
rcWebCaseSteps.setStepOperation("sendkeys");
//解析SendKeys的value
String param=line.substring(line.indexOf(".sendKeys(")+".sendKeys(".length(),line.indexOf(";")-1).replace("\"","");
rcWebCaseSteps.setStepParameters(param);
}
rcWebCaseStepsLinkedList.add(rcWebCaseSteps);
rcWebCaseSteps=null;
}
}else if(line.startsWith("driver.manage().window().setSize(")){
//设置浏览器大小
log.info("设置浏览器大小");
RcWebCaseSteps rcWebCaseSteps=new RcWebCaseSteps();
rcWebCaseSteps.setStepOperation("windowsetsize");
//解析窗口大小
String operationValue;
operationValue=line.substring("driver.manage().window().setSize(new Dimension(".length(),line.indexOf(")","driver.manage().window().setSize(new Dimension(".length()));
rcWebCaseSteps.setStepParameters(operationValue);
rcWebCaseStepsLinkedList.add(rcWebCaseSteps);
rcWebCaseSteps=null;
}else if(line.startsWith("driver.switchTo().window")){
//切换窗口句柄
log.info("切换窗口句柄");
RcWebCaseSteps rcWebCaseSteps=new RcWebCaseSteps();
rcWebCaseSteps.setStepOperation("switchtowindow");
rcWebCaseStepsLinkedList.add(rcWebCaseSteps);
rcWebCaseSteps=null;
}else if (line.startsWith("{")){
start=n;
}else if(line.startsWith("}")){
end=n;
if (end>start){
for (int i=0;i<end-start;i++){
var.get((start-1)+i);
}
start=99999999;//初始化
}
}
}
log.info(StringBuilder.toString());
return rcWebCaseStepsLinkedList;
}
}

View File

@ -0,0 +1,33 @@
package com.luckyframe.rc.entity;
import lombok.Getter;
import lombok.Setter;
/**
* 元素行为
*/
@Getter
@Setter
public class ElementAction {
/**
* access
* action
* actionValue
*/
String access;
String action;
String actionValue;
@Override
public String toString() {
return "ElementAction{" +
"access='" + access + '\'' +
", action='" + action + '\'' +
", actionValue='" + actionValue + '\'' +
'}';
}
}

View File

@ -0,0 +1,30 @@
package com.luckyframe.rc.entity;
import lombok.Getter;
import lombok.Setter;
/**
* WEB自动化测试用例步骤
* @author lifengyang
*/
@Getter
@Setter
public class RcWebCaseSteps {
/** 包路径|定位路径 */
private String stepPath;
/** 方法名|操作 */
private String stepOperation;
/** 参数 */
private String stepParameters;
/** 步骤动作 */
private String action;
/** 预期结果 */
private String expectedResult;
/** 默认类型 0 HTTP接口 1 Web UI 2 API驱动 3移动端 */
private Integer stepType;
/** 扩展字段可用于备注、存储HTTP模板等 */
private String extend;
/** 备注字段,用于接口类型的步骤的备注 */
private String stepRemark;
}

View File

@ -83,8 +83,6 @@
var taskExecutePrefix = ctx + "testexecution/taskExecute";
$(function() {
//初始化子表格(无限循环)
const InitSubTable = function (index, row, $detail) {
const cur_table = $detail.html('<table style="word-break:break-all;"></table>').find('table');
@ -125,11 +123,11 @@
cellStyle: function (value, row, index, field) {
if (row.logGrade != "info") {
return {
css: {"color": "#ff0000"}
css: {"color": "#ff0000","class":"test1"}
};
} else {
return {
css: {"color": "#00bf5f"}
css: {"color": "#00bf5f","class":"test2"}
};
}
}
@ -153,7 +151,7 @@
return '<font style="color:#ff0000">' + value + '</font>&nbsp;&nbsp;&nbsp;&nbsp;' +
'<a href="#" onclick="updateStep(' + row.logId + ')">同步结果</a> ';
} else {
return '<font style="color:#ff0000">' + value + '</font>';
return '<font style="color:#ff0000">' + value + '</font>';
}
} else {
return '<font style="color:#00bf5f">' + value + '</font>';
@ -165,10 +163,9 @@
}
},],
//无线循环取子表,直到子表里面没有记录
onExpandRow: function (index, row, $Subdetail) {
oInit.InitSubTable(index, row, $Subdetail);
},
// onExpandRow: function (index, row, $Subdetail) {
// oInit.InitSubTable(index, row, $Subdetail);
// },
});
};
var options = {
@ -215,17 +212,20 @@
{
field : 'caseId',
title : '用例ID',
visible: false
width : '5%',
visible: true
},
{
field : 'taskId',
title : '任务ID',
visible: false
width : '5%',
visible: true
},
{
field : 'projectId',
title : '项目ID',
visible: false
width : '5%',
visible: true
},
{
field : 'caseSign',
@ -236,7 +236,7 @@
{
field : 'caseName',
title : '用例名称',
width : '50%',
width : '20%',
class : 'myTDLengthHidden'
},
{
@ -278,7 +278,22 @@
return '<span style="color:#9966FF">未执行</span>';
}
}
}],
},
//修改点
{
field : 'failureReason',
title : '失败原因',
width : '15%',
id:'myTDLengthHiddenFy',
class : 'myTDLengthHidden',
formatter : function(value,
row, index) {
var innerText = getFailedInfo(row.taskCaseId,'error');
return '<span style="color:#ff0000;word-break:break-all;white-space:pre-wrap">'+ innerText+'</span>';
}
}],
//注册加载子表的事件。注意下这里的三个参数!
onExpandRow : function(index, row, $detail) {
InitSubTable(index, row, $detail);
@ -286,8 +301,11 @@
};
$.table.init(options);
});
/* 同步测试结果到用例步骤 */
function updateStep(logId){
$.modal.confirm("确认要同步测试结果到步骤中吗?", function() {
@ -297,6 +315,25 @@
});
}
/*获取失败原因*/
function getFailedInfo(taskCaseId,error){
var url =logPrefix + "/list";
var options = "";
var data = {"taskCaseId": taskCaseId,"errorInfo": error};
$.ajaxSettings.async = false;
$.getJSON(url, data, function call(result) {
/*设置新的调度列表*/
$.each(result, function(i, node){
options = options + node.logDetail ;
});
});
return options;
}
/* 运行所有非成功用例 */
function run(){
$.modal.confirm("确认要运行所有非成功用例吗?", function() {
@ -342,7 +379,7 @@
});
}
/*弹出层展示图片*/
/*弹出层展示图片*/
function showPlanCase(logId) {
var url = prefix+"/showImage.do?logId="+logId;
$.modal.openViewFull($.table._option.childrenModalName, url, null, null);

View File

@ -105,7 +105,8 @@
{
field : 'schedulingId',
title : '调度ID',
visible: false,
visible: true,
width : '5%',
sortable: true
},
{
@ -116,7 +117,7 @@
{
field : 'schedulingName',
title : '调度名称',
width : '20%',
width : '15%',
formatter : function(value,
row, index) {
return '<a href="#" onclick="taskExcute('+row.schedulingId+')">'+ value + '</a> ';

View File

@ -7,12 +7,21 @@
<form class="form-horizontal m" id="form-debugCase" th:object="${projectCaseDebug}">
<input id="caseId" th:field="*{caseId}" type="hidden">
<input id="userId" th:field="*{userId}" type="hidden">
<input id="caseType" th:field="*{caseType}" type="hidden"><!--修改点-->
<div class="form-group" id="divBrowserType" style="display:none">
<label class="col-sm-3 control-label">浏览器类型:</label>
<div class="col-sm-8">
<label class="radio-box"> <input type="radio" th:field="*{browserType}" value="0" /> IE </label>
<label class="radio-box"> <input type="radio" th:field="*{browserType}" value="1" /> 火狐 </label>
<label class="radio-box"> <input type="radio" th:field="*{browserType}" value="2" th:checked="true"/> 谷歌 </label>
<label class="radio-box"> <input type="radio" th:field="*{browserType}" value="3" /> Edge </label>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">客户端:</label>
<div class="col-sm-8">
<select class="form-control" id="clientId" th:field="*{clientId}" onChange="getDriverPath()">
<option th:each="client:${clients}" th:value="${client.clientId}"
th:text="【+${client.clientName}+】+${client.clientIp}+${client.statusStr}"></option>
<option th:each="client:${clients}" th:value="${client.clientId}" th:text="【+${client.clientName}+】+${client.clientIp}+${client.statusStr}"></option>
</select>
</div>
</div>
@ -113,6 +122,14 @@
$("#driverPath option[index='1']").remove();
}
}
//修改点
//控制BrowserType显示
if($("#caseType").attr("value")==1){
$("#divBrowserType").css("display","block");
}else {
$("#divBrowserType").css("display", "none");
};
</script>
</body>
</html>

View File

@ -2,7 +2,188 @@
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<meta charset="utf-8">
<head th:include="include :: header"></head>
<!--修改点-->
<style>
.upload_dialog_div{
position:fixed;
left:0px;
right:0px;
top:0px;
bottom:0px;
overflow:auto;
visibility:hidden;
background-color: rgba(60,60,60,0.5);
z-index:99;
}
.style_content_div{
position:relative;
margin:auto;
margin-top:160px;
width:400px;
height:160px;
background: #ffffff;
}
.style_content_upper_div{
position:absolute;
left:0px;
top:0px;
width:400px;
height:100px;
background:#ffffff;
}
.style_content_lower_div{
position:absolute;
left:0px;
top:100px;
width:400px;
height:60px;
background:#ffffff;
}
.style_content_file_div{
position:absolute;
left:15px;
top:20px;
width:380px;
height:60px;
}
.style_file_span{
float:left;
width:120px;
height:36px;
font-size:24px;
text-align:right;
}
.style_file_content{
margin-top:5px;
}
.style_content_prog_div{
position:absolute;
left:18px;
top:57px;
width:360px;
height:40px;
}
.style_prog_span_hit{
color:#ff0000;
}
.style_prog_content{
width:360px;
visibility:hidden;
}
</style>
<script>
function createHttpRequest()
{
var xmlHttp=null;
try{
// Firefox, Opera 8.0+, Safari
xmlHttp=new XMLHttpRequest();
}catch (e){
// Internet Explorer
try{
xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
}catch (e){
try{
xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
}catch (e){
alert("您的浏览器不支持AJAX");
}
}
}
return xmlHttp;
}
function uploadFileToServer(){
var caseId = $($("tr[class='selected']").find('td')[4]).find("a").attr("pt_caseId");
var projectId = $($("tr[class='selected']").find('td')[4]).find("a").attr("pt_projectId");
var stepType= $($("tr[class='selected']").find('td')[4]).find("a").attr("pt_caseType");
// alert(caseId+" "+projectId+" "+stepType);
var uploadFile = document.getElementById("upload_file_id");
var uploadTip = document.getElementById("upload_tip_id");
var uploadProgress = document.getElementById("upload_progress_id");
if(uploadFile.value==""){
uploadTip.innerText="请选择一个文件";
}else if(uploadFile.files[0].size>0 &&uploadFile.files[0].size<(100*1024*1024)){
try{
if(window.FileReader){
var fReader = new FileReader();
var xhreq=createHttpRequest();
var form = new FormData();
form.append('caseId',caseId);
form.append('projectId', projectId);
form.append('stepType', stepType);
form.append('file', uploadFile.files[0]);
xhreq.onreadystatechange=function(){
if(xhreq.readyState==4){
if(xhreq.status==200){
uploadTip.innerText="文件上传成功";
setTimeout(function(){
hideUploadDialog()
},2000); //2秒后隐藏
}else{
uploadTip.innerText="文件上传失败了";
}
}
}
fReader.onload=function(e){
xhreq.open("POST","/testmanagmt/RecordController/insert",true);
xhreq.setRequestHeader("ContentType", "multipart/form-data"); //流类型
xhreq.send(form);
}
fReader.onprogress = function(e){
uploadProgress.value = e.loaded*100/e.total;
}
fReader.readAsArrayBuffer(uploadFile.files[0]);
uploadProgress.style.visibility="visible";
uploadProgress.value = 0;
}else{
uploadTip.innerText="浏览器不支持上传文件";
}
}catch(e){
uploadTip.innerText="文件上传失败";
}
}else{
uploadTip.innerText="文件不符合要求";
}
}
function showUploadDialog(){
var up_dialog=document.getElementById("upload_dialog");
document.getElementById("upload_tip_id").innerText="请选择要上传的文件";
document.getElementById("upload_progress_id").style.visibility="hidden";
up_dialog.style.visibility="visible";
}
function hideUploadDialog(){
var up_dialog=document.getElementById("upload_dialog");
document.getElementById("upload_progress_id").style.visibility="hidden";
up_dialog.style.visibility="hidden";
}
</script>
<body class="gray-bg">
<!--修改点-->
<div id="upload_dialog" class="upload_dialog_div">
<div class="style_content_div">
<div class="style_content_upper_div">
<div class="style_content_file_div">
<span class="style_file_span"> 文件:</span>
<input class="style_file_content" type="file" id="upload_file_id"/>
</div>
<div class="style_content_prog_div">
<span class="style_prog_span_hit" id="upload_tip_id"> 请选择要上传的文件 </span>
<progress class="style_prog_content" id="upload_progress_id" value="0" max="100"></progress>
</div>
</div>
<div class="style_content_lower_div">
<div class="layui-layer-btn layui-layer-btn-">
<a class="layui-layer-btn1" onclick="hideUploadDialog()">取消</a>
<a class="layui-layer-btn0" onclick="uploadFileToServer()">确认</a>
</div>
</div>
</div>
</div>
<div class="container-div">
<div class="row">
@ -88,6 +269,12 @@
<i class="fa fa-list-ol"></i> 用例步骤
</a>
</shiro:hasPermission>
<!--修改点-->
<shiro:hasPermission name="testmanagmt:projectCase:import">
<a class="btn btn-info btn-import" onclick="showUploadDialog()">
<span class="glyphicon glyphicon-import" aria-hidden="true"></span> 用例步骤导入
</a>
</shiro:hasPermission>
<shiro:hasPermission name="testmanagmt:projectCase:remove">
<a class="btn btn-danger btn-del btn-del disabled" onclick="$.operate.removeAll()">
<i class="fa fa-remove"></i> 删除
@ -95,7 +282,12 @@
</shiro:hasPermission>
<shiro:hasPermission name="testmanagmt:projectCase:export">
<a class="btn btn-warning" onclick="$.table.exportExcel('formId')">
<i class="fa fa-download"></i> 导出
<span class="glyphicon glyphicon-export" aria-hidden="true"></span> 导出用例列表
</a>
</shiro:hasPermission>
<shiro:hasPermission name="testmanagmt:projectCase:exportcasestep">
<a class="btn btn-warning" onclick="exportcasestepExcel()">
<span class="glyphicon glyphicon-export" aria-hidden="true"></span> 导出用例步骤
</a>
</shiro:hasPermission>
</div>
@ -173,7 +365,11 @@
width: '10%',
class: 'myTDLengthHidden',
formatter: function (value, row, index) {
return '<a class="btn btn-info btn-edit" onclick="$.operate.customFull('+row.caseId+')">'+value+'</a>';
return '<a class="btn btn-info btn-edit" '+
'pt_caseId="'+row.caseId+'" ' +
'pt_projectId="'+row.projectId+'" '+
'pt_caseType="'+row.caseType+'" '+
'onclick="$.operate.customFull('+row.caseId+')">'+value+'</a>';
}
},
{
@ -551,6 +747,28 @@
initializeModuleTree();
$.form.reset();
}
/* 导出用例步骤*/
function exportcasestepExcel() {
$.modal.loading("正在导出数据,请稍后...");
var caseId = $($("tr[class='selected']").find('td')[4]).find("a").attr("pt_caseId");
$.ajax({
type: "POST",
url: stepsPrefix + "/export",
data: {caseId:caseId},
async: false,
error: function (result) {
$.modal.alertError(result.msg);
$.modal.closeLoading();
},
success: function (result) {
if (result.code == web_status.SUCCESS) {
window.location.href = ctx + "common/download?fileName=" + result.msg + "&delete=" + true;
}
$.modal.closeLoading();
}
});
}
</script>
</body>
</html>

View File

@ -87,6 +87,9 @@
<div class="col-lg-4 text-center" style="width: 100%">
<button class="btn btn-info" id="debugButton" onclick="showDebugModal()" type="button" style="padding-left: 36px;padding-right: 36px;" th:if="${projectCase.caseType==0||projectCase.caseType==2}">&nbsp;&nbsp;&nbsp;&nbsp;</button>
</div>
<div class="col-lg-4 text-center" style="width: 100%">
<button class="btn btn-info" id="debugButton2" onclick="showDebugModal()" type="button" style="padding-left: 36px;padding-right: 36px;" th:if="${projectCase.caseType==1}">&nbsp;&nbsp;&nbsp;&nbsp;</button><!--修改点-->
</div>
</div>
</div>
@ -321,12 +324,14 @@
/*不同的校验规则,不同的错误提示*/
$.validator.messages.required = "HTTP接口必填";
return true;
}else if(stepType=="2" && $("#expectedResult"+num).val()==""){
$.validator.messages.required = "API接口必填";
return true;
}else{
}else {
return false;
}
//修改点
// else if(stepType=="2" && $("#expectedResult"+num).val()==""){
// $.validator.messages.required = "API接口必填";
// return true;
// }
}
},
extend:{