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 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 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 ( {record.casename.length > 23 ? record.casename.substr(0, 20) + '...' : record.casename} ); } ] } }, { header: { label: 'key', formatters: [ () => { return ( {' '} 每个用例都有唯一的key,用于获取所匹配接口的响应数据,例如使用{' '} {' '} 变量参数{' '} {' '} 功能{' '} } > Key ); } ] }, props: { style: { width: '100px' } }, cell: { formatters: [ (value, { rowData }) => { return {rowData._id}; } ] } }, { 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 (
); } switch (code) { case 0: return (
); case 400: return (
); case 1: return (
); default: return (
); } } ] } }, { property: 'path', header: { label: '接口路径' }, cell: { formatters: [ (text, { rowData }) => { let record = rowData; return ( {record.path.length > 23 ? record.path + '...' : record.path} ); } ] } }, { header: { label: '测试报告' }, props: { style: { width: '200px' } }, cell: { formatters: [ (text, { rowData }) => { let reportFun = () => { if (!this.reports[rowData.id]) { return null; } return ; }; return
{reportFun()}
; } ] } } ]; 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 (
{ let {commonSetting} = this.state; this.setState({ commonSetting :{ ...commonSetting, checkHttpCodeIs200: e } }) }} checked={this.state.commonSetting.checkHttpCodeIs200} checkedChildren="开" unCheckedChildren="关" /> { let {commonSetting} = this.state; this.setState({ commonSetting :{ ...commonSetting, checkResponseSchema: e } }) }} checked={this.state.commonSetting.checkResponseSchema} checkedChildren="开" unCheckedChildren="关" />
{ let {commonSetting} = this.state; this.setState({ commonSetting :{ ...commonSetting, checkScript: { ...this.state.checkScript, enable: e } } }) }} checked={this.state.commonSetting.checkScript.enable} checkedChildren="开" unCheckedChildren="关" />
{ this.aceEditor = aceEditor; }} />
{InsertCodeMap.map(item => { return (
{ this.handleInsertCode('\n' + item.code); }} > {item.title}
); })}

测试集合 

{this.state.hasPlugin ? (
{this.props.curProjectRole !== 'guest' && ( )}  
) : ( )}

是否开启:  this.setState({ enableScript: e })} />

{this.state.autoVisible && ( 选择环境  : 输出格式: 消息通知  : 下载数据  : {autoTestsUrl}
注:访问该URL,可以测试所有用例,请确保YApi服务器可以访问到环境配置的 domain
)}
); } } export default InterfaceColContent;