界面自动化测试录制工具,让python selenium自动化测试脚本开发更加方便
自动化测试中,QTP和selenium IDE都支持浏览器录制与回放功能,简单的来说就像一个记录操作步骤的机器人,可以按照记录的步骤重新执行一遍,这就是脚本录制。
个人觉得传统录制工具有些弊端,加上要定制支持我自己的自动化框架(python单机版自动化测试框架源代码),所以自己用javascript写了一个录制工具,在控制台打印记录的python脚本如下:

代码如下(初稿,还在不断调试完善中):
var click_textContent = '
var father_level = 0;
var child_ctl = "";
var child_ctl_tmp = "";
var next_focusedElement = null;
window.clickedElement = null; document.addEventListener("click", function(event) {
window.clickedElement = event.target;
console.log(window.clickedElement);
father_level = 0;
myDispose_click(window.clickedElement);
}); function myDispose_click(focusedElement) {
console.log(`开始 父${father_level} -------------------------`);
let tag_name = focusedElement.tagName.toLowerCase();
let outerHTML = focusedElement.outerHTML;
console.log(outerHTML); if (tag_name === "body") {
let xpath = getElementfullXPath(next_focusedElement);
let elements = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
if (elements && elements.snapshotLength === 1) {
console.log(`self.myWtClickEx(driver, By.XPATH, "${xpath}")`);
console.log("结束:tag名称为body");
console.log(`结束 父${father_level} -------------------------`);
return xpath;
} else {
console.log("结束:tag名称为body");
console.log(`结束 父${father_level} -------------------------`);
return null;
}
} let my_all_value = "";
let text = focusedElement.textContent.trim().replace(/"/g, "\\\"");
if (text !== "" && !text.includes("\\n")) {
my_all_value = `contains(text(),\'${text}\')`;
if (myDispose_count_number(text, "text", tag_name)) {
let xpath = `//${tag_name}[${my_all_value}]`;
let parameter = `driver, By.XPATH, "${xpath}"`;
myDispose_success(parameter);
return parameter;
}
} else {
text = ""
} let attributes = focusedElement.attributes;
console.log(`属性名称列表: ${Array.from(attributes).map(attr => attr.name).join(",")}`); for (let i = 0; i < attributes.length; i++) {
let attribute_name = attributes[i].name;
let attribute_value = attributes[i].value; if (attribute_name === "class") {
let class_value_list = attribute_value.split(" ");
console.log(`class列表:${class_value_list}`);
if (class_value_list.includes("focusing")) {
class_value_list = class_value_list.filter(value => value !== "focusing");
} for (let class_value of class_value_list) {
if (my_all_value === "") {
my_all_value = `contains(@class,"${class_value}")`;
} else {
my_all_value += ` and contains(@class,"${class_value}")`;
} if (myDispose_count_number(class_value, attribute_name, tag_name)) {
let parameter = `driver, By.CLASS_NAME, "${class_value}"`;
myDispose_success(parameter);
return parameter;
} let xpath = "";
if (text === "") {
xpath = `//${tag_name}[${my_all_value}]`;
} else {
xpath = `//${tag_name}[contains(text(),\'${text}\') and ${my_all_value}]`;
}
let result = myDispose_count_evaluate(xpath);
if (result) {
let parameter = `driver, By.XPATH, "${xpath}"`;
myDispose_success(parameter);
return parameter;
}
}
} else {
/*if (attribute_value === "" || /\d/.test(attribute_value)) {*/
if (attribute_value === "" || (attribute_name !== "src" && attribute_value.match(/[0-9]/))) {
continue;
} if (my_all_value === "") {
my_all_value = `contains(@${attribute_name}, "${attribute_value}")`;
} else {
my_all_value += ` and contains(@${attribute_name}, "${attribute_value}")`;
} if (myDispose_count_number(attribute_value, attribute_name, tag_name)) {
let xpath = `//${tag_name}[contains(@${attribute_name}, "${attribute_value}")]`;
let parameter = `driver, By.XPATH, "${xpath}"`;
myDispose_success(parameter);
return parameter;
} let xpath = "";
if (text === "") {
xpath = `//${tag_name}[${my_all_value}]`;
} else {
xpath = `//${tag_name}[contains(text(),\'${text}\') and ${my_all_value}]`;
}
let result = myDispose_count_evaluate(xpath);
if (result) {
let parameter = `driver, By.XPATH, "${xpath}"`;
myDispose_success(parameter);
return parameter;
}
}
} if (my_all_value !== "") {
let xpath = `//${tag_name}[${my_all_value}]`;
let result = myDispose_count_evaluate(xpath);
if (result) {
let parameter = `driver, By.XPATH, "${xpath}"`;
myDispose_success(parameter);
return parameter;
} else {
let textStr = `self.myWtClickEx(driver, By.XPATH, "${xpath}")`;
console.log("# 不是1");
console.log(textStr);
console.log(`结束 父${father_level} -------------------------`); if (father_level === 0) {
child_ctl = `father, By.XPATH, ".${xpath}"`;
child_ctl_tmp = `.${xpath}`;
next_focusedElement = focusedElement;
} let father = focusedElement.parentElement;
if (father) {
father_level++;
myDispose_click(father);
}
}
}
return null;
} function myDispose_success(parameter) {
if (father_level === 0) {
console.log(`self.myWtClickEx(${parameter})`);
} else {
console.log(`father = self.myWtFindElement(${parameter})`);
console.log(`self.myWtClickEx(${child_ctl})`);
}
console.log(`结束 父${father_level} -------------------------`);
} function myDispose_count_evaluate(xpath) {
let elements = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
if (father_level === 0) {
if (elements.snapshotLength === 1) {
return true
} else {
return null
}
} else {
if (elements.snapshotLength === 1) {
let firstElement = elements.snapshotItem(0);
let result = document.evaluate(child_ctl_tmp, firstElement, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
if (result.snapshotLength === 1) {
return true
} else {
return null
}
} else {
return null
}
}
} function myDispose_count_number(attribute_value, attribute_name, tag_name) {
if (attribute_value === "") {
return null;
};
if (attribute_name !== "text" && attribute_name !== "src" && attribute_value.match(/[0-9]/)) {
return null;
}; let xpath;
if (attribute_name !== "text") {
xpath = `//${tag_name}[contains(@${attribute_name}, "${attribute_value}")]`;
} else {
xpath = `//${tag_name}[contains(text(), \'${attribute_value}\')]`;
}; let result = myDispose_count_evaluate(xpath); if (result) {
console.log(`${attribute_name}:"${attribute_value}" 在网页中出现1次`);
return true;
} else {
console.log(`${attribute_name}:"${attribute_value}" 在网页中出现了多次`);
return null;
};
} function getElementfullXPath(element) {
if (element && element.id)
if (!element.id.match(/[0-9]/)) {
return \'//*[@id="\' + element.id + \'"]\';
} if (element==null)
return ""; var index = 0;
var loacl_tagName = element.tagName;
var sibling = element.previousSibling;
var sibling_tagName = null;
if (sibling) {
sibling_tagName = sibling.tagName;
}
while (sibling && sibling.nodeType === 1 && loacl_tagName === sibling_tagName) {
index++;
sibling = sibling.previousSibling;
if (sibling) {
sibling_tagName = sibling.tagName;
} else {
sibling_tagName = null;
}
} parent = element.parentNode;
if (parent) {
var xpath = getElementfullXPath(parent);
if (xpath === "undefined") {
return "";
} else {
if (index === 0) {
xpath += "/" + element.tagName.toLowerCase();
} else {
xpath += "/" + element.tagName.toLowerCase() + "[" + (index+1) + "]";
}
return xpath;
}
} else {
return "";
}
}
'; var input_textContent = '
let inputs = document.querySelectorAll(`input[type="text"]`);
inputs.forEach(input => {
input.addEventListener("input", function(event) {
console.log(this.value);
let parameter = myDispose_click(event.target);
if (parameter !== null) {
let textStr = `self.myWtSendKeysWebEx(${parameter}, "${this.value}")`;
console.log(textStr);
}
});
});
'; /*iframe*/
let iframes = document.getElementsByTagName('iframe');
for (let i = 0; i < iframes.length; i++) {
let iframe = iframes[i];
if (iframe.contentWindow && iframe.contentWindow.document) {
let script = iframe.contentWindow.document.createElement('script');
script.type = 'text/javascript';
script.textContent = click_textContent;
iframe.contentWindow.document.head.appendChild(script);
let inputs = iframe.contentWindow.document.querySelectorAll(`input[type="text"]`);
inputs.forEach(input => {
input.addEventListener("input", function(event) {
console.log(this.value);
let parameter = myDispose_click(event.target);
if (parameter !== null) {
let textStr = `self.myWtSendKeysWebEx(${parameter}, "${this.value}")`;
console.log(textStr);
}
});
});
}
} /*非iframe*/
let script = document.createElement('script');
script.type = 'text/javascript';
script.textContent = click_textContent + input_textContent;
document.head.appendChild(script);
界面自动化测试录制工具,让python selenium自动化测试脚本开发更加方便的更多相关文章
- Jenkins持续集成项目搭建与实践——基于Python Selenium自动化测试(自由风格)
Jenkins简介 Jenkins是Java编写的非常流行的持续集成(CI)服务,起源于Hudson项目.所以Jenkins和Hudson功能相似. Jenkins支持各种版本的控制工具,如CVS.S ...
- Python+selenium自动化测试中Windows窗口跳转方法
Python+selenium自动化测试中Windows窗口跳转方法 #第一种方法 #获得当前窗口 nowhandle=driver.current_window_handle #打开弹窗 drive ...
- 从零开始到设计Python+Selenium自动化测试框架-如何开始
如何开始学习web ui自动化测试?如何选择一门脚本语言?选择什么自动化测试工具? 本人已经做测试快5年,很惭愧,感觉积累不够,很多测试都不会,三年多功能测试,最近两年才开始接触和学习自动化测试.打算 ...
- 第一章 python+selenium自动化测试实战
@序章 自动化测试是软件测试的主流方向之一: 教程从测试的根本需求出发,讲解如何开展自动化测试. 首先,我们要明白,自动化仅仅是满足我们某种需求的一种工具:没有必要花费时间把它全部弄懂:我们只需要学会 ...
- python selenium 自动化测试web
如何使用python完成自动化测试web页面呢?首选selenium 那基于python的selenium如何使用,下面看一段测试案例: 基于python的selenium 安装方法: pip i ...
- python+selenium 自动化测试实战
一.前言: 之前的文章说过, 要写一篇自动化实战的文章, 这段时间比较忙再加回家过11一直没有更新博客,今天整理一下实战项目的代码共大家学习.(注:项目是针对我们公司内部系统的测试,只能内部网络访问, ...
- 在公司内部网络如何搭建Python+selenium自动化测试环境
在公司内部安装Python+selenium测试环境,由于不能连外网所以不能使用pip命令进行安装,经过多次尝试终于安装成功,现总结如下分享给大家,也希望跟大家一起学习和交流自动化网页测试时遇到的问题 ...
- python+selenium自动化测试_1
前言 回顾一下python+selenium基础,并整理相关知识点,分享给有需要,在前进道路上的朋友. print打印 #打印Hello World print("Hello World&q ...
- 《一头扎进》系列之Python+Selenium自动化测试框架实战篇6 - 价值好几K的框架,呦!这个框架还真牛叉哦!!!
1. 简介 本文开始介绍如何通过unittest来管理和执行测试用例,这一篇主要是介绍unittest下addTest()方法来加载测试用例到测试套件中去.用addTest()方法来加载我们测试用例到 ...
- python+selenium自动化测试环境搭建
selenium 是一个web的自动化测试工具,不少学习功能自动化的同学开始首选selenium ,相因为它相比QTP有诸多有点: * 免费,也不用再为破解QTP而大伤脑筋 * 小巧,对于不同的语 ...
随机推荐
- Flutter(八):Flutter路由管理(Router)
目录 一.术语 路由(route): 导航(Navigator): 二.路由管理 1.Navigator示例代码 2.路由定义(命名路由) 在App中定义router: 3.Navigator方法介绍 ...
- pandas基础--数据结构:索引对象
pandas含有是数据分析工作变得更快更简单的高级数据结构和操作工具,是基于numpy构建的. 本章节的代码引入pandas约定为:import pandas as pd,另外import numpy ...
- TQX 的 DP AAgain!
闲话: 这确实抽象,将所有人给干离线了-- 不如叫做 TQX 的离线 DP QwQ DP 基本思路就是找一个比较好的能够描绘问题的状态,想怎么转移,再进行优化. --TQX 背包 DP loj 608 ...
- 剑指Offer-58.对称的二叉树(C++/Java)
题目: 请实现一个函数,用来判断一颗二叉树是不是对称的.注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的. 分析: 从根结点开始递归对比左右子树即可.需要注意的是,当前左右两个结点相同比较 ...
- ABC340
E 我们可以知道每一个点在每一轮加多少,具体如下: 假如现在操作的点的为 \(k\).那么所有的数都至少会加 \(\dfrac{A_k}{n}\).但是肯定有剩的,剩了 \(A_k \mod n\). ...
- apollo配置中心从数据库中查询所有的配置及项目
apollo配置中心从数据库中查询所有的配置及项目 需求背景:如果需要从Apollo查询某个配置项做批量的更新替换,如果一个一个找不合适且容易遗漏,需要从底层数据库表中模糊查询来实现. 1.查看apo ...
- Docker中部署单机Redis详细教程
1.拉取Redis镜像 # 拉取redis镜像,不指定版本则默认是最新版本 docker pull redis 2.查看镜像 # 列出本地镜像 docker images 3.准备配置文件路径 # 创 ...
- SpringBoot指标监控功能
SpringBoot指标监控功能 随时查看SpringBoot运行状态,将状态以josn格式返回 添加Actuator功能 Spring Boot Actuator可以帮助程序员监控和管理Spring ...
- 麒麟操作系统V10安装mysql8.0.26
今年mysql装得有点多,大概有4次了,快变系统工程师了! 本文重点说下如何识别版本和配置服务! 首先两点: 1)麒麟本质是linux内核,所以基本上centos的操作在这里可以通用 2)虽然通用,但 ...
- power bi权限控制笔记
power bi权限控制:power bi权限控制角色目前只能在desktop 客户端进行创建.报表进行权限控制后,用于行级别安全,需注意:a.对数据集做行级别安全性的角色分配 b.需要进行报表的共享 ...