1258 lines
37 KiB
JavaScript
Executable File
1258 lines
37 KiB
JavaScript
Executable File
import React, { PureComponent as Component } from 'react';
|
||
import { connect } from 'react-redux';
|
||
import PropTypes from 'prop-types';
|
||
import { withRouter } from 'react-router';
|
||
import { Link } from 'react-router-dom';
|
||
//import constants from '../../../../constants/variable.js'
|
||
import { Tooltip, Icon,Input, Button, Row, Col, Spin, Modal, message, Select, Switch } from 'antd';
|
||
import {
|
||
fetchInterfaceColList,
|
||
fetchCaseList,
|
||
setColData,
|
||
fetchCaseEnvList
|
||
} from '../../../../reducer/modules/interfaceCol';
|
||
import HTML5Backend from 'react-dnd-html5-backend';
|
||
import { getToken, getEnv } from '../../../../reducer/modules/project';
|
||
import { DragDropContext } from 'react-dnd';
|
||
import AceEditor from 'client/components/AceEditor/AceEditor';
|
||
import * as Table from 'reactabular-table';
|
||
import * as dnd from 'reactabular-dnd';
|
||
import * as resolve from 'table-resolver';
|
||
import axios from 'axios';
|
||
import CaseReport from './CaseReport.js';
|
||
import _ from 'underscore';
|
||
import { initCrossRequest } from 'client/components/Postman/CheckCrossInstall.js';
|
||
import produce from 'immer';
|
||
import {InsertCodeMap} from 'client/components/Postman/Postman.js'
|
||
|
||
const plugin = require('client/plugin.js');
|
||
const {
|
||
handleParams,
|
||
crossRequest,
|
||
handleCurrDomain,
|
||
checkNameIsExistInArray
|
||
} = require('common/postmanLib.js');
|
||
const { handleParamsValue, json_parse, ArrayToObject } = require('common/utils.js');
|
||
import CaseEnv from 'client/components/CaseEnv';
|
||
import Label from '../../../../components/Label/Label.js';
|
||
|
||
const Option = Select.Option;
|
||
const createContext = require('common/createContext')
|
||
|
||
import copy from 'copy-to-clipboard';
|
||
|
||
const defaultModalStyle = {
|
||
top: 10
|
||
}
|
||
|
||
function handleReport(json) {
|
||
try {
|
||
return JSON.parse(json);
|
||
} catch (e) {
|
||
return {};
|
||
}
|
||
}
|
||
|
||
@connect(
|
||
state => {
|
||
return {
|
||
interfaceColList: state.interfaceCol.interfaceColList,
|
||
currColId: state.interfaceCol.currColId,
|
||
currCaseId: state.interfaceCol.currCaseId,
|
||
isShowCol: state.interfaceCol.isShowCol,
|
||
isRander: state.interfaceCol.isRander,
|
||
currCaseList: state.interfaceCol.currCaseList,
|
||
currProject: state.project.currProject,
|
||
token: state.project.token,
|
||
envList: state.interfaceCol.envList,
|
||
curProjectRole: state.project.currProject.role,
|
||
projectEnv: state.project.projectEnv,
|
||
curUid: state.user.uid
|
||
};
|
||
},
|
||
{
|
||
fetchInterfaceColList,
|
||
fetchCaseList,
|
||
setColData,
|
||
getToken,
|
||
getEnv,
|
||
fetchCaseEnvList
|
||
}
|
||
)
|
||
@withRouter
|
||
@DragDropContext(HTML5Backend)
|
||
class InterfaceColContent extends Component {
|
||
static propTypes = {
|
||
match: PropTypes.object,
|
||
interfaceColList: PropTypes.array,
|
||
fetchInterfaceColList: PropTypes.func,
|
||
fetchCaseList: PropTypes.func,
|
||
setColData: PropTypes.func,
|
||
history: PropTypes.object,
|
||
currCaseList: PropTypes.array,
|
||
currColId: PropTypes.number,
|
||
currCaseId: PropTypes.number,
|
||
isShowCol: PropTypes.bool,
|
||
isRander: PropTypes.bool,
|
||
currProject: PropTypes.object,
|
||
getToken: PropTypes.func,
|
||
token: PropTypes.string,
|
||
curProjectRole: PropTypes.string,
|
||
getEnv: PropTypes.func,
|
||
projectEnv: PropTypes.object,
|
||
fetchCaseEnvList: PropTypes.func,
|
||
envList: PropTypes.array,
|
||
curUid: PropTypes.number
|
||
};
|
||
|
||
constructor(props) {
|
||
super(props);
|
||
this.reports = {};
|
||
this.records = {};
|
||
this.state = {
|
||
rows: [],
|
||
reports: {},
|
||
visible: false,
|
||
curCaseid: null,
|
||
hasPlugin: false,
|
||
|
||
advVisible: false,
|
||
curScript: '',
|
||
enableScript: false,
|
||
autoVisible: false,
|
||
mode: 'html',
|
||
email: false,
|
||
download: false,
|
||
currColEnvObj: {},
|
||
collapseKey: '1',
|
||
commonSettingModalVisible: false,
|
||
commonSetting: {
|
||
checkHttpCodeIs200: false,
|
||
checkResponseField: {
|
||
name: 'code',
|
||
value: '0',
|
||
enable: false
|
||
},
|
||
checkResponseSchema: false,
|
||
checkScript:{
|
||
enable: false,
|
||
content: ''
|
||
}
|
||
}
|
||
};
|
||
this.onRow = this.onRow.bind(this);
|
||
this.onMoveRow = this.onMoveRow.bind(this);
|
||
}
|
||
|
||
async handleColIdChange(newColId){
|
||
this.props.setColData({
|
||
currColId: +newColId,
|
||
isShowCol: true,
|
||
isRander: false
|
||
});
|
||
|
||
let result = await this.props.fetchCaseList(newColId);
|
||
if (result.payload.data.errcode === 0) {
|
||
this.reports = handleReport(result.payload.data.colData.test_report);
|
||
this.setState({
|
||
commonSetting:{
|
||
...this.state.commonSetting,
|
||
...result.payload.data.colData
|
||
}
|
||
})
|
||
}
|
||
|
||
await this.props.fetchCaseList(newColId);
|
||
await this.props.fetchCaseEnvList(newColId);
|
||
this.changeCollapseClose();
|
||
this.handleColdata(this.props.currCaseList);
|
||
}
|
||
|
||
async UNSAFE_componentWillMount() {
|
||
const result = await this.props.fetchInterfaceColList(this.props.match.params.id);
|
||
await this.props.getToken(this.props.match.params.id);
|
||
let { currColId } = this.props;
|
||
const params = this.props.match.params;
|
||
const { actionId } = params;
|
||
this.currColId = currColId = +actionId || result.payload.data.data[0]._id;
|
||
// this.props.history.push('/project/' + params.id + '/interface/col/' + currColId);
|
||
if (currColId && currColId != 0) {
|
||
await this.handleColIdChange(currColId)
|
||
}
|
||
|
||
this._crossRequestInterval = initCrossRequest(hasPlugin => {
|
||
this.setState({ hasPlugin: hasPlugin });
|
||
});
|
||
}
|
||
|
||
componentWillUnmount() {
|
||
clearInterval(this._crossRequestInterval);
|
||
}
|
||
|
||
// 更新分类简介
|
||
handleChangeInterfaceCol = (desc, name) => {
|
||
let params = {
|
||
col_id: this.props.currColId,
|
||
name: name,
|
||
desc: desc
|
||
};
|
||
|
||
axios.post('/api/col/up_col', params).then(async res => {
|
||
if (res.data.errcode) {
|
||
return message.error(res.data.errmsg);
|
||
}
|
||
let project_id = this.props.match.params.id;
|
||
await this.props.fetchInterfaceColList(project_id);
|
||
message.success('接口集合简介更新成功');
|
||
});
|
||
};
|
||
|
||
// 整合header信息
|
||
handleReqHeader = (project_id, req_header, case_env) => {
|
||
let envItem = _.find(this.props.envList, item => {
|
||
return item._id === project_id;
|
||
});
|
||
|
||
let currDomain = handleCurrDomain(envItem && envItem.env, case_env);
|
||
let header = currDomain.header;
|
||
header.forEach(item => {
|
||
if (!checkNameIsExistInArray(item.name, req_header)) {
|
||
// item.abled = true;
|
||
item = {
|
||
...item,
|
||
abled: true
|
||
};
|
||
req_header.push(item);
|
||
}
|
||
});
|
||
return req_header;
|
||
};
|
||
|
||
handleColdata = (rows, currColEnvObj = {}) => {
|
||
let that = this;
|
||
let newRows = produce(rows, draftRows => {
|
||
draftRows.map(item => {
|
||
item.id = item._id;
|
||
item._test_status = item.test_status;
|
||
if(currColEnvObj[item.project_id]){
|
||
item.case_env =currColEnvObj[item.project_id];
|
||
}
|
||
item.req_headers = that.handleReqHeader(item.project_id, item.req_headers, item.case_env);
|
||
return item;
|
||
});
|
||
});
|
||
this.setState({ rows: newRows });
|
||
};
|
||
|
||
executeTests = async () => {
|
||
for (let i = 0, l = this.state.rows.length, newRows, curitem; i < l; i++) {
|
||
let { rows } = this.state;
|
||
|
||
let envItem = _.find(this.props.envList, item => {
|
||
return item._id === rows[i].project_id;
|
||
});
|
||
|
||
curitem = Object.assign(
|
||
{},
|
||
rows[i],
|
||
{
|
||
env: envItem.env,
|
||
pre_script: this.props.currProject.pre_script,
|
||
after_script: this.props.currProject.after_script
|
||
},
|
||
{ test_status: 'loading' }
|
||
);
|
||
newRows = [].concat([], rows);
|
||
newRows[i] = curitem;
|
||
this.setState({ rows: newRows });
|
||
let status = 'error',
|
||
result;
|
||
try {
|
||
result = await this.handleTest(curitem);
|
||
|
||
if (result.code === 400) {
|
||
status = 'error';
|
||
} else if (result.code === 0) {
|
||
status = 'ok';
|
||
} else if (result.code === 1) {
|
||
status = 'invalid';
|
||
}
|
||
} catch (e) {
|
||
console.error(e);
|
||
status = 'error';
|
||
result = e;
|
||
}
|
||
|
||
//result.body = result.data;
|
||
this.reports[curitem._id] = result;
|
||
this.records[curitem._id] = {
|
||
status: result.status,
|
||
params: result.params,
|
||
body: result.res_body
|
||
};
|
||
|
||
curitem = Object.assign({}, rows[i], { test_status: status });
|
||
newRows = [].concat([], rows);
|
||
newRows[i] = curitem;
|
||
this.setState({ rows: newRows });
|
||
}
|
||
await axios.post('/api/col/up_col', {
|
||
col_id: this.props.currColId,
|
||
test_report: JSON.stringify(this.reports)
|
||
});
|
||
};
|
||
|
||
handleTest = async interfaceData => {
|
||
let requestParams = {};
|
||
let options = handleParams(interfaceData, this.handleValue, requestParams);
|
||
|
||
let result = {
|
||
code: 400,
|
||
msg: '数据异常',
|
||
validRes: []
|
||
};
|
||
|
||
await plugin.emitHook('before_col_request', Object.assign({}, options, {
|
||
type: 'col',
|
||
caseId: options.caseId,
|
||
projectId: interfaceData.project_id,
|
||
interfaceId: interfaceData.interface_id
|
||
}));
|
||
|
||
try {
|
||
let data = await crossRequest(options, interfaceData.pre_script, interfaceData.after_script, createContext(
|
||
this.props.curUid,
|
||
this.props.match.params.id,
|
||
interfaceData.interface_id
|
||
));
|
||
options.taskId = this.props.curUid;
|
||
let res = (data.res.body = json_parse(data.res.body));
|
||
result = {
|
||
...options,
|
||
...result,
|
||
res_header: data.res.header,
|
||
res_body: res,
|
||
status: data.res.status,
|
||
statusText: data.res.statusText
|
||
};
|
||
|
||
await plugin.emitHook('after_col_request', result, {
|
||
type: 'col',
|
||
caseId: options.caseId,
|
||
projectId: interfaceData.project_id,
|
||
interfaceId: interfaceData.interface_id
|
||
});
|
||
|
||
if (options.data && typeof options.data === 'object') {
|
||
requestParams = {
|
||
...requestParams,
|
||
...options.data
|
||
};
|
||
}
|
||
|
||
let validRes = [];
|
||
|
||
let responseData = Object.assign(
|
||
{},
|
||
{
|
||
status: data.res.status,
|
||
body: res,
|
||
header: data.res.header,
|
||
statusText: data.res.statusText
|
||
}
|
||
);
|
||
|
||
// 断言测试
|
||
await this.handleScriptTest(interfaceData, responseData, validRes, requestParams);
|
||
|
||
if (validRes.length === 0) {
|
||
result.code = 0;
|
||
result.validRes = [
|
||
{
|
||
message: '验证通过'
|
||
}
|
||
];
|
||
} else if (validRes.length > 0) {
|
||
result.code = 1;
|
||
result.validRes = validRes;
|
||
}
|
||
} catch (data) {
|
||
result = {
|
||
...options,
|
||
...result,
|
||
res_header: data.header,
|
||
res_body: data.body || data.message,
|
||
status: 0,
|
||
statusText: data.message,
|
||
code: 400,
|
||
validRes: [
|
||
{
|
||
message: data.message
|
||
}
|
||
]
|
||
};
|
||
}
|
||
|
||
result.params = requestParams;
|
||
return result;
|
||
};
|
||
|
||
//response, validRes
|
||
// 断言测试
|
||
handleScriptTest = async (interfaceData, response, validRes, requestParams) => {
|
||
// 是否启动断言
|
||
try {
|
||
let test = await axios.post('/api/col/run_script', {
|
||
response: response,
|
||
records: this.records,
|
||
script: interfaceData.test_script,
|
||
params: requestParams,
|
||
col_id: this.props.currColId,
|
||
interface_id: interfaceData.interface_id
|
||
});
|
||
if (test.data.errcode !== 0) {
|
||
test.data.data.logs.forEach(item => {
|
||
validRes.push({ message: item });
|
||
});
|
||
}
|
||
} catch (err) {
|
||
validRes.push({
|
||
message: 'Error: ' + err.message
|
||
});
|
||
}
|
||
};
|
||
|
||
handleValue = (val, global) => {
|
||
let globalValue = ArrayToObject(global);
|
||
let context = Object.assign({}, { global: globalValue }, this.records);
|
||
return handleParamsValue(val, context);
|
||
};
|
||
|
||
arrToObj = (arr, requestParams) => {
|
||
arr = arr || [];
|
||
const obj = {};
|
||
arr.forEach(item => {
|
||
if (item.name && item.enable && item.type !== 'file') {
|
||
obj[item.name] = this.handleValue(item.value);
|
||
if (requestParams) {
|
||
requestParams[item.name] = obj[item.name];
|
||
}
|
||
}
|
||
});
|
||
return obj;
|
||
};
|
||
|
||
onRow(row) {
|
||
return { rowId: row.id, onMove: this.onMoveRow, onDrop: this.onDrop };
|
||
}
|
||
|
||
onDrop = () => {
|
||
let changes = [];
|
||
this.state.rows.forEach((item, index) => {
|
||
changes.push({ id: item._id, index: index });
|
||
});
|
||
axios.post('/api/col/up_case_index', changes).then(() => {
|
||
this.props.fetchInterfaceColList(this.props.match.params.id);
|
||
});
|
||
};
|
||
onMoveRow({ sourceRowId, targetRowId }) {
|
||
let rows = dnd.moveRows({ sourceRowId, targetRowId })(this.state.rows);
|
||
|
||
if (rows) {
|
||
this.setState({ rows });
|
||
}
|
||
}
|
||
|
||
onChangeTest = d => {
|
||
|
||
this.setState({
|
||
commonSetting: {
|
||
...this.state.commonSetting,
|
||
checkScript: {
|
||
...this.state.commonSetting.checkScript,
|
||
content: d.text
|
||
}
|
||
}
|
||
});
|
||
};
|
||
|
||
handleInsertCode = code => {
|
||
this.aceEditor.editor.insertCode(code);
|
||
};
|
||
|
||
async UNSAFE_componentWillReceiveProps(nextProps) {
|
||
let newColId = !isNaN(nextProps.match.params.actionId) ? +nextProps.match.params.actionId : 0;
|
||
|
||
if (newColId && ((this.currColId && newColId !== this.currColId) || nextProps.isRander)) {
|
||
this.currColId = newColId;
|
||
this.handleColIdChange(newColId)
|
||
}
|
||
}
|
||
|
||
// 测试用例环境面板折叠
|
||
changeCollapseClose = key => {
|
||
if (key) {
|
||
this.setState({
|
||
collapseKey: key
|
||
});
|
||
} else {
|
||
this.setState({
|
||
collapseKey: '1',
|
||
currColEnvObj: {}
|
||
});
|
||
}
|
||
};
|
||
|
||
openReport = id => {
|
||
if (!this.reports[id]) {
|
||
return message.warn('还没有生成报告');
|
||
}
|
||
this.setState({ visible: true, curCaseid: id });
|
||
};
|
||
|
||
openAdv = id => {
|
||
let findCase = _.find(this.props.currCaseList, item => item.id === id);
|
||
|
||
this.setState({
|
||
enableScript: findCase.enable_script,
|
||
curScript: findCase.test_script,
|
||
advVisible: true,
|
||
curCaseid: id
|
||
});
|
||
};
|
||
|
||
handleScriptChange = d => {
|
||
this.setState({ curScript: d.text });
|
||
};
|
||
|
||
handleAdvCancel = () => {
|
||
this.setState({ advVisible: false });
|
||
};
|
||
|
||
handleAdvOk = async () => {
|
||
const { curCaseid, enableScript, curScript } = this.state;
|
||
const res = await axios.post('/api/col/up_case', {
|
||
id: curCaseid,
|
||
test_script: curScript,
|
||
enable_script: enableScript
|
||
});
|
||
if (res.data.errcode === 0) {
|
||
message.success('更新成功');
|
||
}
|
||
this.setState({ advVisible: false });
|
||
let currColId = this.currColId;
|
||
this.props.setColData({
|
||
currColId: +currColId,
|
||
isShowCol: true,
|
||
isRander: false
|
||
});
|
||
await this.props.fetchCaseList(currColId);
|
||
|
||
this.handleColdata(this.props.currCaseList);
|
||
};
|
||
|
||
handleCancel = () => {
|
||
this.setState({ visible: false });
|
||
};
|
||
|
||
currProjectEnvChange = (envName, project_id) => {
|
||
let currColEnvObj = {
|
||
...this.state.currColEnvObj,
|
||
[project_id]: envName
|
||
};
|
||
this.setState({ currColEnvObj });
|
||
// this.handleColdata(this.props.currCaseList, envName, project_id);
|
||
this.handleColdata(this.props.currCaseList,currColEnvObj);
|
||
};
|
||
|
||
autoTests = () => {
|
||
this.setState({ autoVisible: true, currColEnvObj: {}, collapseKey: '' });
|
||
};
|
||
|
||
handleAuto = () => {
|
||
this.setState({
|
||
autoVisible: false,
|
||
email: false,
|
||
download: false,
|
||
mode: 'html',
|
||
currColEnvObj: {},
|
||
collapseKey: ''
|
||
});
|
||
};
|
||
|
||
copyUrl = url => {
|
||
copy(url);
|
||
message.success('已经成功复制到剪切板');
|
||
};
|
||
|
||
modeChange = mode => {
|
||
this.setState({ mode });
|
||
};
|
||
|
||
emailChange = email => {
|
||
this.setState({ email });
|
||
};
|
||
|
||
downloadChange = download => {
|
||
this.setState({ download });
|
||
};
|
||
|
||
handleColEnvObj = envObj => {
|
||
let str = '';
|
||
for (let key in envObj) {
|
||
str += envObj[key] ? `&env_${key}=${envObj[key]}` : '';
|
||
}
|
||
return str;
|
||
};
|
||
|
||
handleCommonSetting = ()=>{
|
||
let setting = this.state.commonSetting;
|
||
|
||
let params = {
|
||
col_id: this.props.currColId,
|
||
...setting
|
||
|
||
};
|
||
console.log(params)
|
||
|
||
axios.post('/api/col/up_col', params).then(async res => {
|
||
if (res.data.errcode) {
|
||
return message.error(res.data.errmsg);
|
||
}
|
||
message.success('配置测试集成功');
|
||
});
|
||
|
||
this.setState({
|
||
commonSettingModalVisible: false
|
||
})
|
||
}
|
||
|
||
cancelCommonSetting = ()=>{
|
||
this.setState({
|
||
commonSettingModalVisible: false
|
||
})
|
||
}
|
||
|
||
openCommonSetting = ()=>{
|
||
this.setState({
|
||
commonSettingModalVisible: true
|
||
})
|
||
}
|
||
|
||
changeCommonFieldSetting = (key)=>{
|
||
return (e)=>{
|
||
let value = e;
|
||
if(typeof e === 'object' && e){
|
||
value = e.target.value;
|
||
}
|
||
let {checkResponseField} = this.state.commonSetting;
|
||
this.setState({
|
||
commonSetting: {
|
||
...this.state.commonSetting,
|
||
checkResponseField: {
|
||
...checkResponseField,
|
||
[key]: value
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
render() {
|
||
const currProjectId = this.props.currProject._id;
|
||
const columns = [
|
||
{
|
||
property: 'casename',
|
||
header: {
|
||
label: '用例名称'
|
||
},
|
||
props: {
|
||
style: {
|
||
width: '250px'
|
||
}
|
||
},
|
||
cell: {
|
||
formatters: [
|
||
(text, { rowData }) => {
|
||
let record = rowData;
|
||
return (
|
||
<Link to={'/project/' + currProjectId + '/interface/case/' + record._id}>
|
||
{record.casename.length > 23
|
||
? record.casename.substr(0, 20) + '...'
|
||
: record.casename}
|
||
</Link>
|
||
);
|
||
}
|
||
]
|
||
}
|
||
},
|
||
{
|
||
header: {
|
||
label: 'key',
|
||
formatters: [
|
||
() => {
|
||
return (
|
||
<Tooltip
|
||
title={
|
||
<span>
|
||
{' '}
|
||
每个用例都有唯一的key,用于获取所匹配接口的响应数据,例如使用{' '}
|
||
<a
|
||
href="https://hellosean1025.github.io/yapi/documents/case.html#%E7%AC%AC%E4%BA%8C%E6%AD%A5%EF%BC%8C%E7%BC%96%E8%BE%91%E6%B5%8B%E8%AF%95%E7%94%A8%E4%BE%8B"
|
||
className="link-tooltip"
|
||
target="blank"
|
||
>
|
||
{' '}
|
||
变量参数{' '}
|
||
</a>{' '}
|
||
功能{' '}
|
||
</span>
|
||
}
|
||
>
|
||
Key
|
||
</Tooltip>
|
||
);
|
||
}
|
||
]
|
||
},
|
||
props: {
|
||
style: {
|
||
width: '100px'
|
||
}
|
||
},
|
||
cell: {
|
||
formatters: [
|
||
(value, { rowData }) => {
|
||
return <span>{rowData._id}</span>;
|
||
}
|
||
]
|
||
}
|
||
},
|
||
{
|
||
property: 'test_status',
|
||
header: {
|
||
label: '状态'
|
||
},
|
||
props: {
|
||
style: {
|
||
width: '100px'
|
||
}
|
||
},
|
||
cell: {
|
||
formatters: [
|
||
(value, { rowData }) => {
|
||
let id = rowData._id;
|
||
let code = this.reports[id] ? this.reports[id].code : 0;
|
||
if (rowData.test_status === 'loading') {
|
||
return (
|
||
<div>
|
||
<Spin />
|
||
</div>
|
||
);
|
||
}
|
||
|
||
switch (code) {
|
||
case 0:
|
||
return (
|
||
<div>
|
||
<Tooltip title="Pass">
|
||
<Icon
|
||
style={{
|
||
color: '#00a854'
|
||
}}
|
||
type="check-circle"
|
||
/>
|
||
</Tooltip>
|
||
</div>
|
||
);
|
||
case 400:
|
||
return (
|
||
<div>
|
||
<Tooltip title="请求异常">
|
||
<Icon
|
||
type="info-circle"
|
||
style={{
|
||
color: '#f04134'
|
||
}}
|
||
/>
|
||
</Tooltip>
|
||
</div>
|
||
);
|
||
case 1:
|
||
return (
|
||
<div>
|
||
<Tooltip title="验证失败">
|
||
<Icon
|
||
type="exclamation-circle"
|
||
style={{
|
||
color: '#ffbf00'
|
||
}}
|
||
/>
|
||
</Tooltip>
|
||
</div>
|
||
);
|
||
default:
|
||
return (
|
||
<div>
|
||
<Icon
|
||
style={{
|
||
color: '#00a854'
|
||
}}
|
||
type="check-circle"
|
||
/>
|
||
</div>
|
||
);
|
||
}
|
||
}
|
||
]
|
||
}
|
||
},
|
||
{
|
||
property: 'path',
|
||
header: {
|
||
label: '接口路径'
|
||
},
|
||
cell: {
|
||
formatters: [
|
||
(text, { rowData }) => {
|
||
let record = rowData;
|
||
return (
|
||
<Tooltip title="跳转到对应接口">
|
||
<Link to={`/project/${record.project_id}/interface/api/${record.interface_id}`}>
|
||
{record.path.length > 23 ? record.path + '...' : record.path}
|
||
</Link>
|
||
</Tooltip>
|
||
);
|
||
}
|
||
]
|
||
}
|
||
},
|
||
{
|
||
header: {
|
||
label: '测试报告'
|
||
},
|
||
props: {
|
||
style: {
|
||
width: '200px'
|
||
}
|
||
},
|
||
cell: {
|
||
formatters: [
|
||
(text, { rowData }) => {
|
||
let reportFun = () => {
|
||
if (!this.reports[rowData.id]) {
|
||
return null;
|
||
}
|
||
return <Button onClick={() => this.openReport(rowData.id)}>测试报告</Button>;
|
||
};
|
||
return <div className="interface-col-table-action">{reportFun()}</div>;
|
||
}
|
||
]
|
||
}
|
||
}
|
||
];
|
||
const { rows } = this.state;
|
||
const components = {
|
||
header: {
|
||
cell: dnd.Header
|
||
},
|
||
body: {
|
||
row: dnd.Row
|
||
}
|
||
};
|
||
const resolvedColumns = resolve.columnChildren({ columns });
|
||
const resolvedRows = resolve.resolve({ columns: resolvedColumns, method: resolve.nested })(
|
||
rows
|
||
);
|
||
|
||
const localUrl =
|
||
location.protocol +
|
||
'//' +
|
||
location.hostname +
|
||
(location.port !== '' ? ':' + location.port : '');
|
||
let currColEnvObj = this.handleColEnvObj(this.state.currColEnvObj);
|
||
const autoTestsUrl = `/api/open/run_auto_test?id=${this.props.currColId}&token=${
|
||
this.props.token
|
||
}${currColEnvObj ? currColEnvObj : ''}&mode=${this.state.mode}&email=${
|
||
this.state.email
|
||
}&download=${this.state.download}`;
|
||
|
||
let col_name = '';
|
||
let col_desc = '';
|
||
|
||
for (var i = 0; i < this.props.interfaceColList.length; i++) {
|
||
if (this.props.interfaceColList[i]._id === this.props.currColId) {
|
||
col_name = this.props.interfaceColList[i].name;
|
||
col_desc = this.props.interfaceColList[i].desc;
|
||
break;
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div className="interface-col">
|
||
<Modal
|
||
title="通用规则配置"
|
||
visible={this.state.commonSettingModalVisible}
|
||
onOk={this.handleCommonSetting}
|
||
onCancel={this.cancelCommonSetting}
|
||
width={'1000px'}
|
||
style={defaultModalStyle}
|
||
>
|
||
<div className="common-setting-modal">
|
||
<Row className="setting-item">
|
||
<Col className="col-item" span="4">
|
||
<label>检查HttpCode: <Tooltip title={'检查 http code 是否为 200'}>
|
||
<Icon type="question-circle-o" style={{ width: '10px' }} />
|
||
</Tooltip></label>
|
||
</Col>
|
||
<Col className="col-item" span="18">
|
||
<Switch onChange={e=>{
|
||
let {commonSetting} = this.state;
|
||
this.setState({
|
||
commonSetting :{
|
||
...commonSetting,
|
||
checkHttpCodeIs200: e
|
||
}
|
||
})
|
||
}} checked={this.state.commonSetting.checkHttpCodeIs200} checkedChildren="开" unCheckedChildren="关" />
|
||
</Col>
|
||
</Row>
|
||
|
||
<Row className="setting-item">
|
||
<Col className="col-item" span="4">
|
||
<label>检查返回json: <Tooltip title={'检查接口返回数据字段值,比如检查 code 是不是等于 0'}>
|
||
<Icon type="question-circle-o" style={{ width: '10px' }} />
|
||
</Tooltip></label>
|
||
</Col>
|
||
<Col className="col-item" span="6">
|
||
<Input value={this.state.commonSetting.checkResponseField.name} onChange={this.changeCommonFieldSetting('name')} placeholder="字段名" />
|
||
</Col>
|
||
<Col className="col-item" span="6">
|
||
<Input onChange={this.changeCommonFieldSetting('value')} value={this.state.commonSetting.checkResponseField.value} placeholder="值" />
|
||
</Col>
|
||
<Col className="col-item" span="6">
|
||
<Switch onChange={this.changeCommonFieldSetting('enable')} checked={this.state.commonSetting.checkResponseField.enable} checkedChildren="开" unCheckedChildren="关" />
|
||
</Col>
|
||
</Row>
|
||
|
||
<Row className="setting-item">
|
||
<Col className="col-item" span="4">
|
||
<label>检查返回数据结构: <Tooltip title={'只有 response 基于 json-schema 方式定义,该检查才会生效'}>
|
||
<Icon type="question-circle-o" style={{ width: '10px' }} />
|
||
</Tooltip></label>
|
||
</Col>
|
||
<Col className="col-item" span="18">
|
||
<Switch onChange={e=>{
|
||
let {commonSetting} = this.state;
|
||
this.setState({
|
||
commonSetting :{
|
||
...commonSetting,
|
||
checkResponseSchema: e
|
||
}
|
||
})
|
||
}} checked={this.state.commonSetting.checkResponseSchema} checkedChildren="开" unCheckedChildren="关" />
|
||
</Col>
|
||
</Row>
|
||
|
||
<Row className="setting-item">
|
||
<Col className="col-item " span="4">
|
||
<label>全局测试脚本: <Tooltip title={'在跑自动化测试时,优先调用全局脚本,只有全局脚本通过测试,才会开始跑case自定义的测试脚本'}>
|
||
<Icon type="question-circle-o" style={{ width: '10px' }} />
|
||
</Tooltip></label>
|
||
</Col>
|
||
<Col className="col-item" span="14">
|
||
<div><Switch onChange={e=>{
|
||
let {commonSetting} = this.state;
|
||
this.setState({
|
||
commonSetting :{
|
||
...commonSetting,
|
||
checkScript: {
|
||
...this.state.checkScript,
|
||
enable: e
|
||
}
|
||
}
|
||
})
|
||
}} checked={this.state.commonSetting.checkScript.enable} checkedChildren="开" unCheckedChildren="关" /></div>
|
||
<AceEditor
|
||
onChange={this.onChangeTest}
|
||
className="case-script"
|
||
data={this.state.commonSetting.checkScript.content}
|
||
ref={aceEditor => {
|
||
this.aceEditor = aceEditor;
|
||
}}
|
||
/>
|
||
</Col>
|
||
<Col span="6">
|
||
<div className="insert-code">
|
||
{InsertCodeMap.map(item => {
|
||
return (
|
||
<div
|
||
style={{ cursor: 'pointer' }}
|
||
className="code-item"
|
||
key={item.title}
|
||
onClick={() => {
|
||
this.handleInsertCode('\n' + item.code);
|
||
}}
|
||
>
|
||
{item.title}
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
</Col>
|
||
</Row>
|
||
|
||
|
||
</div>
|
||
</Modal>
|
||
<Row type="flex" justify="center" align="top">
|
||
<Col span={5}>
|
||
<h2
|
||
className="interface-title"
|
||
style={{
|
||
display: 'inline-block',
|
||
margin: '8px 20px 16px 0px'
|
||
}}
|
||
>
|
||
测试集合 <a
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
href="https://hellosean1025.github.io/yapi/documents/case.html"
|
||
>
|
||
<Tooltip title="点击查看文档">
|
||
<Icon type="question-circle-o" />
|
||
</Tooltip>
|
||
</a>
|
||
</h2>
|
||
</Col>
|
||
<Col span={10}>
|
||
<CaseEnv
|
||
envList={this.props.envList}
|
||
currProjectEnvChange={this.currProjectEnvChange}
|
||
envValue={this.state.currColEnvObj}
|
||
collapseKey={this.state.collapseKey}
|
||
changeClose={this.changeCollapseClose}
|
||
/>
|
||
</Col>
|
||
<Col span={9}>
|
||
{this.state.hasPlugin ? (
|
||
<div
|
||
style={{
|
||
float: 'right',
|
||
paddingTop: '8px'
|
||
}}
|
||
>
|
||
{this.props.curProjectRole !== 'guest' && (
|
||
<Tooltip title="在 YApi 服务端跑自动化测试,测试环境不能为私有网络,请确保 YApi 服务器可以访问到自动化测试环境domain">
|
||
<Button
|
||
style={{
|
||
marginRight: '8px'
|
||
}}
|
||
onClick={this.autoTests}
|
||
>
|
||
服务端测试
|
||
</Button>
|
||
</Tooltip>
|
||
)}
|
||
<Button onClick={this.openCommonSetting} style={{
|
||
marginRight: '8px'
|
||
}} >通用规则配置</Button>
|
||
|
||
<Button type="primary" onClick={this.executeTests}>
|
||
开始测试
|
||
</Button>
|
||
</div>
|
||
) : (
|
||
<Tooltip title="请安装 cross-request Chrome 插件">
|
||
<Button
|
||
disabled
|
||
type="primary"
|
||
style={{
|
||
float: 'right',
|
||
marginTop: '8px'
|
||
}}
|
||
>
|
||
开始测试
|
||
</Button>
|
||
</Tooltip>
|
||
)}
|
||
</Col>
|
||
</Row>
|
||
|
||
<div className="component-label-wrapper">
|
||
<Label onChange={val => this.handleChangeInterfaceCol(val, col_name)} desc={col_desc} />
|
||
</div>
|
||
|
||
<Table.Provider
|
||
components={components}
|
||
columns={resolvedColumns}
|
||
style={{
|
||
width: '100%',
|
||
borderCollapse: 'collapse'
|
||
}}
|
||
>
|
||
<Table.Header
|
||
className="interface-col-table-header"
|
||
headerRows={resolve.headerRows({ columns })}
|
||
/>
|
||
|
||
<Table.Body
|
||
className="interface-col-table-body"
|
||
rows={resolvedRows}
|
||
rowKey="id"
|
||
onRow={this.onRow}
|
||
/>
|
||
</Table.Provider>
|
||
<Modal
|
||
title="测试报告"
|
||
width="900px"
|
||
style={{
|
||
minHeight: '500px'
|
||
}}
|
||
visible={this.state.visible}
|
||
onCancel={this.handleCancel}
|
||
footer={null}
|
||
>
|
||
<CaseReport {...this.reports[this.state.curCaseid]} />
|
||
</Modal>
|
||
|
||
<Modal
|
||
title="自定义测试脚本"
|
||
width="660px"
|
||
style={{
|
||
minHeight: '500px'
|
||
}}
|
||
visible={this.state.advVisible}
|
||
onCancel={this.handleAdvCancel}
|
||
onOk={this.handleAdvOk}
|
||
maskClosable={false}
|
||
>
|
||
<h3>
|
||
是否开启:
|
||
<Switch
|
||
checked={this.state.enableScript}
|
||
onChange={e => this.setState({ enableScript: e })}
|
||
/>
|
||
</h3>
|
||
<AceEditor
|
||
className="case-script"
|
||
data={this.state.curScript}
|
||
onChange={this.handleScriptChange}
|
||
/>
|
||
</Modal>
|
||
{this.state.autoVisible && (
|
||
<Modal
|
||
title="服务端自动化测试"
|
||
width="780px"
|
||
style={{
|
||
minHeight: '500px'
|
||
}}
|
||
visible={this.state.autoVisible}
|
||
onCancel={this.handleAuto}
|
||
className="autoTestsModal"
|
||
footer={null}
|
||
>
|
||
<Row type="flex" justify="space-around" className="row" align="top">
|
||
<Col span={3} className="label" style={{ paddingTop: '16px' }}>
|
||
选择环境
|
||
<Tooltip title="默认使用测试用例选择的环境">
|
||
<Icon type="question-circle-o" />
|
||
</Tooltip>
|
||
:
|
||
</Col>
|
||
<Col span={21}>
|
||
<CaseEnv
|
||
envList={this.props.envList}
|
||
currProjectEnvChange={this.currProjectEnvChange}
|
||
envValue={this.state.currColEnvObj}
|
||
collapseKey={this.state.collapseKey}
|
||
changeClose={this.changeCollapseClose}
|
||
/>
|
||
</Col>
|
||
</Row>
|
||
<Row type="flex" justify="space-around" className="row" align="middle">
|
||
<Col span={3} className="label">
|
||
输出格式:
|
||
</Col>
|
||
<Col span={21}>
|
||
<Select value={this.state.mode} onChange={this.modeChange}>
|
||
<Option key="html" value="html">
|
||
html
|
||
</Option>
|
||
<Option key="json" value="json">
|
||
json
|
||
</Option>
|
||
</Select>
|
||
</Col>
|
||
</Row>
|
||
<Row type="flex" justify="space-around" className="row" align="middle">
|
||
<Col span={3} className="label">
|
||
消息通知
|
||
<Tooltip title={'测试不通过时,会给项目组成员发送消息通知'}>
|
||
<Icon
|
||
type="question-circle-o"
|
||
style={{
|
||
width: '10px'
|
||
}}
|
||
/>
|
||
</Tooltip>
|
||
:
|
||
</Col>
|
||
<Col span={21}>
|
||
<Switch
|
||
checked={this.state.email}
|
||
checkedChildren="开"
|
||
unCheckedChildren="关"
|
||
onChange={this.emailChange}
|
||
/>
|
||
</Col>
|
||
</Row>
|
||
<Row type="flex" justify="space-around" className="row" align="middle">
|
||
<Col span={3} className="label">
|
||
下载数据
|
||
<Tooltip title={'开启后,测试数据将被下载到本地'}>
|
||
<Icon
|
||
type="question-circle-o"
|
||
style={{
|
||
width: '10px'
|
||
}}
|
||
/>
|
||
</Tooltip>
|
||
:
|
||
</Col>
|
||
<Col span={21}>
|
||
<Switch
|
||
checked={this.state.download}
|
||
checkedChildren="开"
|
||
unCheckedChildren="关"
|
||
onChange={this.downloadChange}
|
||
/>
|
||
</Col>
|
||
</Row>
|
||
<Row type="flex" justify="space-around" className="row" align="middle">
|
||
<Col span={21} className="autoTestUrl">
|
||
<a
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
href={localUrl + autoTestsUrl} >
|
||
{autoTestsUrl}
|
||
</a>
|
||
</Col>
|
||
<Col span={3}>
|
||
<Button className="copy-btn" onClick={() => this.copyUrl(localUrl + autoTestsUrl)}>
|
||
复制
|
||
</Button>
|
||
</Col>
|
||
</Row>
|
||
<div className="autoTestMsg">
|
||
注:访问该URL,可以测试所有用例,请确保YApi服务器可以访问到环境配置的 domain
|
||
</div>
|
||
</Modal>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
}
|
||
|
||
export default InterfaceColContent;
|