mirror of
https://github.com/YMFE/yapi.git
synced 2024-11-27 04:40:08 +08:00
feat: 支持导入不同项目测试用例
This commit is contained in:
parent
854badbedc
commit
be4e449190
@ -3,7 +3,12 @@ import PropTypes from 'prop-types'
|
||||
import { Row, Col, Tabs } from 'antd'
|
||||
const TabPane = Tabs.TabPane;
|
||||
function json_format(json) {
|
||||
return JSON.stringify(json, null, ' ')
|
||||
// console.log('json',json)
|
||||
if(json && typeof json === 'object'){
|
||||
return JSON.stringify(json, null, ' ')
|
||||
}
|
||||
return json;
|
||||
|
||||
}
|
||||
|
||||
const CaseReport = function (props) {
|
||||
|
@ -1,49 +1,85 @@
|
||||
import React, { PureComponent as Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React, { PureComponent as Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
// import { connect } from 'react-redux'
|
||||
import { Table } from 'antd'
|
||||
import { Table, Select } from 'antd';
|
||||
import variable from '../../../../constants/variable';
|
||||
import { connect } from 'react-redux';
|
||||
const Option = Select.Option;
|
||||
import { fetchInterfaceListMenu } from '../../../../reducer/modules/interface.js';
|
||||
|
||||
@connect(
|
||||
state => {
|
||||
return {
|
||||
projectList: state.project.projectList,
|
||||
list: state.inter.list
|
||||
};
|
||||
},
|
||||
{
|
||||
fetchInterfaceListMenu
|
||||
}
|
||||
)
|
||||
export default class ImportInterface extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
super(props);
|
||||
}
|
||||
|
||||
state = {
|
||||
selectedRowKeys: [],
|
||||
categoryCount: {}
|
||||
}
|
||||
categoryCount: {},
|
||||
project: this.props.currProjectId
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
list: PropTypes.array,
|
||||
onChange: PropTypes.func
|
||||
selectInterface: PropTypes.func,
|
||||
projectList: PropTypes.array,
|
||||
currProjectId: PropTypes.string,
|
||||
fetchInterfaceListMenu: PropTypes.func
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
// console.log(this.props.currProjectId)
|
||||
await this.props.fetchInterfaceListMenu(this.props.currProjectId);
|
||||
}
|
||||
|
||||
// 切换项目
|
||||
onChange = async val => {
|
||||
this.setState({
|
||||
project: val,
|
||||
selectedRowKeys: [],
|
||||
categoryCount: {}
|
||||
});
|
||||
await this.props.fetchInterfaceListMenu(val);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { list } = this.props;
|
||||
const { list, projectList } = this.props;
|
||||
|
||||
// const { selectedRowKeys } = this.state;
|
||||
const data = list.map(item => {
|
||||
return {
|
||||
key: 'category_' + item._id,
|
||||
title: item.name,
|
||||
isCategory: true,
|
||||
children: item.list ? item.list.map(e => {
|
||||
e.key = e._id
|
||||
e.categoryKey = 'category_' + item._id
|
||||
e.categoryLength = item.list.length
|
||||
return e
|
||||
}) : []
|
||||
}
|
||||
children: item.list
|
||||
? item.list.map(e => {
|
||||
e.key = e._id;
|
||||
e.categoryKey = 'category_' + item._id;
|
||||
e.categoryLength = item.list.length;
|
||||
return e;
|
||||
})
|
||||
: []
|
||||
};
|
||||
});
|
||||
const self = this;
|
||||
const rowSelection = {
|
||||
// onChange: (selectedRowKeys) => {
|
||||
// console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
|
||||
// if (selectedRows.isCategory) {
|
||||
// const selectedRowKeys = selectedRows.children.map(item => item._id)
|
||||
// this.setState({ selectedRowKeys })
|
||||
// }
|
||||
// this.props.onChange(selectedRowKeys.filter(id => ('' + id).indexOf('category') === -1));
|
||||
// console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
|
||||
// if (selectedRows.isCategory) {
|
||||
// const selectedRowKeys = selectedRows.children.map(item => item._id)
|
||||
// this.setState({ selectedRowKeys })
|
||||
// }
|
||||
// this.props.onChange(selectedRowKeys.filter(id => ('' + id).indexOf('category') === -1));
|
||||
// },
|
||||
onSelect: (record, selected) => {
|
||||
// console.log(record, selected, selectedRows);
|
||||
@ -53,85 +89,137 @@ export default class ImportInterface extends Component {
|
||||
const categoryLength = record.categoryLength;
|
||||
let selectedRowKeys = [];
|
||||
if (record.isCategory) {
|
||||
selectedRowKeys = record.children.map(item => item._id).concat(record.key)
|
||||
selectedRowKeys = record.children.map(item => item._id).concat(record.key);
|
||||
if (selected) {
|
||||
selectedRowKeys = selectedRowKeys.filter(id => oldSelecteds.indexOf(id) === -1).concat(oldSelecteds)
|
||||
selectedRowKeys = selectedRowKeys
|
||||
.filter(id => oldSelecteds.indexOf(id) === -1)
|
||||
.concat(oldSelecteds);
|
||||
categoryCount[categoryKey] = categoryLength;
|
||||
} else {
|
||||
selectedRowKeys = oldSelecteds.filter(id => selectedRowKeys.indexOf(id) === -1)
|
||||
selectedRowKeys = oldSelecteds.filter(id => selectedRowKeys.indexOf(id) === -1);
|
||||
categoryCount[categoryKey] = 0;
|
||||
}
|
||||
} else {
|
||||
if (selected) {
|
||||
selectedRowKeys = oldSelecteds.concat(record._id)
|
||||
selectedRowKeys = oldSelecteds.concat(record._id);
|
||||
if (categoryCount[categoryKey]) {
|
||||
categoryCount[categoryKey] += 1;
|
||||
} else {
|
||||
categoryCount[categoryKey] = 1;
|
||||
}
|
||||
if (categoryCount[categoryKey] === record.categoryLength) {
|
||||
selectedRowKeys.push(categoryKey)
|
||||
selectedRowKeys.push(categoryKey);
|
||||
}
|
||||
} else {
|
||||
selectedRowKeys = oldSelecteds.filter(id => id !== record._id)
|
||||
selectedRowKeys = oldSelecteds.filter(id => id !== record._id);
|
||||
if (categoryCount[categoryKey]) {
|
||||
categoryCount[categoryKey] -= 1;
|
||||
}
|
||||
selectedRowKeys = selectedRowKeys.filter(id => id !== categoryKey)
|
||||
selectedRowKeys = selectedRowKeys.filter(id => id !== categoryKey);
|
||||
}
|
||||
}
|
||||
self.setState({ selectedRowKeys, categoryCount })
|
||||
self.props.onChange(selectedRowKeys.filter(id => ('' + id).indexOf('category') === -1));
|
||||
self.setState({ selectedRowKeys, categoryCount });
|
||||
self.props.selectInterface(
|
||||
selectedRowKeys.filter(id => ('' + id).indexOf('category') === -1),
|
||||
self.state.project
|
||||
);
|
||||
},
|
||||
onSelectAll: (selected) => {
|
||||
onSelectAll: selected => {
|
||||
// console.log(selected, selectedRows, changeRows);
|
||||
let selectedRowKeys = [];
|
||||
let categoryCount = self.state.categoryCount;
|
||||
if (selected) {
|
||||
data.forEach(item => {
|
||||
if(item.children) {
|
||||
if (item.children) {
|
||||
categoryCount['category_' + item._id] = item.children.length;
|
||||
selectedRowKeys = selectedRowKeys.concat(item.children.map(item => item._id))
|
||||
selectedRowKeys = selectedRowKeys.concat(item.children.map(item => item._id));
|
||||
}
|
||||
});
|
||||
selectedRowKeys = selectedRowKeys.concat(data.map(item => item.key))
|
||||
selectedRowKeys = selectedRowKeys.concat(data.map(item => item.key));
|
||||
} else {
|
||||
categoryCount = {};
|
||||
selectedRowKeys = [];
|
||||
}
|
||||
self.setState({ selectedRowKeys, categoryCount })
|
||||
self.props.onChange(selectedRowKeys.filter(id => ('' + id).indexOf('category') === -1));
|
||||
self.setState({ selectedRowKeys, categoryCount });
|
||||
self.props.selectInterface(
|
||||
selectedRowKeys.filter(id => ('' + id).indexOf('category') === -1),
|
||||
self.state.project
|
||||
);
|
||||
},
|
||||
selectedRowKeys: self.state.selectedRowKeys
|
||||
};
|
||||
|
||||
const columns = [{
|
||||
title: '接口名称',
|
||||
dataIndex: 'title',
|
||||
width: '30%'
|
||||
}, {
|
||||
title: '接口路径',
|
||||
dataIndex: 'path',
|
||||
width: '40%'
|
||||
}, {
|
||||
title: '请求方法',
|
||||
dataIndex: 'method',
|
||||
render: (item) => {
|
||||
let methodColor = variable.METHOD_COLOR[item ? item.toLowerCase() : 'get'];
|
||||
return <span style={{color: methodColor.color, backgroundColor: methodColor.bac, borderRadius: 4}} className="colValue">{item}</span>
|
||||
const columns = [
|
||||
{
|
||||
title: '接口名称',
|
||||
dataIndex: 'title',
|
||||
width: '30%'
|
||||
},
|
||||
{
|
||||
title: '接口路径',
|
||||
dataIndex: 'path',
|
||||
width: '40%'
|
||||
},
|
||||
{
|
||||
title: '请求方法',
|
||||
dataIndex: 'method',
|
||||
render: item => {
|
||||
let methodColor = variable.METHOD_COLOR[item ? item.toLowerCase() : 'get'];
|
||||
return (
|
||||
<span
|
||||
style={{
|
||||
color: methodColor.color,
|
||||
backgroundColor: methodColor.bac,
|
||||
borderRadius: 4
|
||||
}}
|
||||
className="colValue"
|
||||
>
|
||||
{item}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
render: text => {
|
||||
return (
|
||||
text &&
|
||||
(text === 'done' ? (
|
||||
<span className="tag-status done">已完成</span>
|
||||
) : (
|
||||
<span className="tag-status undone">未完成</span>
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
render: (text) => {
|
||||
return text && (text === 'done' ? <span className="tag-status done">已完成</span> : <span className="tag-status undone">未完成</span>)
|
||||
}
|
||||
}];
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* <RadioGroup onChange={this.onChange} value={this.state.project}>
|
||||
{projectList.map(item => {
|
||||
return (
|
||||
<Radio value={`${item._id}`} key={item._id}>
|
||||
{item.name}
|
||||
</Radio>
|
||||
);
|
||||
})}
|
||||
</RadioGroup> */}
|
||||
<div className="select-project">
|
||||
<span>选择要导入的项目: </span>
|
||||
<Select value={this.state.project} style={{ width: 200 }} onChange={this.onChange}>
|
||||
{projectList.map(item => {
|
||||
return (
|
||||
<Option value={`${item._id}`} key={item._id}>
|
||||
{item.name}
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</div>
|
||||
<Table columns={columns} rowSelection={rowSelection} dataSource={data} pagination={false} />
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -171,7 +171,7 @@ export default class InterfaceCaseContent extends Component {
|
||||
after_script: currProject.after_script
|
||||
}, { _id: currCase._id });
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div style={{ padding: '6px 0' }} className="case-content">
|
||||
<div className="case-title">
|
||||
@ -183,7 +183,7 @@ export default class InterfaceCaseContent extends Component {
|
||||
<Input value={editCasename} onChange={e => this.setState({ editCasename: e.target.value })} style={{ fontSize: 18 }} />
|
||||
</div>}
|
||||
<span className="inter-link" style={{ margin: '0px 8px 0px 6px', fontSize: 12 }}>
|
||||
<Link className="text" to={`/project/${currProject._id}/interface/api/${currCase.interface_id}`}>对应接口</Link>
|
||||
<Link className="text" to={`/project/${currCase.project_id}/interface/api/${currCase.interface_id}`}>对应接口</Link>
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
|
@ -474,7 +474,8 @@ class InterfaceColContent extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
// console.log('rows',this.state.rows);
|
||||
// console.log('rows',this.props.currProject);
|
||||
const currProjectId = this.props.currProject._id
|
||||
const columns = [{
|
||||
property: 'casename',
|
||||
header: {
|
||||
@ -489,7 +490,7 @@ class InterfaceColContent extends Component {
|
||||
formatters: [
|
||||
(text, { rowData }) => {
|
||||
let record = rowData;
|
||||
return <Link to={"/project/" + record.project_id + "/interface/case/" + record._id}>{record.casename.length > 23 ? record.casename.substr(0, 20) + '...' : record.casename}</Link>
|
||||
return <Link to={"/project/" + currProjectId + "/interface/case/" + record._id}>{record.casename.length > 23 ? record.casename.substr(0, 20) + '...' : record.casename}</Link>
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -3,7 +3,8 @@ import { connect } from 'react-redux';
|
||||
import { withRouter } from 'react-router'
|
||||
import PropTypes from 'prop-types'
|
||||
import { fetchInterfaceColList, fetchInterfaceCaseList, setColData, fetchCaseList } from '../../../../reducer/modules/interfaceCol'
|
||||
import { fetchInterfaceListMenu } from '../../../../reducer/modules/interface.js';
|
||||
// import { fetchInterfaceListMenu } from '../../../../reducer/modules/interface.js';
|
||||
import { fetchProjectList } from '../../../../reducer/modules/project'
|
||||
import axios from 'axios';
|
||||
// import { Input, Icon, Button, Modal, message, Tooltip, Tree, Dropdown, Menu, Form } from 'antd';
|
||||
import ImportInterface from './ImportInterface'
|
||||
@ -52,15 +53,19 @@ const ColModalForm = Form.create()((props) => {
|
||||
currCase: state.interfaceCol.currCase,
|
||||
isRander: state.interfaceCol.isRander,
|
||||
currCaseId: state.interfaceCol.currCaseId,
|
||||
list: state.inter.list
|
||||
// list: state.inter.list,
|
||||
// 当前项目的信息
|
||||
curProject: state.project.currProject
|
||||
// projectList: state.project.projectList
|
||||
}
|
||||
},
|
||||
{
|
||||
fetchInterfaceColList,
|
||||
fetchInterfaceCaseList,
|
||||
fetchInterfaceListMenu,
|
||||
// fetchInterfaceListMenu,
|
||||
fetchCaseList,
|
||||
setColData
|
||||
setColData,
|
||||
fetchProjectList
|
||||
}
|
||||
)
|
||||
@withRouter
|
||||
@ -71,15 +76,18 @@ export default class InterfaceColMenu extends Component {
|
||||
interfaceColList: PropTypes.array,
|
||||
fetchInterfaceColList: PropTypes.func,
|
||||
fetchInterfaceCaseList: PropTypes.func,
|
||||
fetchInterfaceListMenu: PropTypes.func,
|
||||
// fetchInterfaceListMenu: PropTypes.func,
|
||||
fetchCaseList: PropTypes.func,
|
||||
setColData: PropTypes.func,
|
||||
currCaseId: PropTypes.number,
|
||||
history: PropTypes.object,
|
||||
isRander: PropTypes.bool,
|
||||
list: PropTypes.array,
|
||||
// list: PropTypes.array,
|
||||
router: PropTypes.object,
|
||||
currCase: PropTypes.object
|
||||
currCase: PropTypes.object,
|
||||
curProject: PropTypes.object,
|
||||
fetchProjectList: PropTypes.func
|
||||
// projectList: PropTypes.array
|
||||
}
|
||||
|
||||
state = {
|
||||
@ -92,7 +100,8 @@ export default class InterfaceColMenu extends Component {
|
||||
importColId: 0,
|
||||
expands: null,
|
||||
list: [],
|
||||
delIcon: null
|
||||
delIcon: null,
|
||||
selectedProject: null
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
@ -279,18 +288,21 @@ export default class InterfaceColMenu extends Component {
|
||||
this.form = form;
|
||||
}
|
||||
|
||||
selectInterface = (importInterIds) => {
|
||||
// console.log(importInterIds)
|
||||
this.setState({ importInterIds })
|
||||
selectInterface = (importInterIds, selectedProject) => {
|
||||
|
||||
this.setState({ importInterIds, selectedProject })
|
||||
}
|
||||
|
||||
showImportInterfaceModal = async (colId) => {
|
||||
const projectId = this.props.match.params.id;
|
||||
await this.props.fetchInterfaceListMenu(projectId)
|
||||
// const projectId = this.props.match.params.id;
|
||||
// console.log('project', this.props.curProject)
|
||||
const groupId = this.props.curProject.group_id
|
||||
await this.props.fetchProjectList(groupId)
|
||||
// await this.props.fetchInterfaceListMenu(projectId)
|
||||
this.setState({ importInterVisible: true, importColId: colId })
|
||||
}
|
||||
handleImportOk = async () => {
|
||||
const project_id = this.props.match.params.id;
|
||||
const project_id = this.state.selectedProject || this.props.match.params.id;
|
||||
const { importColId, importInterIds } = this.state;
|
||||
const res = await axios.post('/api/col/add_case_list', {
|
||||
interface_list: importInterIds,
|
||||
@ -367,7 +379,7 @@ export default class InterfaceColMenu extends Component {
|
||||
render() {
|
||||
// const { currColId, currCaseId, isShowCol } = this.props;
|
||||
const { colModalType, colModalVisible, importInterVisible } = this.state;
|
||||
|
||||
const currProjectId = this.props.match.params.id;
|
||||
// const menu = (col) => {
|
||||
// return (
|
||||
// <Menu>
|
||||
@ -540,7 +552,7 @@ export default class InterfaceColMenu extends Component {
|
||||
className="import-case-modal"
|
||||
width={800}
|
||||
>
|
||||
<ImportInterface onChange={this.selectInterface} list={this.props.list} />
|
||||
<ImportInterface currProjectId={currProjectId} selectInterface={this.selectInterface} />
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
|
@ -137,6 +137,10 @@
|
||||
max-height: 800px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.select-project {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.autoTestsModal{
|
||||
|
@ -55,6 +55,22 @@ export default class Project extends Component {
|
||||
}]);
|
||||
}
|
||||
|
||||
async componentWillReceiveProps(nextProps) {
|
||||
const currProjectId = this.props.match.params.id;
|
||||
const nextProjectId = nextProps.match.params.id;
|
||||
if(currProjectId !== nextProjectId) {
|
||||
await this.props.getProject(nextProjectId);
|
||||
|
||||
this.props.setBreadcrumb([{
|
||||
name: this.props.currGroup.group_name,
|
||||
href: '/group/' + this.props.currGroup._id
|
||||
}, {
|
||||
name: this.props.curProject.name
|
||||
}]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
const { match, location } = this.props;
|
||||
let routers = {
|
||||
|
@ -9,7 +9,7 @@ const PROJECT_DEL = 'yapi/project/PROJECT_DEL';
|
||||
const PROJECT_UPDATE = 'yapi/project/PROJECT_UPDATE';
|
||||
const PROJECT_UPDATE_ENV = 'yapi/project/PROJECT_UPDATE_ENV';
|
||||
const PROJECT_UPSET = 'yapi/project/PROJECT_UPSET';
|
||||
const GET_CURR_PROJECT = 'yapi/project/GET_CURR_PROJECT'
|
||||
const GET_CURR_PROJECT = 'yapi/project/GET_CURR_PROJECT';
|
||||
const GET_PEOJECT_MEMBER = 'yapi/project/GET_PEOJECT_MEMBER';
|
||||
const ADD_PROJECT_MEMBER = 'yapi/project/ADD_PROJECT_MEMBER';
|
||||
const DEL_PROJECT_MEMBER = 'yapi/project/DEL_PROJECT_MEMBER';
|
||||
@ -19,7 +19,6 @@ const UPDATE_TOKEN = 'yapi/project/UPDATE_TOKEN';
|
||||
const CHECK_PROJECT_NAME = 'yapi/project/CHECK_PROJECT_NAME';
|
||||
const COPY_PROJECT_MSG = 'yapi/project/COPY_PROJECT_MSG';
|
||||
|
||||
|
||||
// Reducer
|
||||
const initialState = {
|
||||
isUpdateModalShow: false,
|
||||
@ -31,18 +30,16 @@ const initialState = {
|
||||
total: 0,
|
||||
currPage: 1,
|
||||
token: '',
|
||||
currProject: {
|
||||
}
|
||||
currProject: {}
|
||||
};
|
||||
|
||||
export default (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case GET_CURR_PROJECT: {
|
||||
|
||||
return {
|
||||
...state,
|
||||
currProject: action.payload.data.data
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
case FETCH_PROJECT_LIST: {
|
||||
@ -65,25 +62,24 @@ export default (state = initialState, action) => {
|
||||
return {
|
||||
...state,
|
||||
token: action.payload.data.data
|
||||
}
|
||||
};
|
||||
}
|
||||
case UPDATE_TOKEN: {
|
||||
return {
|
||||
...state,
|
||||
token: action.payload.data.data.token
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
case CHECK_PROJECT_NAME: {
|
||||
return{
|
||||
return {
|
||||
...state
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
case COPY_PROJECT_MSG: {
|
||||
return {
|
||||
...state
|
||||
}
|
||||
};
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
@ -117,7 +113,7 @@ export function addMember(param) {
|
||||
return {
|
||||
type: ADD_PROJECT_MEMBER,
|
||||
payload: axios.post('/api/project/add_member', param)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 删除项目成员
|
||||
@ -125,7 +121,7 @@ export function delMember(param) {
|
||||
return {
|
||||
type: DEL_PROJECT_MEMBER,
|
||||
payload: axios.post('/api/project/del_member', param)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 修改项目成员权限
|
||||
@ -133,7 +129,7 @@ export function changeMemberRole(param) {
|
||||
return {
|
||||
type: CHANGE_PROJECT_MEMBER,
|
||||
payload: axios.post('/api/project/change_member_role', param)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 获取项目成员列表
|
||||
@ -143,7 +139,7 @@ export function getProjectMemberList(id) {
|
||||
payload: axios.get('/api/project/get_member_list', {
|
||||
params: { id }
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// export function changeTableLoading(data) {
|
||||
@ -154,7 +150,18 @@ export function getProjectMemberList(id) {
|
||||
// }
|
||||
|
||||
export function addProject(data) {
|
||||
const { name, prd_host, basepath, desc, group_id, group_name, protocol, icon, color, project_type } = data;
|
||||
const {
|
||||
name,
|
||||
prd_host,
|
||||
basepath,
|
||||
desc,
|
||||
group_id,
|
||||
group_name,
|
||||
protocol,
|
||||
icon,
|
||||
color,
|
||||
project_type
|
||||
} = data;
|
||||
const param = {
|
||||
name,
|
||||
prd_host,
|
||||
@ -221,7 +228,6 @@ export function upsetProject(param) {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// 删除项目
|
||||
export function delProject(id) {
|
||||
const param = { id };
|
||||
@ -236,34 +242,32 @@ export async function getProject(id) {
|
||||
return {
|
||||
type: GET_CURR_PROJECT,
|
||||
payload: result
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export async function getToken(project_id){
|
||||
export async function getToken(project_id) {
|
||||
return {
|
||||
type: GET_TOKEN,
|
||||
payload: axios.get('/api/project/token', {
|
||||
params: { project_id }
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export async function updateToken(project_id){
|
||||
export async function updateToken(project_id) {
|
||||
return {
|
||||
type: UPDATE_TOKEN,
|
||||
payload: axios.get('/api/project/update_token', {
|
||||
params: { project_id }
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export async function checkProjectName(name, group_id){
|
||||
export async function checkProjectName(name, group_id) {
|
||||
return {
|
||||
type: CHECK_PROJECT_NAME,
|
||||
payload: axios.get('/api/project/check_project_name', {
|
||||
params: { name, group_id }
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
const { isJson5, json_parse, handleJson, joinPath, safeArray } = require('./utils')
|
||||
const constants = require('../client/constants/variable.js')
|
||||
const _ = require("underscore")
|
||||
const URL = require('url')
|
||||
const { isJson5, json_parse, handleJson, joinPath, safeArray } = require('./utils');
|
||||
const constants = require('../client/constants/variable.js');
|
||||
const _ = require('underscore');
|
||||
const URL = require('url');
|
||||
const utils = require('./power-string.js').utils;
|
||||
const HTTP_METHOD = constants.HTTP_METHOD;
|
||||
const axios = require('axios');
|
||||
@ -11,19 +11,21 @@ const isNode = typeof global == 'object' && global.global === global;
|
||||
const ContentTypeMap = {
|
||||
'application/json': 'json',
|
||||
'application/xml': 'xml',
|
||||
'other': 'text',
|
||||
other: 'text',
|
||||
'application/html': 'html'
|
||||
}
|
||||
};
|
||||
|
||||
async function httpRequestByNode(options) {
|
||||
function handleRes(response){
|
||||
if(!response || typeof response !== 'object'){
|
||||
function handleRes(response) {
|
||||
if (!response || typeof response !== 'object') {
|
||||
return {
|
||||
res: {
|
||||
status: 500,
|
||||
body: isNode ? '请求出错, 内网服务器自动化测试无法访问到,请检查是否为内网服务器!': '请求出错'
|
||||
body: isNode
|
||||
? '请求出错, 内网服务器自动化测试无法访问到,请检查是否为内网服务器!'
|
||||
: '请求出错'
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
res: {
|
||||
@ -31,47 +33,54 @@ async function httpRequestByNode(options) {
|
||||
status: response.status,
|
||||
body: response.data
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function handleData(){
|
||||
function handleData() {
|
||||
let contentTypeItem;
|
||||
if(!options) return;
|
||||
if(typeof options.headers === 'object' && options.headers ){
|
||||
if (!options) return;
|
||||
if (typeof options.headers === 'object' && options.headers) {
|
||||
Object.keys(options.headers).forEach(key => {
|
||||
if (/content-type/i.test(key)) {
|
||||
if(options.headers[key]){
|
||||
contentTypeItem = options.headers[key].split(";")[0].trim().toLowerCase();
|
||||
}
|
||||
if (options.headers[key]) {
|
||||
contentTypeItem = options.headers[key]
|
||||
.split(';')[0]
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
}
|
||||
}
|
||||
if(!options.headers[key]) delete options.headers[key];
|
||||
})
|
||||
if (!options.headers[key]) delete options.headers[key];
|
||||
});
|
||||
|
||||
if(contentTypeItem === 'application/x-www-form-urlencoded' && typeof options.data === 'object' && options.data){
|
||||
options.data = qs.stringify(options.data);
|
||||
if (
|
||||
contentTypeItem === 'application/x-www-form-urlencoded' &&
|
||||
typeof options.data === 'object' &&
|
||||
options.data
|
||||
) {
|
||||
options.data = qs.stringify(options.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try{
|
||||
|
||||
try {
|
||||
handleData(options);
|
||||
let response=await axios({
|
||||
let response = await axios({
|
||||
method: options.method,
|
||||
url: options.url,
|
||||
headers: options.headers,
|
||||
timeout: 5000,
|
||||
data: options.data
|
||||
})
|
||||
return handleRes(response)
|
||||
}catch(err){
|
||||
if(err.response === undefined){
|
||||
});
|
||||
return handleRes(response);
|
||||
} catch (err) {
|
||||
if (err.response === undefined) {
|
||||
handleRes({
|
||||
headers: {},
|
||||
status: null,
|
||||
data: err.message
|
||||
})
|
||||
});
|
||||
}
|
||||
return handleRes(err.response)
|
||||
return handleRes(err.response);
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,18 +90,25 @@ function handleContentType(headers) {
|
||||
try {
|
||||
Object.keys(headers).forEach(key => {
|
||||
if (/content-type/i.test(key)) {
|
||||
contentTypeItem = headers[key].split(";")[0].trim().toLowerCase();
|
||||
contentTypeItem = headers[key]
|
||||
.split(';')[0]
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
}
|
||||
})
|
||||
});
|
||||
return ContentTypeMap[contentTypeItem] ? ContentTypeMap[contentTypeItem] : ContentTypeMap.other;
|
||||
} catch (err) {
|
||||
return ContentTypeMap.other
|
||||
return ContentTypeMap.other;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function checkRequestBodyIsRaw(method, reqBodyType) {
|
||||
if (reqBodyType && reqBodyType !== 'file' && reqBodyType !== 'form' && HTTP_METHOD[method].request_body) {
|
||||
if (
|
||||
reqBodyType &&
|
||||
reqBodyType !== 'file' &&
|
||||
reqBodyType !== 'form' &&
|
||||
HTTP_METHOD[method].request_body
|
||||
) {
|
||||
return reqBodyType;
|
||||
}
|
||||
return false;
|
||||
@ -103,14 +119,13 @@ function checkNameIsExistInArray(name, arr) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
let item = arr[i];
|
||||
if (item.name === name) {
|
||||
isRepeat = true
|
||||
isRepeat = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return isRepeat;
|
||||
}
|
||||
|
||||
|
||||
function handleCurrDomain(domains, case_env) {
|
||||
let currDomain = _.find(domains, item => item.name === case_env);
|
||||
if (!currDomain) {
|
||||
@ -119,7 +134,7 @@ function handleCurrDomain(domains, case_env) {
|
||||
return currDomain;
|
||||
}
|
||||
|
||||
function sandboxByNode(sandbox={}, script){
|
||||
function sandboxByNode(sandbox = {}, script) {
|
||||
const vm = require('vm');
|
||||
script = new vm.Script(script);
|
||||
const context = new vm.createContext(sandbox);
|
||||
@ -129,35 +144,32 @@ function sandboxByNode(sandbox={}, script){
|
||||
return sandbox;
|
||||
}
|
||||
|
||||
async function sandbox(context={}, script){
|
||||
|
||||
if(isNode){
|
||||
try{
|
||||
async function sandbox(context = {}, script) {
|
||||
if (isNode) {
|
||||
try {
|
||||
context.context = context;
|
||||
context.console = console;
|
||||
context.Promise = Promise;
|
||||
context.setTimeout = setTimeout;
|
||||
context = sandboxByNode(context, script)
|
||||
}catch(err){
|
||||
context = sandboxByNode(context, script);
|
||||
} catch (err) {
|
||||
err.message = `Script: ${script}
|
||||
message: ${err.message}`
|
||||
message: ${err.message}`;
|
||||
throw err;
|
||||
}
|
||||
}else{
|
||||
context = sandboxByBrowser(context, script)
|
||||
} else {
|
||||
context = sandboxByBrowser(context, script);
|
||||
}
|
||||
if(context.promise && typeof context.promise === 'object' && context.promise.then){
|
||||
try{
|
||||
await context.promise
|
||||
}catch(err){
|
||||
if (context.promise && typeof context.promise === 'object' && context.promise.then) {
|
||||
try {
|
||||
await context.promise;
|
||||
} catch (err) {
|
||||
err.message = `Script: ${script}
|
||||
message: ${err.message}`
|
||||
message: ${err.message}`;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
return context;
|
||||
|
||||
|
||||
}
|
||||
|
||||
function sandboxByBrowser(context = {}, script) {
|
||||
@ -171,40 +183,60 @@ function sandboxByBrowser(context = {}, script) {
|
||||
try {
|
||||
eval(beginScript + script);
|
||||
} catch (err) {
|
||||
console.log('----CodeBegin----: ')
|
||||
console.log(beginScript + script)
|
||||
console.log('----CodeEnd----')
|
||||
console.log(err);
|
||||
return context;
|
||||
let message = `Script:
|
||||
----CodeBegin----:
|
||||
${beginScript}
|
||||
${script}
|
||||
----CodeEnd----
|
||||
`;
|
||||
err.message = `Script: ${message}
|
||||
message: ${err.message}`;
|
||||
|
||||
throw err;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
async function crossRequest(defaultOptions, preScript, afterScript) {
|
||||
let options = Object.assign({}, defaultOptions);
|
||||
let urlObj = URL.parse(options.url, true), query = {};
|
||||
let urlObj = URL.parse(options.url, true),
|
||||
query = {};
|
||||
query = Object.assign(query, urlObj.query);
|
||||
|
||||
let context = {
|
||||
get href() {
|
||||
return urlObj.href;
|
||||
},
|
||||
set href(val){
|
||||
throw new Error('context.href 不能被赋值')
|
||||
},
|
||||
get hostname() {
|
||||
return urlObj.hostname;
|
||||
},
|
||||
set hostname(val){
|
||||
throw new Error('context.hostname 不能被赋值')
|
||||
},
|
||||
method: options.method,
|
||||
pathname: urlObj.pathname,
|
||||
query: query,
|
||||
requestHeader: options.headers || {},
|
||||
requestBody: options.data,
|
||||
promise: false,
|
||||
utils: {
|
||||
_: _,
|
||||
base64: utils.base64,
|
||||
md5: utils.md5,
|
||||
sha1: utils.sha1,
|
||||
sha224: utils.sha224,
|
||||
sha256: utils.sha256,
|
||||
sha384: utils.sha384,
|
||||
sha512: utils.sha512,
|
||||
unbase64: utils.unbase64,
|
||||
axios: axios
|
||||
}
|
||||
promise: false
|
||||
};
|
||||
|
||||
context.utils = Object.freeze({
|
||||
_: _,
|
||||
base64: utils.base64,
|
||||
md5: utils.md5,
|
||||
sha1: utils.sha1,
|
||||
sha224: utils.sha224,
|
||||
sha256: utils.sha256,
|
||||
sha384: utils.sha384,
|
||||
sha512: utils.sha512,
|
||||
unbase64: utils.unbase64,
|
||||
axios: axios
|
||||
})
|
||||
|
||||
if (preScript) {
|
||||
context = await sandbox(context, preScript);
|
||||
defaultOptions.url = options.url = URL.format({
|
||||
@ -212,22 +244,21 @@ async function crossRequest(defaultOptions, preScript, afterScript) {
|
||||
host: urlObj.host,
|
||||
query: context.query,
|
||||
pathname: context.pathname
|
||||
})
|
||||
});
|
||||
defaultOptions.headers = options.headers = context.requestHeader;
|
||||
defaultOptions.data = options.data = context.requestBody;
|
||||
|
||||
}
|
||||
|
||||
|
||||
let data;
|
||||
|
||||
if(isNode){
|
||||
data = await httpRequestByNode(options)
|
||||
if (isNode) {
|
||||
data = await httpRequestByNode(options);
|
||||
data.req = options;
|
||||
}else{
|
||||
data = await (new Promise((resolve, reject) => {
|
||||
options.error = options.success = function (res, header, data) {
|
||||
} else {
|
||||
data = await new Promise((resolve, reject) => {
|
||||
options.error = options.success = function(res, header, data) {
|
||||
let message = '';
|
||||
if(res && typeof res === 'string'){
|
||||
if (res && typeof res === 'string') {
|
||||
res = json_parse(data.res.body);
|
||||
data.res.body = res;
|
||||
}
|
||||
@ -237,14 +268,14 @@ async function crossRequest(defaultOptions, preScript, afterScript) {
|
||||
body: res || message,
|
||||
header,
|
||||
message
|
||||
})
|
||||
});
|
||||
}
|
||||
resolve(data);
|
||||
}
|
||||
};
|
||||
window.crossRequest(options);
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (afterScript) {
|
||||
context.responseData = data.res.body;
|
||||
context.responseHeader = data.res.header;
|
||||
@ -259,7 +290,6 @@ async function crossRequest(defaultOptions, preScript, afterScript) {
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
function handleParams(interfaceData, handleValue, requestParams) {
|
||||
function paramsToObjectWithEnable(arr) {
|
||||
const obj = {};
|
||||
@ -270,7 +300,7 @@ function handleParams(interfaceData, handleValue, requestParams) {
|
||||
requestParams[item.name] = obj[item.name];
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -283,13 +313,14 @@ function handleParams(interfaceData, handleValue, requestParams) {
|
||||
requestParams[item.name] = obj[item.name];
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
let { case_env, path, env } = interfaceData;
|
||||
let currDomain, requestBody, requestOptions = {};
|
||||
let currDomain,
|
||||
requestBody,
|
||||
requestOptions = {};
|
||||
|
||||
interfaceData.req_params = interfaceData.req_params || [];
|
||||
interfaceData.req_params.forEach(item => {
|
||||
@ -301,7 +332,6 @@ function handleParams(interfaceData, handleValue, requestParams) {
|
||||
path = path.replace(`{${item.name}}`, val || `{${item.name}}`);
|
||||
});
|
||||
|
||||
|
||||
currDomain = handleCurrDomain(env, case_env);
|
||||
const urlObj = URL.parse(joinPath(currDomain.domain, path), true);
|
||||
const url = URL.format({
|
||||
@ -309,7 +339,6 @@ function handleParams(interfaceData, handleValue, requestParams) {
|
||||
host: urlObj.host,
|
||||
pathname: urlObj.pathname,
|
||||
query: Object.assign(urlObj.query, paramsToObjectWithEnable(interfaceData.req_query))
|
||||
|
||||
});
|
||||
|
||||
requestOptions = {
|
||||
@ -317,13 +346,15 @@ function handleParams(interfaceData, handleValue, requestParams) {
|
||||
method: interfaceData.method,
|
||||
headers: paramsToObjectUnWithEnable(interfaceData.req_headers),
|
||||
timeout: 82400000
|
||||
}
|
||||
};
|
||||
|
||||
if (HTTP_METHOD[interfaceData.method].request_body) {
|
||||
if (interfaceData.req_body_type === 'form') {
|
||||
requestBody = paramsToObjectWithEnable(safeArray(interfaceData.req_body_form).filter(item => {
|
||||
return item.type == 'text'
|
||||
}));
|
||||
requestBody = paramsToObjectWithEnable(
|
||||
safeArray(interfaceData.req_body_form).filter(item => {
|
||||
return item.type == 'text';
|
||||
})
|
||||
);
|
||||
} else if (interfaceData.req_body_type === 'json') {
|
||||
let reqBody = isJson5(interfaceData.req_body_other);
|
||||
if (reqBody === false) {
|
||||
@ -339,16 +370,17 @@ function handleParams(interfaceData, handleValue, requestParams) {
|
||||
}
|
||||
requestOptions.data = requestBody;
|
||||
if (interfaceData.req_body_type === 'form') {
|
||||
requestOptions.files = paramsToObjectWithEnable(safeArray(interfaceData.req_body_form).filter(item => {
|
||||
return item.type == 'file'
|
||||
}))
|
||||
requestOptions.files = paramsToObjectWithEnable(
|
||||
safeArray(interfaceData.req_body_form).filter(item => {
|
||||
return item.type == 'file';
|
||||
})
|
||||
);
|
||||
} else if (interfaceData.req_body_type === 'file') {
|
||||
requestOptions.file = 'single-file'
|
||||
requestOptions.file = 'single-file';
|
||||
}
|
||||
}
|
||||
|
||||
return requestOptions;
|
||||
|
||||
}
|
||||
|
||||
exports.checkRequestBodyIsRaw = checkRequestBodyIsRaw;
|
||||
|
@ -135,7 +135,7 @@ context = {
|
||||
context.responseBody.a = 2;
|
||||
|
||||
```
|
||||
|
||||
> (v1.3.16+新增)context.href和context.hostname
|
||||
### 工具函数
|
||||
```
|
||||
context.utils = {
|
||||
@ -172,6 +172,8 @@ context.promise = new Promise(function(resolve){
|
||||
|
||||
> 处理完成后,不要忘记 `resolve()`,不然会一直处于挂起状态
|
||||
|
||||
|
||||
|
||||
## token配置
|
||||
|
||||
每个项目都有唯一的标识token,用户可以使用这个token值来请求项目的所有资源数据。目前用到的地方是接口的<a href="./case.md">自动化测试</a>,用户不需要登录就可以访问接口测试结果信息。
|
||||
|
@ -59,7 +59,7 @@ class interfaceCase extends baseModel {
|
||||
}
|
||||
|
||||
list(col_id, select) {
|
||||
select = select || 'casename uid col_id _id index interface_id'
|
||||
select = select || 'casename uid col_id _id index interface_id project_id'
|
||||
if (select === 'all') {
|
||||
return this.model.find({
|
||||
col_id: col_id
|
||||
@ -67,7 +67,7 @@ class interfaceCase extends baseModel {
|
||||
}
|
||||
return this.model.find({
|
||||
col_id: col_id
|
||||
}).select("casename uid col_id _id index interface_id").exec();
|
||||
}).select(select).exec();
|
||||
}
|
||||
|
||||
del(id) {
|
||||
|
@ -1,7 +1,10 @@
|
||||
const defaultTheme = require("./defaultTheme.js");
|
||||
|
||||
function json_format(json) {
|
||||
return JSON.stringify(json, null, ' ')
|
||||
if(json && typeof json === 'object'){
|
||||
return JSON.stringify(json, null, ' ')
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
module.exports = function renderToHtml(reports){
|
||||
|
Loading…
Reference in New Issue
Block a user