JS执行器在UI自动化测试中的应用

前言

在进行UI自动化过程会遇到滚动条下拉、隐藏元素定位、只读属性元素的编辑、富文本处理等,此时可以使用JS执行器简化我们的一些处理操作。

 

具体应用

JS执行器的使用步骤:

1.先写个JS脚本,如果需要获取操作后的值,JS脚本前面需要加return

2.使用webdriver的execute_script()方法执行脚本;方法中传递2个参数或1个参数;传递2个参数时,分别传入JS脚本、要操作的页面元素;传递1个参数时,传入JS脚本,对整个webdriver做操作。

3.操作通过JS执行器注入了JS脚本的页面元素或webdriver对象

 

接下来我们以百度、12306网站为例,演绎具体用法

1.通过JS脚本,获取页面元素的属性值

from selenium import webdriverdriver = webdriver.Chrome()#创建浏览器对象
driver.maximize_window()#窗口最大化driver.get(r'https://www.baidu.com')#访问百度
el = driver.find_element('id','su') #页面的百度一下元素
'''
写JS脚本,写JS脚本的步骤:
js:定义一个变量名js,用于存放所写的JS季报表
= :赋值
JS脚本全部用引号引起来,整个脚本就是一个字符串
arguments[0]:理解为占位符,代值被操作的元素或对象
.:连接被操作的元素对象与所做的具体操作
getAttribute():我们这里的举例的操作为:获取元素的某个属性值
JS脚本最前面加上return,是因为我们要获取对元素操作后的值
'''
js = "return arguments[0].getAttribute('value')" #JS脚本的意思为:返回某个元素的value属性的值
value = driver.execute_script(js,el)#执行JS脚本,传入的2个参数依次为:要执行的脚本、要操作的对象
print(value)#打印获取到的值
driver.quit()#关闭浏览器

执行的结果:

2.通过JS脚本,获取页面元素的文本值

from selenium import webdriverdriver = webdriver.Chrome()#创建浏览器对象
driver.maximize_window()#窗口最大化driver.get(r'https://www.12306.cn/index/')#访问12306#页面的出发日期元素
el = driver.find_element('xpath','/html/body/div[3]/div[2]/div/div[1]/div/div[2]/div[1]/div[1]/div[2]/label') #
js = "return arguments[0].innerHTML" #JS脚本的意思为:返回某个元素的文本值
value = driver.execute_script(js,el)#执行JS脚本,传入的2个参数依次为:要执行的脚本、要操作的对象
print(value)#打印获取到的值
driver.quit()#关闭浏览器

 运行结果

3.通过JS脚本,删除页面元素的属性值,然后对页面元素进行操作

 

我们发现出发日期输入框因为有readonly属性,导致这个输入框我们无法直接输入日期,在实际的自动化操作中我们就只能通过点击右边的日历控件,从而选择日期,这样操作就变得非常的复杂,现在我们通过执行JS脚本,删除中个输入框的readonly属性,实现对这个输入框直接输入日期


from selenium import webdriver
from time import sleep
driver = webdriver.Chrome()#创建浏览器对象
driver.maximize_window()#窗口最大化driver.get(r'https://www.12306.cn/index/')#访问12306#页面的出发日期输入框
el = driver.find_element('xpath','//input[@id="train_date" and @class="input"]')
js = "arguments[0].removeAttribute('readonly')" #JS脚本的意思为:移除某个元素的readonly属性
driver.execute_script(js,el)#执行JS脚本,传入的2个参数依次为:要执行的脚本、要操作的对象
sleep(1)#强制等待1秒
el.clear() #清空输入框内容
el.send_keys("2021-07-11")#对输入框进行输入操作
sleep(3)
driver.quit()#关闭浏览器

 执行结果

4.通过JS脚本,修改页面元素的属性值

from selenium import webdriver
from time import sleepdriver = webdriver.Chrome()#创建浏览器对象
driver.maximize_window()#窗口最大化driver.get(r'https://www.baidu.com')#访问百度
el = driver.find_element('id','su') #页面的百度一下元素js = "return arguments[0].setAttribute('value','AAAAA')" #JS脚本的意思为:返回某个元素的value属性的值
driver.execute_script(js,el)#执行JS脚本,传入的2个参数依次为:要执行的脚本、要操作的对象
sleep(10)
driver.quit()#关闭浏览器

 执行结果

5.执行JS脚本,进行滚动条操作

from selenium import webdriver
from time import sleepdriver = webdriver.Chrome()#创建浏览器对象
driver.maximize_window()#窗口最大化
driver.implicitly_wait(30)driver.get(r'https://www.baidu.com')#访问百度
driver.find_element('id','kw').send_keys("它石科技")
driver.find_element('id','su').click()
sleep(3)
js = "document.documentElement.scrollTop=1000" #向下滚动
driver.execute_script(js)#执行JS脚本
sleep(3)
js1 = "document.documentElement.scrollTop=0" #先上滑动
driver.execute_script(js1)#执行JS脚本
sleep(3)
js2 = "window.scrollTo(100,200)" #左右滑动
driver.execute_script(js2)
sleep(3)
js3 = "arguments[0].scrollIntoView()"
el = driver.find_element('xpath','//span[text()="百度热搜"]')
sleep(1)
driver.execute_script(js3,el)
sleep(3)
driver.quit()#关闭浏览器

 

执行结果:

由于是动态的图,这里就不截图了,大家可以按照代码自己尝试

总结:

selenium底层就方式通过执行JS脚本来操作页面元素的,所以当我们使用普通封装的常规操作方法无法进行相关操作时,可以使用JS执行器来达到我们的操作目的。

3.logging模块的封装处理

具体封装好的log.py文件代码如下:

import logging
import os
import time
class Logging():
def make_log_dir(self,dirname='logs'): #创建存放日志的目录,并返回目录的路径
now_dir = os.path.dirname(__file__)
father_path = os.path.split(now_dir)[0]
path = os.path.join(father_path,dirname)
path = os.path.normpath(path)
if not os.path.exists(path):
os.mkdir(path)
return path
def get_log_filename(self):#创建日志文件的文件名格式,便于区分每天的日志
filename = "{}.log".format(time.strftime("%Y-%m-%d",time.localtime()))
filename = os.path.join(self.make_log_dir(),filename)
filename = os.path.normpath(filename)
return filename
def log(self):#生成日志的主方法
logger = logging.getLogger()
logger.setLevel(logging.INFO)
if not logger.handlers: #作用:防止日志重打
sh = logging.StreamHandler()
fh =
logging.FileHandler(filename=self.get_log_filename(),mode='a',encoding="utf-8")
fmt = logging.Formatter("%(asctime)s-%(levelname)s-%(filename)s-
Line:%(lineno)d-Message:%(message)s")
sh.setFormatter(fmt=fmt)
fh.setFormatter(fmt=fmt)
logger.addHandler(sh)
logger.addHandler(fh)
return logger
if __name__ == '__main__':
#Logging().log().debug("1111111111111111111111")
Logging().log().info("222222222222222222222222")
Logging().log().error("附件为IP飞机外婆家二分IP文件放")

selenium中的非input型上传

我们在之前的文章中介绍了有四种方式可以实现,接下来我们展开说明一下实现

win32gui

废话不多说,上代码先:

示例网址:http://www.sahitest.com/demo/php/fileUpload.htm

# -*- coding: utf-8 -*-
from selenium import webdriver
import win32gui
import win32con
import timedr = webdriver.Firefox()
dr.get('http://sahitest.com/demo/php/fileUpload.htm')
upload = dr.find_element_by_id('file')
upload.click()
time.sleep(1)# win32gui
dialog = win32gui.FindWindow('#32770', u'文件上传')  # 对话框
ComboBoxEx32 = win32gui.FindWindowEx(dialog, 0, 'ComboBoxEx32', None) 
ComboBox = win32gui.FindWindowEx(ComboBoxEx32, 0, 'ComboBox', None)
Edit = win32gui.FindWindowEx(ComboBox, 0, 'Edit', None)  # 上面三句依次寻找对象,直到找到输入框Edit对象的句柄
button = win32gui.FindWindowEx(dialog, 0, 'Button', None)  # 确定按钮Buttonwin32gui.SendMessage(Edit, win32con.WM_SETTEXT, None, 'd:\\baidu.py')  # 往输入框输入绝对地址
win32gui.SendMessage(dialog, win32con.WM_COMMAND, 1, button)  # 按buttonprint upload.get_attribute('value')
dr.quit()

结果:

baidu.py

在这里你需要一个非常重要的小工具:Spy++,百度一下有很多,当然你也可以用autoIT自带的工具,不过没有这个好用,建议去下一个吧。

而且,你得安装pywin32的库,你可以到这里找到对应你Python版本的库,注意32位还是64位一定要和你安装的Python版本对应。

安装完成之后在【开始菜单Python的文件夹】里看到PyWin32的文档【Python for GitCode - 全球开发者的开源社区,开源代码托管平台 Documentation】,你能从中找到对应的方法API。

简单介绍几个用到的:

win32gui.FindWindow(lpClassName=None, lpWindowName=None):

自顶层窗口开始寻找匹配条件的窗口,并返回这个窗口的句柄。
lpClassName:类名,在Spy++里能够看到
lpWindowName:窗口名,标题栏上能看到的名字
代码示例里我们用来寻找上传窗口,你可以只用其中的一个,用classname定位容易被其他东西干扰,用windowname定位不稳定,不同的上传对话框可能window_name不同,怎么定位取决于你的情况。
win32gui.FindWindowEx(hwndParent=0, hwndChildAfter=0, lpszClass=None, lpszWindow=None)

搜索类名和窗体名匹配的窗体,并返回这个窗体的句柄。找不到就返回0。
hwndParent:若不为0,则搜索句柄为hwndParent窗体的子窗体。
hwndChildAfter:若不为0,则按照z-index的顺序从hwndChildAfter向后开始搜索子窗体,否则从第一个子窗体开始搜索。
lpClassName:字符型,是窗体的类名,这个可以在Spy++里找到。
lpWindowName:字符型,是窗口名,也就是标题栏上你能看见的那个标题。
代码示例里我们用来层层寻找输入框和寻找确定按钮
win32gui.SendMessage(hWnd, Msg, wParam, lParam)

hWnd:整型,接收消息的窗体句柄
Msg:整型,要发送的消息,这些消息都是windows预先定义好的,可以参见系统定义消息(System-Defined Messages)
wParam:整型,消息的wParam参数
lParam:整型,消息的lParam参数
代码示例里我们用来向输入框输入文件地址以及点击确定按钮
至于win32api模块以及其他的方法,这里不进行更多描述,想要了解的自行百度或看pywin32文档。

SendKeys

首先要安装SendKeys库,可以用pip安装

pip install SendKeys

代码示例:

示例网址:http://www.sahitest.com/demo/php/fileUpload.htm

# -*- coding: utf-8 -*-
from selenium import webdriver
import win32gui
import win32con
import timedr = webdriver.Firefox()
dr.get('http://sahitest.com/demo/php/fileUpload.htm')
upload = dr.find_element_by_id('file')
upload.click()
time.sleep(1)# SendKeys
SendKeys.SendKeys('D:\\baidu.py')  # 发送文件地址
SendKeys.SendKeys("{ENTER}") # 发送回车键print upload.get_attribute('value')
dr.quit()

 

结果:

baidu.py
1
通过SendKeys库可以直接向焦点里输入信息,不过要注意在打开窗口是略微加一点等待时间,否则容易第一个字母send不进去(或者你可以在地址之前加一个无用字符),不过我觉得这种方法很不稳定,不推荐。

keybd_event

win32api提供了一个keybd_event()方法模拟按键,不过此方法比较麻烦,也不稳定,所以很不推荐,下面给出部分代码示例,如果想要研究,可以自己研究研究

...# 先找一个input框,输入想要上传的文件的地址,剪切到剪贴板 
video.send_keys('C:\\Users\\Administrator\\Pictures\\04b20919fc78baf41fc993fd8ee2c5c9.jpg')
video.send_keys(Keys.CONTROL, 'a')  # selenium的send_keys(ctrl+a)
video.send_keys(Keys.CONTROL, 'x')  # (ctrl+x)
driver.find_element_by_id('uploadImage').click()  # 点击上传按钮,打开上传框# 粘贴(ctrl + v)
win32api.keybd_event(17, 0, 0, 0)  # 按下按键 ctrl
win32api.keybd_event(86, 0, 0, 0)  # 按下按键 v
win32api.keybd_event(86, 0, win32con.KEYEVENTF_KEYUP, 0)  # 升起按键 v
win32api.keybd_event(17, 0, win32con.KEYEVENTF_KEYUP, 0)  # 升起按键 ctrl
time.sleep(1)# 回车(enter)
win32api.keybd_event(13, 0, 0, 0)  # 按下按键 enter
win32api.keybd_event(13, 0, win32con.KEYEVENTF_KEYUP, 0)  # 升起按键 enter...

是不是很麻烦,当然,你甚至可以用按键把整个路径输入进去,不过,我想没人愿意这么做的。而且在此过程中你不能随意移动鼠标,不能使用剪贴板,太不稳定了,所以非常不建议你用这种办法。

多文件上传
接下来还有一种情况值得我们考虑,那就是多文件上传。如何上传多个文件,当然我们还是往输入框里输入文件路径,所以唯一要搞清楚的就是多文件上传时,文件路径是怎么写的。

我来告诉你吧,多文件上传就是在文件路径框里用引号括起单个路径,然后用逗号隔开多个路径,就是这么简单,例如:
“D:\a.txt” “D:\b.txt”
但需要注意的是:只有多个文件在同一路径下,才能这样用,否则是会失败的(下面的写法是不可以的):
“C:\a.txt” “D:\b.txt”

接下里找一个例子试试:

# -*- coding: utf-8 -*-from selenium import webdriver
import win32gui
import win32con
import timedr = webdriver.Firefox()
dr.get('http://www.sucaijiayuan.com/api/demo.php?url=/demo/20150128-1')dr.switch_to.frame('iframe')  # 一定要注意frame
dr.find_element_by_class_name('filePicker').click()
time.sleep(1)dialog = win32gui.FindWindow('#32770', None)
ComboBoxEx32 = win32gui.FindWindowEx(dialog, 0, 'ComboBoxEx32', None)
ComboBox = win32gui.FindWindowEx(ComboBoxEx32, 0, 'ComboBox', None)
Edit = win32gui.FindWindowEx(ComboBox, 0, 'Edit', None)
button = win32gui.FindWindowEx(dialog, 0, 'Button', None)# 跟上面示例的代码是一样的,只是这里传入的参数不同,如果愿意可以写一个上传函数把上传功能封装起来
win32gui.SendMessage(Edit, win32con.WM_SETTEXT, 0, '"d:\\baidu.py" "d:\\upload.py" "d:\\1.html"')
win32gui.SendMessage(dialog, win32con.WM_COMMAND, 1, button)print dr.find_element_by_id('status_info').text
dr.quit()

结果:

选中3张文件,共1.17KB。
可见,多文件上传并没有那么复杂,也很简单,唯一的区别就是输入的参数不同而已。autoIT也可以实现,有兴趣可以自己试试。

而且我们可以发现一点,就是上面的这个窗口的代码跟之前示例中的基本是一样,说明我们可以把上传的部分抽出来,写一个函数,这样每次要上传,直接去调用函数,传入参数即可。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/78606.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解析Suna:全球首款开源通用AI智能体

导语: 嘿,哥们儿,最近 AI Agent 这块儿挺火的,有个叫 Suna 的开源项目冒出来挺快!听说只用了 3 周就开发出来了,但功能上感觉已经能跟那个商业版的 Manus掰掰手腕了。它能帮你搞定浏览器自动化、管文件、爬…

模板方法模式:定义算法骨架的设计模式

模板方法模式:定义算法骨架的设计模式 一、模式核心:模板方法定义算法骨架,具体步骤延迟到子类实现 在软件开发中,经常会遇到这样的情况:某个算法的步骤是固定的,但具体步骤的实现可能因不同情况而有所不…

浅谈Java 内存管理:栈与堆,垃圾回收

在Java编程世界里,内存管理是一项极为关键的技能,它就像程序运行背后的“隐形守护者”,默默影响着程序的性能与稳定性。今天,咱们就来简单学习一下Java内存管理中的两大核心要点:栈与堆的内存分配机制,以及…

【WebGL小知识】WebGL平台上不同Json的比较

今天来总结一下WebGL平台上不同Json插件的差别,话不多说直接开始。 JsonUtility JsonUtility是Unity自带的Json解析,无需另外安装插件。 优点: Unity自带,兼容性好,WebGL平台可以使用轻量级,性能较好。 …

4.22tx视频后台开发一面

总时长大概在一个小时,主要提问C、操作系统、计网以及数据库等方面,最后两个算法编程题。 一上来先介绍项目 Linux下的mybash命令处理器和内存池 mybash可以再总结归纳一下,一上来有点紧张没有条理 内存池是用边界标识法写的,…

从StandardMaterial和PBRMaterial到PBRMetallicRoughnessMaterial:Babylon.js材质转换完全指南

在现代3D图形开发中,基于物理的渲染(PBR)已成为行业标准。本文将深入探讨如何在Babylon.js中将传统StandardMaterial和PBRMaterial转换为PBRMetallicRoughnessMaterial,并保持视觉一致性。 为什么需要转换? PBRMetallicRoughnessMaterial作…

UEditor文档在Servlet项目上的应用

UEditor 是一款功能强大的富文本编辑器,在项目中应用广泛。 Ueditor使用 引入 UEditor 下载 UEditor:从 UEditor 官方网站(ueditor 官网)下载适合项目需求的版本。解压文件:将下载的压缩包解压到项目的静态资源目录…

ThinkPHP快速使用手册

目录 介绍 安装(windows环境) 安装Composer 安装ThinkPHP 目录结构 配置文件 第一个接口(Controller层) Hello World 自定义Controller 请求参数 获取查询参数(Get请求) 获取指定请求参数 获取…

面向 C# 初学者的完整教程

🧱 一、项目结构说明 你的项目大致结构如下: TaskManager/ ├── backend/ │ ├── TaskManager.Core/ // 实体类和接口 │ ├── TaskManager.Infrastructure/ // 数据库、服务实现 │ └── TaskManager.API/ // We…

Axios 的 GET 和 POST 请求:前端开发中的 HTTP 通信

🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 🍚 蓝桥云课签约作者、…

【前端】如何检查内存泄漏

在实际的场景中,如果观察到内存持续出现峰值,并且内存消耗一直没有减少,那可能存在内存泄漏。 使用 Chrome DevTools 来识别内存图和一些内存泄漏,我们需要关注以下两个方面: ● 使用性能分析器可视化内存消耗&#xf…

JavaScript的JSON处理Map的弊端

直接使用 Map 会遇到的问题及解决方案 直接使用 Map 会导致数据丢失,因为 JSON.stringify 无法序列化 Map。以下是详细分析及解决方法: 问题复现 // 示例代码 const myMap new Map(); myMap.set(user1, { name: Alice }); myMap.set(user2, { name: B…

【数据结构】第五弹——Stack 和 Queue

文章目录 一. 栈(Stack)1.1 概念1.2 栈的使用1.3 栈的模拟实现1.3.1 顺序表结构1.3.2 进栈 压栈1.3.3 删除栈顶元素1.3.4 获取栈顶元素1.3.5 自定义异常 1.4 栈的应用场景1.改变元素序列2. 将递归转化为循环3. 四道习题 1.5 概念分区 二. 队列(Queue)2.1 概念2.2 队列的使用2.3…

第七届能源系统与电气电力国际学术会议(ICESEP 2025)

重要信息 时间:2025年6月20-22日 地点:中国-武汉 官网:www.icesep.net 主题 能源系统 节能技术、能源存储技术、可再生能源、热能与动力工程 、能源工程、可再生能源技术和系统、风力发…

深入解析C++ STL Stack:后进先出的数据结构

一、引言 在计算机科学中,栈(Stack)作为一种遵循后进先出(LIFO)​原则的数据结构,是算法设计和程序开发的基础构件。C STL中的stack容器适配器以简洁的接口封装了底层容器的操作,为开发者提供了…

Golang | 自行实现并发安全的Map

核心思路,读写map之前加锁!哈希思路,大map化分为很多个小map

Mac 「brew」快速安装MySQL

安装MySQL 在 macOS 上安装 MySQL 环境可以通过Homebrew快速实现,以下是步骤指南: 方法 1:使用 Homebrew 安装 MySQL 1. 安装 Homebrew 如果尚未安装 Homebrew,可以通过以下命令安装: /bin/bash -c "$(curl -…

【数字孪生世界的搭建之旅:从0到1理解飞渡平台】

数字孪生世界的搭建之旅:从0到1理解飞渡平台 前言:数字分身的魔法 想象一下,如果你能在现实世界之外,创造一个物理世界的"分身",这个分身能完美复制现实中的一切变化,甚至可以预测未来可能发生…

【漏洞复现】Struts2系列

【漏洞复现】Struts2系列 1. 了解Struts21. Struts2 S2-061 RCE (CVE-2020-17530)1. 漏洞描述2. 影响版本3. 复现过程 1. 了解Struts2 Apache Struts2是一个基于MVC设计模式的Web应用框架,会对某些标签属性(比如 id)的…

[FPGA Video IP] Video Processing Subsystem

Xilinx Video Processing Subsystem IP (PG231) 详细介绍 概述 Xilinx LogiCORE™ IP Video Processing Subsystem (VPSS)(PG231)是一个高度可配置的视频处理模块,设计用于在单一 IP 核中集成多种视频处理功能,包括缩放&#xf…