Django+vue自动化测试平台(7)-- 使用Selenium+vue实现WebUI自动化及结果展示

Selenium

Selenium是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera,Edge等。这个工具的主要功能包括:测试与浏览器的兼容性——测试应用程序看是否能够很好得工作在不同浏览器和操作系统之上。测试系统功能——创建回归测试检验软件功能和用户需求。支持自动录制动作和自动生成.Net、Java、Perl等不同语言的测试脚本。

结果预览:

在这里插入图片描述

在这里插入图片描述

实现使用到框架

django+ selenium + vue

服务端代码

django models配置

class UI_case(models.Model):
    address = models.TextField("启动的页面地址", null=False, default="")
    value = models.JSONField("执行值", null=False, default=dict)
    # value = [{"name": "每个元素的名称", "xpath": "xpath地址", "iframe":"0不是iframe,1是iframe", "event": "事件类型", "value": "输入值" ,"wait_time":" 等待时间"}]
    update_time = models.DateTimeField("更新时间", auto_now=True)
    create_time = models.DateTimeField("创建时间", auto_now_add=True)
    user = models.ForeignKey(Userinfo, on_delete=models.CASCADE)

    def __str__(self):
        return self.value


class UI_case_result(models.Model):
    result = models.TextField("执行结果", null=False, default="")
    result_type = models.IntegerField("结果类型", null=False, default=1)  # 1.执行结果, 2.截图
    create_time = models.DateTimeField("创建时间", auto_now_add=True)
    ui = models.ForeignKey(UI_case, on_delete=models.CASCADE)

    def __str__(self):
        return self.result

服务端逻辑代码

import json
from datetime import datetime
from django.http import JsonResponse
from lapi_app.models.testcase_model.testcase_tree import Menu
from lapi_app.models.ui_model.ui_case import UI_case

# 获取UI自动化的列表信息
def ui_ifo(request):
    global ui_dict, ui_result, update_time, create_time, ui_id, user_name, address
    try:
        data = json.loads(request.body)
        menu_id = data["id"]
        menu = Menu.objects.filter(id=menu_id)
        for i in menu:
            case = UI_case.objects.filter(id=i.ui_id)
            ui_id = i.ui_id
            for j in case:
                address = j.address
                user_name = j.user.account
                update_time, create_time = datetime.strftime(j.create_time, "%Y-%m-%d %H:%M:%S"), datetime.strftime(
                    j.update_time, "%Y-%m-%d %H:%M:%S")
                if j.value:
                    ui_result = j.value
                else:
                    ui_result = []
        return JsonResponse({
            "code": 200,
            "message": "获取用例信息成功",
            "address": address,
            "ui_id": ui_id,
            "content": ui_result,
            "update_time": update_time,
            "create_time": create_time,
            "username": user_name
        })
    except Exception as e:
        return JsonResponse({
            "code": 100,
            "message": str(e)
        })


# 编辑UI自动化的列表信息
def edit_ui_info(request):
    try:
        data = json.loads(request.body)
        id = data["id"]
        value = data["value"]
        UI_case.objects.filter(id=id).update(value=value, address=data["address"], update_time=datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
        return JsonResponse({
            "code": 200,
            "message": "编辑成功"
        })
    except Exception as e:
        return JsonResponse({
            "code": 100,
            "message": str(e)
        })
# 执行UI自动化
def run_ui(request):
    data = json.loads(request.body)
    id = data["id"]
    try:
        if id == "" or data["address"] == "":
            return JsonResponse({"code": 100, "message": "必要参数不可为空"})
        case = UI_case.objects.filter(id=id)
        res = UI_case_result.objects.filter(ui_id=id)

        root = r'xxx/img/'
        for j in res:
            if j.result_type == 2:
                os.remove(os.path.join(root, j.result))
        res.delete()
        # chrome_address = "C:/Program Files/Google/Chrome/Application/chrome.exe"
        # 定义驱动
        driver = webdriver.Chrome()

        # 全屏显示
        driver.maximize_window()

        # 使用驱动 ,打开网页
        driver.get(data["address"])
        for i in case:
            if i.value == "":
                return JsonResponse({"code": 100, "message": "自动化配置不可为空"})
            UI_case_result.objects.create(result="[info]->正在遍历操作步骤", ui_id=id)
            for j in i.value:
                if j["iframe"] == 1 and j["iframe_value"] != "":
                    UI_case_result.objects.create(result="[info]->正在遍历iframe", ui_id=id)
                    try:
                        driver.switch_to.frame(j["iframe_value"])
                        UI_case_result.objects.create(result="[info]->存在这个iframe:" + j["iframe_value"], ui_id=id)
                        driver.switch_to.frame(j["iframe_value"])
                        UI_case_result.objects.create(result="[info]->正在进入iframe:" + j["iframe_value"], ui_id=id)
                        time.sleep(int(j["wait_time"]))
                        UI_case_result.objects.create(result="[info]->设置隐性等待时间:" + str(j["wait_time"]) + "s成功",
                                                      ui_id=id)
                    except Exception as e:
                        UI_case_result.objects.create(result="[error]->没有这个iframe元素,地址为>>" + j["iframe_value"],
                                                      ui_id=id)
                        UI_case_result.objects.create(result="[error]->异常原因" + str(e) + ',元素命为:' + j["xpath"], ui_id=id)

                if j["xpath"] is not None:
                    UI_case_result.objects.create(result="[info]->正在遍历页面元素", ui_id=id)
                    img = save_img(driver)
                    try:
                        xpath = driver.find_element(By.XPATH, j["xpath"])
                        if j["event"] == 1:
                            xpath.click()
                            UI_case_result.objects.create(result="[info]->点击成功>>" + j["name"] + ",xpath地址:" + ':' + j["xpath"],
                                                          ui_id=id)
                            UI_case_result.objects.create(result=img, ui_id=id, result_type=2)
                            time.sleep(int(j["wait_time"]))
                            UI_case_result.objects.create(result="[info]->设置隐性等待时间:" + str(j["wait_time"]) + "s成功",
                                                          ui_id=id)
                        elif j["event"] == 2:
                            xpath.send_keys(j["value"])
                            UI_case_result.objects.create(result="[info]->输入值成功: " + str(j["value"]), ui_id=id)
                            time.sleep(int(j["wait_time"]))
                            UI_case_result.objects.create(result=img, ui_id=id, result_type=2)
                            UI_case_result.objects.create(result="[info]->设置隐性等待时间:" + str(j["wait_time"]) + "s成功", ui_id=id)
                    except Exception as e:
                        UI_case_result.objects.create(result="[error]->没有这个元素名为>>" + str(j["name"]) + ':' + j["xpath"],
                                                      ui_id=id)
                        UI_case_result.objects.create(result="[error]->异常原因" + str(e) + ',元素命为:' + str(j["name"]), ui_id=id)
        time.sleep(5)
        # 关闭浏览器
        driver.quit()
        UI_case_result.objects.create(result="[info]->已关闭浏览器", ui_id=id)
        UI_case_result.objects.create(result="结束", ui_id=id)
        return JsonResponse({
            "code": 200,
            "message": "执行结束"
        })
    except Exception as e:
        UI_case_result.objects.create(result="[error]->异常原因" + str(e), ui_id=id)
        UI_case_result.objects.create(result="结束", ui_id=id)
        return JsonResponse({
            "code": 100,
            "message": str(e)
        })

# 执行过程中对步骤进行截图保存
def save_img(driver):
    now = calendar.timegm(time.gmtime())  # 截图保存的文件名格式
    pic_path = str(now) + ''.join(random.sample(string.ascii_letters+string.digits, 4)) + '_screen.png'  # 截图保存的路径
    # print(pic_path)
    driver.save_screenshot("xxx/img/" + pic_path)  # 调用Driver的截图保存功能
    return pic_path


# 获取执行结果
def get_run_ui_result(request):
    try:
        data = json.loads(request.body)
        if data["time"] == "":
            res = UI_case_result.objects.filter(ui_id=data["id"])
        else:
            res = UI_case_result.objects.filter(ui_id=data["id"], create_time__gt=data["time"])
        result = []
        for i in res:
            i_dict = {
                "id": i.id,
                "result": i.result,
                "timestamp": datetime.strftime(i.create_time, "%Y-%m-%d %H:%M:%S"),
                "type": i.result_type
            }
            result.append(i_dict)
        return JsonResponse({
            "code": 200,
            "content": result
        })
    except Exception as e:
        return JsonResponse({
            "code": 100,
            "message": str(e)
        })

前端逻辑代码

<template>
  <div class="app-container" style="height: 100%; padding-left: 5px !important;">
    <div style="border-radius: 2px; float: left; width: 20%; padding-right:10px;height: 100%;overflow: auto">
      <el-input v-model="filterText" placeholder="请输入要搜索的节点" style="margin-bottom:5px;" />
      <el-tree
        ref="tree"
        :data="tree_data"
        :props="defaultProps"
        :highlight-current="true"
        class="filter-tree"
        :default-expanded-keys="[1, 10]"
        :expand-on-click-node="false"
        :filter-node-method="filterNode"
        node-key="id"
        draggable
        :allow-drop="allowDrop"
        @node-drop="node_drop"
        @node-click="node_click"
      >
        <span slot-scope="{ node, data }" class="custom-tree-node" @mouseenter="getcase(data)">
          <span v-if="data.type===0"><i :class="icon0" style="padding-right: 5px" />{{ node.label }}</span>
          <span v-if="data.type===3"><i :class="icon1" style="padding-right: 5px" />{{ node.label }}</span>
          <span v-if="data.type===4"><i :class="icon2" style="padding-right: 5px" />{{ node.label }}</span>
          <span v-if="data.type === 0" class="right" :class="{newStyle: 1 === number}">
            <el-dropdown placement="bottom">
              <i class="el-icon-more" />
              <span class="el-dropdown-link" style="font-size: 20px" />
              <el-dropdown-menu slot="dropdown" class="header-new-drop">
                <el-dropdown-item icon="el-icon-folder-add" @click.native="new_menu(data)">新建子菜单</el-dropdown-item>
              </el-dropdown-menu>
            </el-dropdown>
          </span>
          <span v-if="data.type === 3" class="right" :class="{newStyle: 1 === number}">
            <el-dropdown placement="bottom">
              <i class="el-icon-more" />
              <span class="el-dropdown-link" style="font-size: 20px" />
              <el-dropdown-menu slot="dropdown" class="header-new-drop">
                <el-dropdown-item icon="el-icon-folder-add" @click.native="new_menu(data)">新增</el-dropdown-item>
                <el-dropdown-item divided icon="el-icon-edit" @click.native="edit(data)">重命名</el-dropdown-item>
                <el-dropdown-item divided icon="el-icon-folder-delete" @click.native="delete_del(node, data)">删除文件夹</el-dropdown-item>
              </el-dropdown-menu>
            </el-dropdown>
          </span>
          <span v-if="data.type ===4" class="right" :class="{newStyle: 2 === number}">
            <el-dropdown placement="bottom">
              <i class="el-icon-more" />
              <span class="el-dropdown-link" style="font-size: 20px" />
              <el-dropdown-menu slot="dropdown" class="header-new-drop">
                <el-dropdown-item icon="el-icon-edit" @click.native="edit(data)">重命名</el-dropdown-item>
                <el-dropdown-item divided icon="el-icon-document-delete" @click.native="delete_case(node, data)">删除接口</el-dropdown-item>
              </el-dropdown-menu>
            </el-dropdown>
          </span>
        </span>
      </el-tree>
    </div>
    <div style="overflow: auto; height: 100%">
      <el-tabs v-model="TabsValue" type="card" closable @tab-remove="removeTab" @tab-click="tab_click">
        <el-tab-pane
          v-for="(item) in tableTabs"
          :key="item.name"
          :label="item.title"
          :name="item.name"
        >
          <span slot="label">
            <span v-if="item.type===3"><i :class="icon1" style="padding-right: 5px" />{{ item.title }}</span>
            <span v-if="item.type===4"><i :class="icon2" style="padding-right: 5px" />{{ item.title }}</span>
          </span>
          <div v-show="1 === number">
            {{ item.title }}
          </div>
          <div v-show="2 === number">
            <div>
              <div style="padding-block-end: 10px">
                <el-button type="success" @click="run_ui_test">立即执行</el-button>
                <el-button type="primary" @click="ui_test_result">查看上一次结果</el-button>
                <el-button style="float: right" type="primary" @click="edit_ui_dialog()">编辑</el-button>
              </div>
              <div style="padding-block-end: 10px">
                <el-descriptions :column="3" border>
                  <el-descriptions-item label-class-name="my-label" label="ID:" :content-style="{'text-align': 'left'}" style="width: 200px">
                    {{ ui_id }}</el-descriptions-item>
                  <el-descriptions-item label-class-name="my-label" label="用例名称:" :content-style="{'text-align': 'left'}" style="width: 200px">{{ item.title }}</el-descriptions-item>
                  <el-descriptions-item label-class-name="my-label" label="定位方式:" :content-style="{'text-align': 'left'}" style="width: 200px">xpath</el-descriptions-item>
                  <el-descriptions-item label-class-name="my-label" label="创建人:" :content-style="{'text-align': 'left'}" style="width: 200px">{{ username }}</el-descriptions-item>
                  <el-descriptions-item label-class-name="my-label" label="创建时间:" :content-style="{'text-align': 'left'}" style="width: 200px">
                    {{ create_time }}</el-descriptions-item>
                  <el-descriptions-item label-class-name="my-label" label="编辑时间:" :content-style="{'text-align': 'left'}" style="width: 200px">
                    {{ update_time }}</el-descriptions-item>
                  <el-descriptions-item label-class-name="my-label" label="执行方式:" :content-style="{'text-align': 'left'}" style="width: 200px">Chrome</el-descriptions-item>
                  <el-descriptions-item label-class-name="my-label" label="启动地址:" :content-style="{'text-align': 'left'}" style="width: 200px">{{ address }}</el-descriptions-item>
                </el-descriptions>
              </div>
              <div>
                <el-table
                  class="view_ui"
                  :data="table_data"
                  element-loading-text="Loading"
                  border
                  fit
                  highlight-current-row
                  style="float:left;width:100%"
                >
                  <el-table-column label="步骤名称" width="200px">
                    <template slot-scope="scope">
                      {{ scope.row.name }}
                    </template>
                  </el-table-column>
                  <el-table-column label="xpath地址">
                    <template slot-scope="scope">
                      {{ scope.row.xpath }}
                    </template>
                  </el-table-column>
                  <el-table-column label="是否iframe" width="100px">
                    <template slot-scope="scope">
                      <span v-if="scope.row.iframe===1"><el-tag type="success"></el-tag></span>
                      <span v-if="scope.row.iframe===0"><el-tag type="danger"></el-tag></span>
                    </template>event
                  </el-table-column>
                  <el-table-column label="iframe地址" width="200px">
                    <template slot-scope="scope">
                      {{ scope.row.iframe_value }}
                    </template>event
                  </el-table-column>
                  <el-table-column label="事件类型" width="100px">
                    <template slot-scope="scope">
                      {{ scope.row.event }}
                    </template>
                  </el-table-column>
                  <el-table-column label="输入值" width="100px">
                    <template slot-scope="scope">
                      {{ scope.row.value }}
                    </template>
                  </el-table-column>
                  <el-table-column label="等待时间" width="100px">
                    <template slot-scope="scope">
                      {{ scope.row.wait_time }} s
                    </template>
                  </el-table-column>
                </el-table>
              </div>
            </div>
          </div>
        </el-tab-pane>
      </el-tabs>
    </div>
    <div>
      <el-dialog
        title="重命名"
        :visible.sync="editdialogVisible"
        width="30%"
        :before-close="handleClose"
      >
        <el-form :model="menu_form">
          <el-form-item label="menu_id">
            <el-input v-model="menu_form.id" readonly />
          </el-form-item>
          <el-form-item label="菜单名称">
            <el-input v-model="tree_name" />
          </el-form-item>
        </el-form>
        <span slot="footer" class="dialog-footer">
          <el-button @click="editdialogVisible = false">取 消</el-button>
          <el-button type="primary" @click="edit_menu">确 定</el-button>
        </span>
      </el-dialog>
    </div>
    <div>
      <el-dialog
        title="新增子菜单"
        :visible.sync="dialogVisible"
        width="30%"
        :before-close="handleClose"
      >
        <el-form :model="menu_pid">
          <el-form-item label="Type:">
            <el-select v-model="type_value">
              <el-option
                v-for="item in type_form"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
          </el-form-item>
          <el-form-item label="菜单名称">
            <el-input v-model="menu_form.label" />
          </el-form-item>
        </el-form>
        <span slot="footer" class="dialog-footer">
          <el-button @click="dialogVisible = false">取 消</el-button>
          <el-button type="primary" @click="add_menu">确 定</el-button>
        </span>
      </el-dialog>
    </div>
    <div>
      <el-dialog
        title="提示"
        :visible.sync="del_case_dialog"
        width="20%"
      >
        <span>是否删除改配置项</span>
        <span slot="footer" class="dialog-footer">
          <el-button @click="del_case_dialog = false">取 消</el-button>
          <el-button type="primary" @click="deletepro()">确 定</el-button>
        </span>
      </el-dialog>
    </div>
    <div>
      <el-dialog
        title="编辑配置"
        :visible.sync="edit_case_dialog"
        width="80%"
      >
        <el-form :model="edit_ui">
          <el-form-item>
            <el-button type="primary" style="float: right" @click="add_ui_step">新增步骤</el-button>
          </el-form-item>
          <el-form-item label="启动地址">
            <el-input v-model="address" style="width: 500px" />
          </el-form-item>
          <el-form-item>
            <el-table
              class="edit_ui"
              :data="edit_ui"
              element-loading-text="Loading"
              border
              fit
              highlight-current-row
              style="float:left;width:100%"
              @cell-mouse-enter="handleCellEnter"
              @cell-mouse-leave="handleCellLeave"
              @cell-click="handleCellClick"
            >
              <el-table-column label="步骤名称" width="200px">
                <div slot-scope="scope" class="item">
                  <el-input v-model="scope.row.name" class="item__input" placeholder="请输入内容" />
                </div>
              </el-table-column>
              <el-table-column label="xpath地址">
                <div slot-scope="scope" class="item">
                  <el-input v-model="scope.row.xpath" class="item__input" placeholder="请输入内容" />
                </div>
              </el-table-column>
              <el-table-column label="是否iframe" width="100px">
                <div slot-scope="scope" class="item">
                  <el-select v-model="scope.row.iframe" class="item__input" placeholder="请选择">
                    <el-option
                      v-for="item in iframe"
                      :key="item.iframe"
                      :label="item.label"
                      :value="item.iframe"
                    />
                  </el-select>
                </div>
              </el-table-column>
              <el-table-column label="iframe地址">
                <div slot-scope="scope" class="item">
                  <el-input v-model="scope.row.iframe_value" class="item__input" placeholder="请输入内容" />
                </div>
              </el-table-column>
              <el-table-column label="事件类型" width="120px">
                <div slot-scope="scope" class="item">
                  <el-select v-model="scope.row.event" class="item__input" placeholder="请选择">
                    <el-option
                      v-for="item in event"
                      :key="item.event"
                      :label="item.label"
                      :value="item.event"
                    />
                  </el-select>
                </div>
              </el-table-column>
              <el-table-column label="输入值" width="200px">
                <div slot-scope="scope" class="item">
                  <el-input v-model="scope.row.value" class="item__input" placeholder="请输入内容" />
                </div>
              </el-table-column>
              <el-table-column label="等待时间" width="80px">
                <div slot-scope="scope" class="item">
                  <el-input v-model="scope.row.wait_time" class="item__input" placeholder="请输入内容" />
                </div>
              </el-table-column>
              <el-table-column align="center" prop="action" label="操作" width="100px">
                <template slot-scope="scope">
                  <el-button type="danger" icon="el-icon-delete" circle @click="del_ui_step(scope.$index, scope.row)" />
                </template>
              </el-table-column>
            </el-table>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" style="float: right" @click="save_ui_info">保存</el-button>
          </el-form-item>
        </el-form>
      </el-dialog>
    </div>
    <div>
      <el-dialog
        title="执行结果"
        :visible.sync="run_ui_dialog"
        width="60%"
        append-to-body
      >
        <div style="overflow: auto; height: 630px">
          <el-timeline style="padding: 15px">
            <el-timeline-item
              v-for="(activity, index) in run_ui_result"
              :key="index"
              :icon="icon"
              :type="type"
              :size="size"
              :timestamp="activity.timestamp"
            >
              <div v-if="2 === activity.type">
                <el-image :src="require('xxxx/img/' + activity.result + '')" style="width:800px;height:600px" @click="showImage(activity.result)" />
              </div>
              <div v-if="2 !== activity.type">
                {{ activity.result }}
              </div>
            </el-timeline-item>
          </el-timeline>
        </div>
      </el-dialog>
    </div>
    <div v-if="img_path !== ''">
      <el-dialog
        title="预览大图"
        :visible.sync="imageDialogVisible"
        width="70%"
      >
        <img :src="require('xxx/img/' + img_path + '')" style="width:100%;height:100%">
      </el-dialog>
    </div>
  </div>
</template>

<script>
import { edit_ui_info, get_ui_info, getUI_tree, run_result, run_ui } from "@/api/ui";
import { addmenu, change_pid, deletecase, deletepeoject } from "@/api/testcase";

export default {
  name: "Ui",
  data() {
    return {
      img_path: "",
      imageDialogVisible: false,
      address: "",
      run_ui_dialog: false,
      size: 'large',
      type: 'primary',
      icon: 'el-icon-bottom',
      timer: null,
      run_ui_result: [
        {
          result: '开始',
          // timestamp: '0000-00-00 00:00:00',
          id: 0,
        }
      ],
      iframe: [
        {
          label: "是",
          iframe: 1
        },
        {
          label: "否",
          iframe: 0
        },
      ],
      event: [
        {
          label: "click",
          event: 1
        },
        {
          label: "input",
          event: 2
        },
      ],
      // 需要编辑的属性
      editProp: ['name', 'xpath', 'iframe', 'event', 'value', 'wait_time'],
      // 保存进入编辑的cell
      clickCellMap: {},
      ui_id: null,
      create_time: "",
      update_time: "",
      menu_pid: null,
      editableTabs: [],
      TabsValue: "",
      // 用例id
      case_form: {
        menu_id: '',
        current_page: 1,
        size_page: 3,
      },
      del_case_dialog: false,
      menu_form: {
        label: '',
        pid: '',
        type: null,
        user_id: null,
      },
      table_data: [],
      type_form: [
        {
          label: "文件夹",
          value: 3
        },
        {
          label: "UI用例",
          value: 4
        },
      ],
      icon0: 'el-icon-s-home',
      icon1: 'el-icon-folder-opened',
      icon2: 'el-icon-s-data',
      number: null,
      filterText: '',
      defaultProps: {
        children: 'children',
        label: 'label',
        id: "id"
      },
      node_data: [],
      tree_data: [],
      edit_case_dialog: false,
      dialogVisible: false,
      type_value: '',
      tree_name: '',
      editdialogVisible: false,
      // eslint-disable-next-line no-dupe-keys
      tableTabs: [],
      pidFrom: {
        id: ''
      },
      username: "",
      edit_ui: [],
      now: "",
    }
  },
  watch: {
    filterText(val) {
      this.$refs.tree.filter(val);
    }
  },
  created() {
    this.get_ui_tree()
    this.is_login()
  },
  destroyed() {
    clearTimeout(this.timer)
  },
  methods: {
    is_login() {
      // eslint-disable-next-line no-prototype-builtins
      if (localStorage.hasOwnProperty("user_id") === false || localStorage.getItem("user_id") === '') {
        this.$router.push("/login")
        this.$message.error("未登录,请前往登录")
      }
    },
    showImage(data) {
      this.imageDialogVisible = true
      this.img_path = data
      console.log(this.img_path)
    },
    /** 鼠标移入cell */
    handleCellEnter(row, column, cell, event) {
      const property = column.property
      if (property === 'date' || property === 'name' || property === 'food') {
        cell.querySelector('.item__txt').classList.add('item__txt--hover')
      }
    },
    /** 鼠标移出cell */
    handleCellLeave(row, column, cell, event) {
      const property = column.property
      if (this.editProp.includes(property)) {
        cell.querySelector('.item__txt').classList.remove('item__txt--hover')
      }
    },
    /** 点击cell */
    handleCellClick(row, column, cell, event) {
      const property = column.property
      if (this.editProp.includes(property)) {
        // 保存cell
        this.saveCellClick(row, cell)
        cell.querySelector('.item__txt').style.display = 'none'
        cell.querySelector('.item__input').style.display = 'block'
        cell.querySelector('input').focus()
      }
    },
    /** 取消编辑状态 */
    cancelEditable(cell) {
      cell.querySelector('.item__txt').style.display = 'block'
      cell.querySelector('.item__input').style.display = 'none'
    },
    /** 保存进入编辑的cell */
    saveCellClick(row, cell) {
      const id = row.id
      if (this.clickCellMap[id] !== undefined) {
        if (!this.clickCellMap[id].includes(cell)) {
          this.clickCellMap[id].push(cell)
        }
      } else {
        this.clickCellMap[id] = [cell]
      }
    },
    /** 保存数据 */
    save(row) {
      const id = row.id
      // 取消本行所有cell的编辑状态
      this.clickCellMap[id].forEach(cell => {
        this.cancelEditable(cell)
      })
      this.clickCellMap[id] = []
    },
    // 获取接口目录
    get_ui_tree() {
      getUI_tree().then(res => {
        this.tree_data = res.data.content
      })
    },
    new_menu(data) {
      this.dialogVisible = true
      this.node_data = data
      this.tree_menu_id = data.id
      if (this.children_view(this.node_data) === 0) {
        this.node_data.children = []
      }
    },
    tab_click(tab) {
      const tabs = this.tableTabs
      for (const t of tabs) {
        if (t.name === tab.name) {
          if (t.type === 3) {
            this.number = 1
            this.pidFrom.id = t.content
          } else if (t.type === 4) {
            this.number = 2
            this.case_form.menu_id = t.content
            this.case_form.size_page = 3
            this.case_form.current_page = 1
            this.ui_ifo(t.content)
          }
        }
      }
    },
    removeTab(targetName) {
      const tabs = this.tableTabs;
      let activeName = this.TabsValue;
      if (activeName === targetName) {
        tabs.forEach((tab, index) => {
          if (tab.name === targetName) {
            const nextTab = tabs[index + 1] || tabs[index - 1];
            if (nextTab) {
              activeName = nextTab.name;
              this.tab_click(nextTab)
            }
          }
        });
      }
      this.TabsValue = activeName;
      this.tableTabs = tabs.filter(tab => tab.name !== targetName);
    },
    addTab(targetName, id, type) {
      const index = this.tableTabs.findIndex(item => item.title === targetName)
      if (index === -1) {
        this.tableTabs.push({
          title: targetName,
          name: targetName,
          content: id,
          type: type
        })
      }
      this.TabsValue = targetName;
    },
    node_click(data) {
      this.addTab(data.label, data.id, data.type)
      this.tree_name = data.label
      this.m_id = data.pid
      if (data.type === 3) {
        this.number = 1;
        this.menu_pid = data.id;
        this.pidFrom.id = data.id;
      } else if (data.type === 4) {
        this.number = 2;
        this.case_form.menu_id = data.id;
        this.menu_pid = data.id;
        this.ui_ifo(data.id)
      }
    },
    getcase(data) {
      switch (data.type) {
        case 3:
          this.menu_pid = this.pidFrom.id = data.id;
          this.m_id = data.pid;
          break
        case 4:
          this.case_form.menu_id = this.menu_pid = this.pidFrom.id = data.id;
          this.m_id = data.pid;
          break;
        case 0:
          this.menu_pid = this.pidFrom.id = data.id;
          break;
        default:
          // handle unexpected data.type
          break;
      }
    },
    children_view(data) {
      if ("children" in data) { // json就是数组,name是你要找的值
        return 1
      } else {
        return 0
      }
    },
    delete_case(node, data) {
      deletecase({ "menu_id": data.id }).then(res => {
        if (res.data.code === 200) {
          this.$message({
            message: "删除接口成功",
            type: "success"
          })
          const parent = node.parent;
          const children = parent.data.children || parent.data;
          const index = children.findIndex(d => d.id === data.id);
          children.splice(index, 1);
        } else if (res.data.code === 100) {
          this.$message({
            message: "删除接口失败,无此接口",
            type: 'error'
          })
        }
      })
    },
    add_menu() {
      const req = this.menu_form
      req.type = this.type_value
      req.pid = this.node_data.id
      req.user_id = Number(localStorage.getItem("user_id"))
      addmenu(this.menu_form).then(res => {
        if (res.data.code === 200) {
          this.$message({
            message: res.data.message,
            type: 'success'
          })
          this.dialogVisible = false
          this.node_data.children.push(res.data.data)
        } else if (res.data.code === 100) {
          this.dialogVisible = true
          this.$message.error("菜单名称不可为空")
        } else if (res.data.code === 101) {
          this.dialogVisible = true
          this.$message.error("名称已存在,请重新输入")
        }
      })
    },
    edit(data) {
      this.menu_form.id = data.id
      this.editdialogVisible = true
      this.tree_name = data.label
      this.node_data = data
    },
    edit_menu() {
      const req = this.menu_form
      req.id = this.menu_form.id
      req.label = this.tree_name
      req.pid = this.node_data.id
      req.user_id = Number(localStorage.getItem("user_id"))
      addmenu(req).then(res => {
        if (res.data.code === 200) {
          this.$message({
            message: '重命名成功',
            type: 'success'
          })
          this.editdialogVisible = false
          const temp = this.tableTabs.find(o => o.content === this.menu_form.id)
          if (temp) {
            temp.title = res.data.label;
          }
          this.node_data.label = res.data.label
          this.form_case.name = res.data.label
        } else if (res.data.code === 100) {
          this.editdialogVisible = true
          this.$message.error("菜单名称不可为空")
        } else if (res.data.code === 101) {
          this.editdialogVisible = true
          this.$message.error("名称已存在,请重新输入")
        }
      })
    },
    delete_del(node, data) {
      this.del_case_dialog = true
      this.node_data = node
      this.pid_list = data
    },
    // 删除文件夹
    deletepro() {
      const data = this.pid_list
      const node = this.node_data
      deletepeoject({ "id": data.id }).then(res => {
        if (res.data.code === 200) {
          this.$message({
            message: res.data.message,
            type: "success"
          })
          const parent = node.parent;
          const children = parent.data.children || parent.data;
          const index = children.findIndex(d => d.id === data.id);
          children.splice(index, 1);
          this.del_case_dialog = false
        } else {
          this.$message({
            message: res.data.message,
            type: 'error'
          })
        }
      })
    },
    allowDrop(from, to, type) {
      return !(to.data.type === 3 || type !== 'inner');
    },
    node_drop(from, to) {
      const req = {}
      req.from_id = from.data.id
      req.to_id = to.data.id
      change_pid(req).then(res => {
        if (res.data.code === 200) {
          this.$message.success(res.data.message)
        } else if (res.data.code !== 200) {
          this.$message.error(res.data.message)
        }
      })
    },
    filterNode(value, tree_data) {
      if (!value) return true;
      return tree_data.label.indexOf(value) !== -1;
    },
    handleClose(done) {
      done()
    },
    getNowTime() {
      let now = ""
      const yy = new Date().getFullYear()
      const mm = new Date().getMonth() + 1
      const dd = new Date().getDate()
      const hh = new Date().getHours() < 10 ? '0' + new Date().getHours() : new Date().getHours()
      const mf = new Date().getMinutes() < 10 ? '0' + new Date().getMinutes() : new Date().getMinutes()
      const ss = new Date().getSeconds() < 10 ? '0' + new Date().getSeconds()
        : new Date().getSeconds()
      now = yy + '-' + mm + '-' + dd + ' ' + hh + ':' + mf + ':' + ss
      return now
    },
    ui_test_result() {
      this.run_ui_dialog = true
      this.now = this.getNowTime()
      run_result({ "id": this.ui_id, "time": "" }).then(res => {
        this.run_ui_result = res.data.content
      })
    },
    run_ui_test() {
      this.$message.success("开始执行")
      this.run_ui_dialog = true
      run_ui({
        "id": this.ui_id,
        "address": this.address
      }).then(res => {
        if (res.data.code !== 200) {
          this.$message.error(res.data.message)
        }
      })
      this.now = this.getNowTime()
      this.run_ui_result = [{
        result: '开始',
        // timestamp: '0000-00-00 00:00:00',
        id: 0,
      }]
      this.ui_result()
    },
    edit_ui_dialog() {
      this.edit_case_dialog = true
      this.edit_ui = this.table_data
    },
    del_ui_step(index, row) {
      this.edit_ui.splice(index, 1)
    },
    add_ui_step() {
      const obj = {
        "name": "",
        "xpath": "",
        "iframe": 0,
        "iframe_value": "",
        "value": "",
        "event": null,
        "wait_time": ""
      }
      this.edit_ui.push(obj)
    },
    ui_ifo(data) {
      get_ui_info({ "id": data }).then(res => {
        if (res.data.code === 200) {
          this.update_time = res.data.update_time
          this.create_time = res.data.create_time
          this.table_data = res.data.content
          this.username = res.data.username
          this.ui_id = res.data.ui_id
          this.address = res.data.address
        } else {
          this.$message.error(res.data.message)
        }
      })
    },
    save_ui_info() {
      let flag = true
      this.edit_ui.forEach(item => {
        if (item.name === "" || item.wait_time === "" || item.event === "" || item.xpath === "") {
          this.$message.error("必要参数不可为空")
          flag = false
        }
      })
      if (flag) {
        edit_ui_info({
          "id": this.ui_id,
          "value": this.edit_ui,
          "user_id": localStorage.getItem("user_id"),
          "address": this.address
        }).then(res => {
          if (res.data.code === 200) {
            this.$message.success(res.data.message)
            this.edit_case_dialog = false
          } else {
            this.$message.error(res.data.message)
          }
        })
      }
    },
    ui_result() {
      const that = this
      run_result({ "id": this.ui_id, "time": this.now }).then(res => {
        if (res.data.code === 200) {
          const list = res.data.content
          for (const obj1 of list) {
            let flag = true
            for (const obj2 of this.run_ui_result) {
              if (obj1.id === obj2.id) {
                flag = false
              }
            }
            if (flag) {
              this.run_ui_result.push(obj1)
            }
          }
        } else {
          this.$message.error(res.data.message)
        }
      }).finally(() => {
      // 每隔10s循环遍历数据库获取自动化执行的结果
        this.run_ui_result.forEach(item => {
          if (item.result === '结束') {
            clearTimeout(that.timer);
          } else {
            clearTimeout(that.timer);
            that.timer = setTimeout(() => {
              that.ui_result()
            }, 10000)
          }
        })
      })
    }
  }
}
</script>

<style scoped>
.el-dialog__body {
  padding: 10px !important;
}
  .custom-tree-node {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 14px;
    padding-right: 4px;
  }
  .el-dropdown {
    font-size: 16px;
    color: #5d697a;
  }
  .my-label {
    background: rgba(216, 243, 234, 0.31) !important;
  }
  .my-content {
    background: #e2fdf1;
  }
</style>

总结

闲暇时候写的这套代码只能执行简单的点击,输入,更高深的技术真正摸索中!!!
个人主页仍有精彩的内容,欢迎私信交流!!!!

  • 11
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Python Django自动化测试平台源码是一个基于Python和Django框架开发的系统,用于帮助开发人员进行自动化测试任务的管理和执行。它提供了一个用户友好的界面,使用户能够轻松创建、编辑和执行各种自动化测试任务。 该平台源码基于Django框架构建,因此具有良好的扩展性和稳定性。它采用了MVC架构模式,使得代码易于维护和理解。 源码中包含了多个模块,包括用户管理、任务管理、测试用例管理等。用户管理模块负责用户的注册、登录和权限管理,确保只有授权的用户才能进行测试任务的操作。任务管理模块主要用于任务的创建、编辑和执行,用户可以根据自己的需求设置不同的任务参数,并可以查看任务的执行结果。测试用例管理模块允许用户创建和编辑测试用例,以便在任务中使用。 除了以上核心模块外,源码还包含了其他一些辅助模块,如日志管理、报告生成等。日志管理模块用于记录测试任务的执行日志,方便用户追踪和排查问题。报告生成模块能够生成详细的测试报告,展示测试任务的执行结果和统计信息,方便用户进行分析和评估。 总之,Python Django自动化测试平台源码提供了一个全面且易于使用自动化测试解决方案,使开发人员能够更高效地进行自动化测试任务的管理和执行。通过该平台,可以提高测试效率,减少人工测试的工作量,提供更高质量的软件产品。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

测开小林

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值