This is the 9th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

Hello ~ I’m Milo! I am building an open source interface testing platform from 0 to 1, and I am also writing a complete tutorial corresponding to it. I hope you can support it. Welcome to pay attention to my public number test development pit goods, get the latest article tutorial!

review

In the previous section, we wrote a test plan in conjunction with APScheduler, and scheduled tasks now run properly.

In this section, we will briefly talk about how to do the front end. The weather has become cold recently and the blogger has caught a cold. I found many mistakes in the process of writing. If there are mistakes, I hope we can help point out the next =. =

First talk about design

If you follow the usual pattern, put a table showing information about each test plan.

Add and remove, you put them in the Modal dialog. The content of the form can be filled out by the user, and the curd page can be completed quickly.

But think about it. Our test plan is a complex data set. In my mind, he should have these conditions:

  • Multi-condition search

    Search by project, test plan name, priority, creator, and so on. It’s possible to search for something later based on when it was created.

  • Clear steps

    I want to divide this into 3 pieces:

      1. The basic information
      1. Use case data
      1. Notify the configuration

Following this idea, I found antD steps(steps bar), so that users can have a certain order when entering data and improve their experience.

Write the testplan.jsx component

import {PageContainer} from "@ant-design/pro-layout";
import {connect} from 'umi';
import {Badge, Button, Card, Col, Divider, Form, Input, Row, Select, Table, Tag, Tooltip} from "antd";
import React, {useEffect} from "react";
import {CONFIG} from "@/consts/config";
import {PlusOutlined} from "@ant-design/icons";
import TestPlanForm from "@/components/TestCase/TestPlanForm";

const {Option} = Select;

const TestPlan = ({testplan, dispatch, loading, gconfig, user, project}) = > {

  const {planData} = testplan;
  const {userList, userMap} = user;
  const {projectsMap, projects} = project;
  // const {envList} = gconfig;

  const getStatus = record= > {
    if (record.state === 2) {
      return <Tooltip title="The scheduled task may fail to be added. Please try adding it again."><Badge status="error" text="Error"/></Tooltip>
    }
    if (record.state === 3) {
      return <Tooltip title="Mission suspended."><Badge status="warning" text="Suspended"/></Tooltip>
    }
    if (record.state === 1) {
      return <Tooltip title="Mission in progress."><Badge status="processing" text="In execution"/></Tooltip>
    }
    return <Tooltip title={'Next run time:The ${record.next_run} `} >
      <Badge status="success" text="Waiting"/>
    </Tooltip>
  }

  const columns = [
    {
      title: 'the project'.key: 'project_id'.dataIndex: 'project_id'.render: projectId= > <a href={` / #apiTest/project/ ${projectId} `}target="_blank" rel="noreferrer">{projectsMap[projectId] || 'loading'}</a>
    },
    {
      title: 'Test plan'.key: 'name'.dataIndex: 'name'
    },
    {
      title: 'Priority'.key: 'priority'.dataIndex: 'priority'.render: priority= > <Tag color={CONFIG.CASE_TAG[priority]}>{priority}</Tag>
    },
    {
      title: 'Cron expression'.key: 'cron'.dataIndex: 'cron'}, {title: 'Sequential execution'.key: 'ordered'.dataIndex: 'ordered'.render: bool= > bool ? <Tag color="blue">is</Tag> : <Tag>no</Tag>
    },
    {
      title: 'Number of use Cases'.key: 'case_list'.dataIndex: 'case_list'.render: caseList= > caseList.split(",").length,
    },
    {
      title: 'state'.key: 'next_run'.dataIndex: 'next_run'.render: (_, record) = > getStatus(record)
    },
    {
      title: 'Founder'.key: 'create_user'.dataIndex: 'create_user'.render: create_user= >userMap[create_user] ! = =undefined ? userMap[create_user].name : 'Loading... '
    },
    {
      title: 'operation'.key: 'ops'.render: () = > <>
        <a>The editor</a>
        <Divider type="vertical"/>
        <a>delete</a>
      </>},]// form query criteria
  const [form] = Form.useForm();

  const spin = loading.effects['testplan/listTestPlan'] || loading.effects['project/listProject']

  const fetchTestPlan = () = > {
    const values = form.getFieldsValue();
    dispatch({
      type: 'testplan/listTestPlan'.payload: {
        page: 1.size: 10. values, } }) }const fetchProjectList = () = > {
    dispatch({
      type: 'project/listProject'})},const fetchUsers = () = > {
    if (userList.length === 0) {
      dispatch({
        type: 'user/fetchUserList',}}}const fetchEnvList = () = > {
    dispatch({
      type: 'gconfig/fetchEnvList'.payload: {
        page: 1.size: 1000.exactly: true // Get all}})}const onSave = data= > {
    dispatch({
      type: 'testplan/save'.payload: data
    })
  }

  useEffect(() = > {
    fetchEnvList()
    fetchUsers()
    fetchProjectList()
    fetchTestPlan()
  }, [])

  return (
    <PageContainer title={false}>
      <Card>
        <TestPlanForm/>
        <Form form={form} {. CONFIG.LAYOUT} onValuesChange={()= >{ fetchTestPlan(); }} ><Row gutter={[12, 12]} >
            <Col span={6}>
              <Form.Item label="Project" name="project_id">
                <Select allowClear showSearch placeholder="Select items">
                  {projects.map(item => <Option value={item.id} key={item.id}>{item.name}</Option>)}
                </Select>
              </Form.Item>
            </Col>
            <Col span={6}>
              <Form.Item label="Name" name="name">
                <Input placeholder="Enter test plan name"/>
              </Form.Item>
            </Col>
            <Col span={6}>
              <Form.Item label="Priority" name="priority">
                <Select placeholder="Select priority" allowClear>
                  {CONFIG.PRIORITY.map(v => <Option key={v} value={v}>{v}</Option>)}
                </Select>
              </Form.Item>
            </Col>
            <Col span={6}>
              <Form.Item label="Founder" name="state">
                <Select placeholder="Choose the founder" showSearch allowClear>
                  {userList.map(item => <Option key={item.id} value={item.id}>{item.name}</Option>)}
                </Select>
              </Form.Item>
            </Col>
          </Row>
        </Form>
        <Row style={{marginBottom: 12}} >
          <Button type="primary" onClick={()= >{onSave({visible: true, title: 'New test plan '})}}><PlusOutlined/>Add the plan</Button>
        </Row>
        <Table columns={columns} dataSource={planData} rowKey={row= > row.id} loading={spin}/>
      </Card>
    </PageContainer>)}export default connect(({testplan, project, user, loading, gconfig}) = > ({
  testplan,
  project,
  loading,
  user,
  gconfig,
}))(TestPlan);

Copy the code

The core of the code is to manage state through dVA’s Model-service class redux.

Get test plan information with the latest useEffect.

Place the search option in the form and set the onValuesChange method on the form to automatically re-query the test plan when the data changes. This avoids a human click on the search button again.

The renderings are as follows:

Note that most of the data is presented here, but if you want to see more, you need to click the Edit button.

Write and create test plan form components

This component is still a Modal, according to the 3 fields we just said to allocate the corresponding plate, the general style is as follows:

Since the selection of test cases has not yet been determined, we still need to design slowly for the time being. Personally feel their front part to write up or slow, there are a lot of holes in front of the fill.

Let’s stop here for today’s content. In the next section, we will demonstrate the test plan page and solve the problem of repeated execution of single task of APScheduler under multiple workers.

Test. Pity. Fun