Web自动化测试(二)

Menu: 文件上传弹框处理

文件上传、弹框处理

文件上传:
	input标签可以直接使用send_keys(文件地址)上传文件
用法:
	el = driver.find_element_by_id('上传按钮id')
	el.send_keys("文件路径+文件名")
	
文件上传案例:
	打开百度图片网址:https://image.baidu.com
	识别上传按钮
	点击上传按钮
	将本地的图片文件上传
import time
from selenium_file_alert.base import Base

class TestFile(Base):                   #继承Base()方法,不需要初始化driver
    '''
    打开百度图片网址:https://image.baidu.com
	识别上传按钮
	点击上传按钮
	将本地的图片文件上传
    '''
    def test_file_upload(self):
        self.driver.get("https://image.baidu.com/")
        #点击打开文件上传弹框
        self.driver.find_element_by_css_selector('.st_camera_off').click()
        el = self.driver.find_element_by_id('stfile')
        # el.send_keys("C:/Users/Banana/Desktop/dog.jpg")
        el.send_keys(r"C:\Users\Banana\Desktop\dog.jpg")        #复制的本地路径使用r参数防止字符转义
        time.sleep(5)
弹框处理机制
	在页面操作中有时会碰到JavaScript所生成的alert、confirm以及prompt弹框,
可以使用swith_to.alert()方法定位到。然后用text/accept/dismiss/send_keys等
方法进行操作。参考分辨alert(F12 console: window.alert("hello"))、
window(点击某个链接新打开window窗口)、div模态框(例如百度页面的登录,
div页面会非常复杂有很多渲染和链接),以及操作。

操作alert常用的方法:
	switch_to.alert()		#获取当前页面上的警告框
	text					#返回alert/confirm/prompt中的文字信息
	accept()				#接受现有警告框,也就是点击确定按钮
	dismiss()				#取消现有警告框,也就是点击取消按钮
	send_keys(keysToSend)	#发送文本至警告框。keyToSend:将文本发送至警告框。
	
alert窗口处理案例:
	打开网页https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable
	操作窗口右侧页面,将元素1拖拽到元素2 
	这时候会有一个alert弹框,点击弹框中的‘确定’
	然后再点击‘点击运行’
	关闭网页
import time
from selenium.webdriver import ActionChains
from selenium_file_alert.base import Base

class TestAlert(Base):      	#继承Base()方法,不需要初始化driver
    '''
    打开网页https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable
	操作窗口右侧页面,将元素1拖拽到元素2
	这时候会有一个alert弹框,点击弹框中的‘确定’
	然后再点击‘点击运行’
	关闭网页
    '''
    def test_alert(self):
        self.driver.get("https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable")
        #切换frame
        self.driver.switch_to.frame('iframeResult')
        dragele = self.driver.find_element_by_id('draggable')
        dropele = self.driver.find_element_by_id('droppable')

        action = ActionChains(self.driver)
        #拖拽后记得perform()
        action.drag_and_drop(dragele,dropele).perform()
        time.sleep(3)

        #获取页面警告框,点击 【确定】 按钮
        self.driver.switch_to_alert().accept()

        #切换到父节点frame
        self.driver.switch_to.parent_frame()

        #点击 【点击运行】按钮
        self.driver.find_element_by_id("submitBTN").click()

Menu:Page Object设计模式

案例抽象化
	操作细节
	结果验证
	操作细节
	结果验证
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
PageObject
	测试:操作细节、验证

如果case中存在大量的find_element 界面发生变更,case需要重写 为此我们需要采用PO模式
如果采用PO模式,比如登录,登录的按钮变了,只用在登录对应的地方修改,所有的case是不需要修改的

https://github.com/SeleniumHQ/selenium/wiki/PageObjects
PO六大原则
The public methods represent the services that the page offers   公共方法代表页面提供的服务
Try not to expose the internals of the page                      不要暴露页面的内部细节
Generally don't make assertions                                  通常不做断言
Methods return other PageObjects                                 方法返回其他PageObjects
Need not represent an entire page                                不需要将整个页面功能全部设计出来
Different results for the same action are modelled as different methods    相同动作的不同结果被建模为不同的方法

Menu:Page Object原则

https://github.com/SeleniumHQ/selenium/wiki/PageObjects
PO六大原则
The public methods represent the services that the page offers   		   #公共方法代表页面提供的服务
	#栗子:
	#百度页面很多功能,一个方法click()代表搜索,一个方法sendkeys()代表输入框
Try not to expose the internals of the page                      		   #不要暴露页面的内部细节
Generally don't make assertions                                  		   #通常不做断言
Methods return other PageObjects                                 		   #方法返回其他PageObjects
	#栗子:
	#百度点一个页面弹出一个页面,那么这个页面就应该return弹出的页面
	#如果页面A导航到页面B,Page A应当return Page B 
Need not represent an entire page                                		   #不需要将整个页面功能全部设计出来
Different results for the same action are modelled as different methods    #相同动作的不同结果被建模为不同的方法
	#栗子:
	#404err 和200 设计俩个方法,分别代表正确的页面和错误的页面。不要把正确的页面和错误的页面写在一个页面里

Menu:企业微信的自动化登录

一、使用remote复用已有的浏览器 主要功能也是调试使用
二、使用cookie登录

一、使用remote复用已有的浏览器:
	对当前页面进行测试,不希望重新打开浏览器,不希望进行前面的所有步骤后再执行该页面的操作。通过chromedriver开启debug模式即可实现
		1.在已有chrome浏览器上打开调试
		2.修改selenium代码,适应该调试
	
	详细步骤:
		1.环境变量加入chrome可执行文件地址,也就是chrome运行文件加入到环境变量			#加入到环境变量后,cmd输入chrome(可执行文件名称),如果可运行证明添加成功
		#注意添加环境变量是将文件所在目录添加到环境变量,不是将文件可执行路径加入到环境变量,例如 C:\Program Files\Google\Chrome\Application\chrome.exe 只需要将  C:\Program Files\Google\Chrome\Application 加入到path环境变量即可
		2.chrome --remote-debugging-port=9222				#命令行运行,打开浏览器调试时,一定要关闭所有chrome浏览器,这时运行后会打开一个9222的chrome窗口
		3.代码也需要加入 9222 debug的调试地址					#和浏览器打开的调试端口进行通信,实现已有浏览器的复用
#使用remote复用已有的浏览器
# Generated by Selenium IDE
import time

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By


class Test():
    def setup_method(self):
        options = Options()
        #和浏览器打开的调试端口进行通信
        #浏览器要使用 --remote-debugging-port=9222 开启调试
        options.debugger_address = "127.0.0.1:9222"
        self.driver = webdriver.Chrome(options=options)         #和浏览器打开的调试端口进行通信,实现已有浏览器的复用

    def tear_down(self):
        #self.driver.quit()                                      #可不写,以防将通信浏览器窗口关掉
        pass

    def test_baidu(self):
        self.driver.get("https://www.baidu.com/")
        self.driver.find_element(By.ID, "kw").send_keys("霍格沃兹测试学院")
        self.driver.find_element(By.ID, "su").click()
        time.sleep(2)
        self.driver.find_element(By.PARTIAL_LINK_TEXT, "软件自动化测试开发培训_接口性能测试").click()

Menu:企业微信的自动化登录2

二、使用cookie登录
	driver.get(url)
	driver.delete_all_cookies()
	for cookie in cookies:
		driver.add_cookie(cookie)
	driver.refresh()
	
	
shelve	#python自带的小型数据库
db = shelve.open("cookies")		#如果本地没有cookies数据库,会创建一个名称为cookies的小型数据库。如果本地有cookies数据库,那么就会直接读取

小型数据库如何更新数据?将数据库的key重新赋值即可,对整体进行更新

====================
1.先通过复用浏览器获取登录的cookies,进行打印。通过变量保存登录的cookies
2.将获取的cookies通过小型数据库shelve进行保存后,保存完成关闭小型数据库
3.取消使用复用浏览器,用新窗口打开需要访问的网址(给定url),打开小型数据库shelve,读取数据,
移除cookies中的expiry后,将剩下的cookie注入到浏览器cookie上。关闭小型数据库shelve。
====================

#参考链接:https://ceshiren.com/t/topic/3832


# Generated by Selenium IDE
import shelve
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By


class Test():
    def setup_method(self):
        options = Options()
        #和浏览器打开的调试端口进行通信
        #浏览器要使用 --remote-debugging-port=9222 开启调试
        options.debugger_address = "127.0.0.1:9222"
        # self.driver = webdriver.Chrome(options=options)         #和浏览器打开的调试端口进行通信,实现已有浏览器的复用
        self.driver = webdriver.Chrome()        #使用cookies,不使用复用浏览器

    def test_wework(self):
		'''
        1.
        先通过复用浏览器获取登录的cookies, 进行打印。通过变量保存登录的cookies
        2.
        将获取的cookies通过小型数据库shelve进行保存后,保存完成关闭小型数据库
        3.
        取消使用复用浏览器,用新窗口打开需要访问的网址(给定url),打开小型数据库shelve, 读取数据,
        移除cookies中的expiry后,将剩下的cookie注入到浏览器cookie上。关闭小型数据库shelve。
        '''
        # self.driver.get("https://work.weixin.qq.com/wework_admin/frame#index")
        #复用浏览器时,识别的是当前打开的窗口页面,类似于句柄,识别的是当前句柄
        # self.driver.find_element(By.ID,'menu_contacts').click()     #复用浏览器,浏览器当前窗口为企业微信登录主页,此时复用浏览器也不需要指定访问页面链接,给定元素定位方式即可操作
        #print(self.driver.get_cookies())        #使用复用的浏览器打印页面的cookies
        #打印的cookies结果如下:
        #注意查看domain是否是当前页面domain
        # cookies = [{'domain': '.work.weixin.qq.com', 'httpOnly': True, 'name': 'wwrtx.vst', 'path': '/', 'secure': False, 'value': 'QHVVs-2wzIL4rJjmY3wob1l0ig6tXr57X2AUVnqy_kkxyBdA4VdupnL4gIKgMVXKTuw2beIgPe5lO8FuK5_orfnF97CrZekiYltq5uDTEJSjs-fUY5bqqqZ3uDtdGdETj39cRnXsVnX0pBjl-vhkt0X4fBR_cWKOhkMAiXH6OaxUrrkhT4pxuyccuzLjIF0_Zi9HusVw1XLEca5EEdy8sLP_bjAMxsJN6OlcyS8tf-Z7lK8wvl_hSe0k71ZwUxG74fzL6CduIkr5KvnqHeDOvw'}, {'domain': '.work.weixin.qq.com', 'httpOnly': False, 'name': 'wwrtx.cs_ind', 'path': '/', 'secure': False, 'value': ''}, {'domain': '.work.weixin.qq.com', 'httpOnly': False, 'name': 'wwrtx.vid', 'path': '/', 'secure': False, 'value': '1688851169374160'}, {'domain': '.work.weixin.qq.com', 'httpOnly': False, 'name': 'wxpay.vid', 'path': '/', 'secure': False, 'value': '1688851169374160'}, {'domain': '.work.weixin.qq.com', 'httpOnly': False, 'name': 'wxpay.corpid', 'path': '/', 'secure': False, 'value': '1970325082099192'}, {'domain': '.work.weixin.qq.com', 'httpOnly': True, 'name': 'wwrtx.sid', 'path': '/', 'secure': False, 'value': 'BgQbApgB5Cv3-0pB0wUKylNi9zU150nDOwRQiIWnTnr6ZM6OY5YuUWSccsk0f0WJ'}, {'domain': '.work.weixin.qq.com', 'httpOnly': False, 'name': 'wwrtx.d2st', 'path': '/', 'secure': False, 'value': 'a3875345'}, {'domain': '.qq.com', 'expiry': 1683389247, 'httpOnly': False, 'name': '_ga', 'path': '/', 'secure': False, 'value': 'GA1.2.1823337344.1620312436'}, {'domain': '.work.weixin.qq.com', 'httpOnly': False, 'name': 'Hm_lpvt_9364e629af24cb52acc78b43e8c9f77d', 'path': '/', 'secure': False, 'value': '1620312435'}, {'domain': '.work.weixin.qq.com', 'httpOnly': True, 'name': 'wwrtx.refid', 'path': '/', 'secure': False, 'value': '22550649492593767'}, {'domain': '.qq.com', 'expiry': 1620403647, 'httpOnly': False, 'name': '_gid', 'path': '/', 'secure': False, 'value': 'GA1.2.329605108.1620312436'}, {'domain': 'work.weixin.qq.com', 'expiry': 1620343967, 'httpOnly': True, 'name': 'ww_rtkey', 'path': '/', 'secure': False, 'value': '4u2d0gs'}, {'domain': '.work.weixin.qq.com', 'expiry': 1651848431, 'httpOnly': False, 'name': 'wwrtx.c_gdpr', 'path': '/', 'secure': False, 'value': '0'}, {'domain': '.qq.com', 'expiry': 2147483864, 'httpOnly': False, 'name': 'ptcz', 'path': '/', 'secure': False, 'value': '404395857d0956cd67e5dc882ce36fa983ffc0e12868c7bb44551137c04ea0c0'}, {'domain': '.work.weixin.qq.com', 'httpOnly': True, 'name': 'wwrtx.ltype', 'path': '/', 'secure': False, 'value': '1'}, {'domain': '.qq.com', 'expiry': 2147385600, 'httpOnly': False, 'name': 'pgv_pvid', 'path': '/', 'secure': False, 'value': '1892346730'}, {'domain': '.work.weixin.qq.com', 'expiry': 1651848435, 'httpOnly': False, 'name': 'Hm_lvt_9364e629af24cb52acc78b43e8c9f77d', 'path': '/', 'secure': False, 'value': '1620312435'}, {'domain': '.work.weixin.qq.com', 'httpOnly': True, 'name': 'wwrtx.ref', 'path': '/', 'secure': False, 'value': 'direct'}, {'domain': '.qq.com', 'expiry': 1646627414, 'httpOnly': True, 'name': '_tc_unionid', 'path': '/', 'secure': False, 'value': '4221d27e-d471-4076-9915-90cf48cab78c'}, {'domain': '.qq.com', 'expiry': 2147483864, 'httpOnly': False, 'name': 'RK', 'path': '/', 'secure': False, 'value': 'KcAo+ePfWz'}, {'domain': '.qq.com', 'expiry': 1622302027, 'httpOnly': False, 'name': 'ptui_loginuin', 'path': '/', 'secure': False, 'value': '857552341'}, {'domain': '.work.weixin.qq.com', 'expiry': 1622909251, 'httpOnly': False, 'name': 'wwrtx.i18n_lan', 'path': '/', 'secure': False, 'value': 'zh'}]

        self.driver.get("https://work.weixin.qq.com/")      #不通过复用浏览器,需要给定url
        # # 创建或者打开一个数据库
        db = shelve.open("auth_cookie")          #如果本地没有auth_cookie数据库,会创建一个名称为auth_cookie的小型数据库,会生成相应的二进制文件。如果本地有auth_cookie数据库,那么就会直接读取
        # 将数据存储到 shelve 中
        # db["auth_cookie"] = cookies           #将cookies保存一次后,即可进行注释
        # 取出数据
        cookies = db["auth_cookie"]
        for cookie in cookies:
            if "expiry" in cookie.keys():
            	#移除cookie中的expiry,expiry也是类似于get、post这种规范命名
                cookie.pop("expiry")
            # 把字典加入到 driver 的 cookie 中
            self.driver.add_cookie(cookie)
        self.driver.get("https://work.weixin.qq.com/wework_admin/frame")
        self.driver.find_element(By.XPATH, "//*[@id='menu_contacts']").click()
        db.close()                          #使用完数据库数据关闭数据库

Menu:Page Object演练1

首页pageobject
	1.立即注册
		点击立即注册
		return立即注册pageobject
	2.企业登录
		点击企业登录
		return企业登录PageObject

企业登录pageobject
	1.扫码
		用手机扫描二维码
	2.立即注册
		点击企业注册
		return立即注册pageobject
		
注册页pageobject
	1.填表
		输入文本
		点击下拉框
		点击确定
#见下面详情图

在这里插入图片描述

#案例一:上面的流程图演练	实现链式调用
#案例二:企业微信首页(首页包含添加成员功能)和通讯录添加成员页面 
复用浏览器,跳过登录操作,使用企业微信首页(首页包含添加成员功能)和通讯录添加成员页面 
实现链式调用,同时
1.引进base_page初始化driver判断driver是否存在,如果不存在就进行新建
2.driver的操作都封装在base_page,例如find_element封装在base_page中
3.对改造1里面的driver进行处理,直接判断是否进行了复用		#属于优化代码,待后续补充

注意:
	1.跳转到的页面不用每次都初始化driver,将上一个页面的driver进行传递,跳转的页面直接__init__初始化使用即可
	2.跳转的页面使用上一个页面driver时,查找元素没有提示,此时我们需要在__init__方法里面给个标识即可,相当于告诉编译器这个注释表达意思是个WebDriver
		
#Pycharm使用技巧		
Pycharm进行全局搜索:Edit->Find->Find in Path...
			
所有PO页面都继承basepage这个基类
self._driver 		#私有变量,保护属性,注意单个下划线开头和俩个下划线开头的区别

所有子类实现自己的构造方法,调用自己的方法时,都会主动去调用父类的__init__方法。如果子类自己写了__init__方法会覆盖掉父类的__init__方法
#add_member里面没有__init__方法,但是它继承了base_page,所以会调用base_page的__init__方法		
			
上面的案例:所有子类PO共用了一个driver,如果一个子类PO修改了driver,那么依靠上面的单例模式就无法正常进行链式调用
存在潜在隐患:子类PO修改了driver,单例模式就无法正常进行链式调用

改进:所有关于driver的操作都封装在base_page上
	例如find_element封装在base_page中,其他页面直接调用find方法即可
			
isReuse = False    	#对driver进行改造,待后面进行处理

Menu:Page Object演练2

改造:
	1.所有PO页面都继承basepage这个基类,共用basepage里面的driver
		所有子类再实现自己的构造方法,调用自己的方法时,都会主动去调用父类的__init__方法。
		如果子类自己写了__init__方法会覆盖掉父类的__init__方法
	2.所有关于driver的操作都封装在base_page上
		上面的案例:所有子类PO共用了一个driver,如果一个子类PO修改了driver,那么依靠上面的单例模式就无法正常进行链式调用
存在潜在隐患:子类PO修改了driver,单例模式就无法正常进行链式调用
		改进:所有关于driver的操作都封装在base_page上
		例如find_element封装在base_page中,其他页面直接调用find方法即可
	3.对改造1里面的driver进行处理,直接判断是否进行了复用		#属于优化代码,待后续补充
		isReuse = False 									#对driver进行改造,待后面进行处理

33min

#案例一:上面的流程图演练	实现链式调用
#index.py文件内容			#index PO
from selenium import webdriver
from web.test_wework_login.login import Login
from web.test_wework_login.register import Register

class Index:
    ##案例一:上面的流程图演练	实现链式调用
    #首页PO

    def __init__(self):
        self.driver = webdriver.Chrome()
        self.driver.get("https://work.weixin.qq.com/")
        self.driver.implicitly_wait(3)

    def goto_register(self):
        '''
        1.立即注册
		    点击立即注册
		    return立即注册pageobject
        :return:
        '''
        self.driver.find_element_by_css_selector('.index_head_info_pCDownloadBtn').click()
        return Register(self.driver)       #跳转页面时将driver传递过去,实现下一个页面复用driver

    def goto_login(self):
        '''
        2.企业登录
		    点击企业登录
		    return企业登录PageObject
        :return:
        '''
        self.driver.find_element_by_css_selector('.index_top_operation_loginBtn').click()
        return Login(self.driver)           #跳转页面时将driver传递过去,实现下一个页面复用driver
#案例一:上面的流程图演练	实现链式调用
#login.py文件内容			#login PO
from selenium import webdriver
from selenium.webdriver.remote.webdriver import WebDriver
from web.test_wework_login.register import Register

class Login:
    #跳转的页面使用上一个页面driver时,查找元素没有提示,此时我们需要在__init__方法里面给个标识即可,相当于告诉编译器这个注释表达意思是个WebDriver
    #注意WebDriver的书写方式,不要写成全小写
    def __init__(self,driver:WebDriver):
        #跳转到的页面不用每次都初始化driver,将上一个页面的driver进行传递,跳转的页面直接__init__初始化使用即可
        self.driver = driver


    #登录PO
    def scan(self):
        '''
        1.扫码
		    用手机扫描二维码
        :return:
        '''
        pass

    def goto_register(self):
        '''
        2.立即注册
		    点击企业注册
		    return立即注册pageobject
        :return:
        '''

        self.driver.find_element_by_css_selector('.login_registerBar_link').click()
        return Register(self.driver)	
#案例一:上面的流程图演练	实现链式调用
#register.py文件内容		#register PO
from selenium.webdriver.remote.webdriver import WebDriver

class Register:
    # 跳转的页面使用上一个页面driver时,查找元素没有提示,此时我们需要在__init__方法里面给个标识即可,相当于告诉编译器这个注释表达意思是个WebDriver
    # 注意WebDriver的书写方式,不要写成全小写
    def __init__(self,driver:WebDriver):
        #跳转到的页面不用每次都初始化driver,将上一个页面的driver进行传递,跳转的页面直接__init__初始化使用即可
        self.driver = driver

    def register(self):
        '''
        1.填表
		    输入文本
		    点击下拉框
		    点击确定
        :return:
        '''
        self.driver.find_element_by_id("corp_name").send_keys("goodjob")
        self.driver.find_element_by_id("manager_name").send_keys("Mrs zhang")
        self.driver.find_element_by_id("register_tel").send_keys("13000000000")
        self.driver.find_element_by_id("iagree").click()
        return True			#只是用于举例,此处省略表单部分字段填写,直接返回True,保证文件运行通过
#	实现测试
#案例一:上面的流程图演练	实现链式调用
#test_login.py文件内容			#新建一个测试py文件测试login页面
from web.test_wework_login.index import Index

#新建一个测试py文件测试login页面
class TestLogin:

    # def __init__(self):
    #
    #     self.index = Index()

    #注意这里使用的是setup方法,不是__init__方法
    def setup(self):
        self.index = Index()


    def test_login(self):
        result = self.index.goto_login().goto_register().register()
        assert result
#案例二:企业微信首页(首页包含添加成员功能)和通讯录添加成员页面
#复用浏览器,跳过登录操作,使用企业微信首页(首页包含添加成员功能)和通讯录添加成员页面
#base_page.py文件内容
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.remote.webdriver import WebDriver

class BasePage:
    _driver = None
    _base_url = ""

    def __init__(self, driver: WebDriver = None):
        if driver is None:
            chrome_options = Options()
            # 和浏览器打开的调试端口进行通信
            # 浏览器要使用 --remote-debugging-port=9222 开启调试
            chrome_options.debugger_address = "127.0.0.1:9222"
            self._driver = webdriver.Chrome(options=chrome_options)
            self._driver.implicitly_wait(3)
        else:
            self._driver = driver

        if self._base_url != "":
            self._driver.get(self._base_url)
	
	#find_element封装在base_page中
    def find(self,by,locator):      #一个是定位方式by,一个是元素内容locator
        return self._driver.find_element(by,locator)
'''
#案例二:企业微信首页(首页包含添加成员功能)和通讯录添加成员页面
复用浏览器,跳过登录操作,使用企业微信首页(首页包含添加成员功能)和通讯录添加成员页面
实现链式调用,同时
1.引进base_page初始化driver判断driver是否存在,如果不存在就进行新建
2.driver的操作都封装在base_page,例如find_element封装在base_page中
3.对改造1里面的driver进行处理,直接判断是否进行了复用		#属于优化代码,待后续补充
'''
#index.py文件内容

from web.test_wework.base_page import BasePage
from web.test_wework.contact import Contact


class Index(BasePage):

    _base_url = "https://work.weixin.qq.com/wework_admin/frame#index"
    def goto_contact(self):
        self._driver.find_element_by_css_selector('.index_service_cnt_itemWrap:nth-child(1)').click()
        return Contact(self._driver)            #和Contact.py文件内容有关,需要注意
#案例二:企业微信首页(首页包含添加成员功能)和通讯录添加成员页面
#复用浏览器,跳过登录操作,使用企业微信首页(首页包含添加成员功能)和通讯录添加成员页面
#contact.py文件内容
import time
from web.test_wework.base_page import BasePage

class Contact(BasePage):

    #为啥这里还需要进行定义 _base_url?  
    #该页面不需要定义 _base_url 内容,之前页面报错‘ElementNotInteractableException
	#’是由于 首页点击添加成员直接到了添加成员表单页面,故不需要点击 添加成员 元素定位,这样会找不到元素
	#恰好指定的url链接里面有 添加成员 的元素定位,正好跳过了此问题
    # _base_url = "https://work.weixin.qq.com/wework_admin/frame#contacts"
    '''
    添加成员
    '''
    def add_member(self):
        #通过css定位查找到多个元素,默认点击第一个元素
        # time.sleep(3)
        #首页点击添加成员直接到了添加成员表单页面,故不需要点击元素 添加成员,这样会找不到元素,会报错‘ElementNotInteractableException’
        #‘ElementNotInteractableException’ 元素不可交互,元素定位错了
        #self._driver.find_element_by_css_selector('.ww_operationBar .js_add_member').click()		#注释掉即可
        self._driver.find_element_by_id('username').send_keys("Mrs zhang")
        self._driver.find_element_by_id('memberAdd_acctid').send_keys("1818168")
        self._driver.find_element_by_id("memberAdd_phone").send_keys("18516131349")
        # 通过css定位查找到多个元素,默认点击第一个元素
        self._driver.find_element_by_css_selector('.js_btn_save').click()
#案例二:企业微信首页(首页包含添加成员功能)和通讯录添加成员页面
#复用浏览器,跳过登录操作,使用企业微信首页(首页包含添加成员功能)和通讯录添加成员页面
#实现测试  test_index.py文件内容

from web.test_wework.index import Index

class TestIndex:

    def setup(self):
        self.index = Index()

    def test_index(self):
        self.index.goto_contact().add_member()
一、隐式等待、显示等待
隐式等待:对全局有效。当某些元素不出现的时候,我们判断页面该元素不出现时进行处理,那么隐式等待就不好处理了
		
#以下报错请注意:
1、元素不可交互 ‘ElementNotInteractableException’ 		#元素定位错了
2、元素能被点击,被点击过后仍然报错,主逻辑没加载出来,所以仍然报错。这时候我们需要使用显式等待。
3、当某些元素不出现的时候,我们判断页面该元素不出现时进行处理,那么隐式等待就不好处理了,这时候我们需要使用显式等待。

#请注意:
css定位class属性时取一段即可进行定位,而采用XPath进行定位时,需要使用整个Class属性进行定位哦
#该案例可参考企业微信,首页进入通讯录页面,点击【添加成员】时
	# ‘ElementNotInteractableException’ 定位【添加成员】元素时 ,元素定位错了,第一个元素定位错了
	#修复元素定位错误后执行,元素能被点击,被点击过后仍然报错,主逻辑没加载出来
	#定位【添加成员】元素时,使用css定位和XPath定位,css定位class属性时取一段即可进行定位,而采用XPath进行定位时,需要使用整个Class属性进行定位哦 	

8.企业微信web端自动化测试实战

base_page页面的显式等待方法  传一个元组(既包含了定位方式,又包含了定位内容)
		
		WebDriverWait里面有个方法属性是元素能被点击:
				需要满足	1.元素出现
						2.元素可被点击
		该元素已经出现,也可被点击,但是点击了没有生效。		
		# 使用WebDriverWait定位addmember页面姓名元素提示no such element 是因为加载过慢,selenium对添加成员元素进行点击,添加成员 元素已经出现,并且可以进行点击,于是selenium认为对添加成员元素进行了点击。但是真实情况是虽然点击了 添加成员 元素,但是没有弹出添加成员表单。但是selenium不知道表单没有弹出,此时还是停留在通讯录列表页面,姓名元素是不可能有的,所以会出现姓名元素是不可能有的。
																
		解决方案:改写显式等待,满足添加成员的条件。
			等待addmember页面姓名元素, 姓名元素是在下一个页面出现,判断姓名元素的长度,如果元素长度>=1 则表示新的页面已经出现。判断元素长度请使用find_elements
#改造
#base_page.py文件内容
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.webdriver import WebDriver
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait


class BasePage:
    _driver = None
    _base_url = ""

    def __init__(self, driver: WebDriver = None):
        if driver is None:
            chrome_options = Options()
            # 和浏览器打开的调试端口进行通信
            # 浏览器要使用 --remote-debugging-port=9222 开启调试
            chrome_options.debugger_address = "127.0.0.1:9222"
            self._driver = webdriver.Chrome(options=chrome_options)
            self._driver.implicitly_wait(3)
        else:
            self._driver = driver

        if self._base_url != "":
            self._driver.get(self._base_url)

    def find(self, by, locator):
        return self._driver.find_element(by, locator)

    def finds(self, by, locator):
        return self._driver.find_elements(by, locator)

    def wait_for_click(self, locator):
        # 显示等待, 传一个元组,既包含了定位方式,又包含了定位内容
        WebDriverWait(self._driver, 10).until(expected_conditions.element_to_be_clickable(locator))

    def wait_for_condition(self, condition):
        # 满足 condition 的显示等待
        WebDriverWait(self._driver, 10).until(condition)
#改造
#index.py文件内容
from selenium.webdriver.common.by import By
from test_selenium1.test_wework.add_member import AddMember
from test_selenium1.test_wework.base_page import BasePage

class Index(BasePage):
    _base_url = "https://work.weixin.qq.com/wework_admin/frame#index"

    def goto_add_member(self):
        #案例一演示内容:    #通过首页,点击 常用入口-添加成员 ,跳转通讯录添加成员表单提交页面,此处演示改造内容不用该方式直接进入添加成员表单页面
        # self._driver.find_element_by_css_selector('.index_service_cnt_itemWrap:nth-child(1)').click()           

        '''
        1.采取 首页 点击 通讯录 进入通讯录页面
        2.通讯录页面点击 添加成员 进入 通讯录添加成员表单提交页面

        :return:
        '''
        self.find(By.CSS_SELECTOR,'#menu_contacts').click()

        # 情形1: 直接点击 添加成员 元素,no such element: Unable to locate element: {"method":"css selector","selector":"[id="username"]"}
        # self.find(By.CSS_SELECTOR,".js_has_member>div:nth-child(1)>a:nth-child(2)")

        '''
        # 使用WebDriverWait定位addmember页面姓名元素提示no such element 是因为加载过慢,selenium对添加成员元素进行点击,添加成
																员 元素已经出现,并且可以进行点击,于是selenium认为对添加成员元素进行了点击。但是真实情况是虽然点击了 
																添加成员 元素,但是没有弹出添加成员表单。但是selenium不知道表单没有弹出,此时还是停留在通讯录列表页面,
																姓名元素是不可能有的,所以会出现姓名元素是不可能有的.
        '''
        # 情形2: 使用WebDriverWait 在该情况下也不能使用   no such element: Unable to locate element: {"method":"css selector","selector":"[id="username"]"}
        # 该元素已经出现,也可被点击,但是点击了没有生效。
        # 请注意wait_for_click方法传递的是元组(By.CSS_SELECTOR,".js_has_member>div:nth-child(1)>a:nth-child(2)"),既包含了定位方式,又包含了定位内容
        # self.wait_for_click((By.CSS_SELECTOR,".js_has_member>div:nth-child(1)>a:nth-child(2)"))

        #情形3,也就是解决方案改写显式等待,满足添加成员的条件。

        def add_member_condition(x):  # base_page里面定义的 wait_for_condition 方法 有参数,所以调用需要给个参数
            """
            改写显示等待条件
            等待addmember页面姓名元素, 姓名元素是在下一个页面出现,判断姓名元素的长度,
            如果元素长度>=1 则表示新的页面已经出现。判断元素长度请使用find_elements
            """
            #判断元素长度请使用find_elements,不要弄成find_element啦。请注意
            elements_len = len(self.finds(By.ID, "username"))
            if elements_len <= 0:
                # 如果添加成员表单姓名元素没有出现,一直点击 添加成员 元素
                self.find(By.CSS_SELECTOR, ".js_has_member>div:nth-child(1)>a:nth-child(2)").click()
            # 如果 username 不存在,就会触发 until 中的列循环
            return elements_len > 0

        #请注意调用条件add_member_condition时作为属性传进去,而不是作为方法,不要带括号
        self.wait_for_condition(add_member_condition)

        return AddMember(self._driver)
#改造
#add_member.py文件内容:
import time
from selenium.webdriver.common.by import By
from test_selenium1.test_wework.base_page import BasePage


class AddMember(BasePage):
   def add_member(self):
       """
       添加成员页面,实现成员添加
       :return:
       """
       self.find(By.ID, "username").send_keys("MrDong23456")
       self.find(By.ID, "memberAdd_acctid").send_keys("MrDong23456")
       self.find(By.ID, "memberAdd_phone").send_keys("11111111112")
       self.find(By.CSS_SELECTOR, ".js_btn_save").click()

       '''
       self._driver.find_element_by_id("username").send_keys("Mrsdong")
       self._driver.find_element_by_id("memberAdd_acctid").send_keys("MrDong234567")
       self._driver.find_element_by_id("memberAdd_phone").send_keys("11111111112")
       self._driver.find_element_by_css_selector(".js_btn_save").click()
       '''
#测试文件
#test_add_member.py文件内容
from test_selenium1.test_wework.index import Index


class TestAddMember:
    def setup(self):
        self.index = Index()

    def test_add_member(self):
        self.index.goto_add_member().add_member()
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值