QT4W文档¶
QT4W简介¶
QT4W (Quick Test for Web) 是QTA自动化体系内实现支持Web自动化测试能力的支持库,是QTA的Android、iOS、Windows和Mac自动化测试中实现Web自动化测试能力的基础。
原生和Web控件的关系¶
要理解QT4W和QTA其他的针对原生控件的QT4x(x表示i/A/C/Mac等)的关系,可以先了解一下原生控件和Web控件的关系。
+-------------------------------+
| |
| +-------------------------+ | +-------------------------+
| | | | | +------------+ |
| | | | | | | |
| | | | | | Element | |
| | | | | | | |
| | | | | +------------+ |
| | | | | +------------+ |
| | | | | | | |
| | | | | | Element | |
| | <------------------------> | | |
| | | | | +------------+ |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| +-------------------------+ | +-------------------------+
| Web View Control | Web Page
| |
| |
+-------------------------------+
Native Window
Layers of Native Layers of Web
如上图所示,我们可以理解为,所有的网页(Web Page)都是在原生控件提供的一个Web View的基础上实现的。
统一的Web控件接口¶
参考原生控件和Web控件的关系,QT4W也设计了类似的Web控件使用接口:
from qt4w.webcontrols import WebPage
wv = SomeNativeWebView()
page = WebPage(wx)
page.controls("XXX").click()
无论是哪个操作或终端的UI自动化的Web自动化部分,QTA都是统一使用以上模式的接口的,因此,QT4W提供的第一个功能,就是在QTA的自动化测试工具体系中,提供统一的Web控件识别和操作的接口,关于这块的更多内容,请参考《Web控件识别和操作》。
Write Once Run Anywhere¶
在很多情况下,Web实现的页面,是保持在跨端跨平台上一致的,很多时候也是在一份相同的代码实现的,这个是Web本身的魅力之一。因此,在这种情况下,针对跨平台跨端的Web界面,测试自动化本身也是可以做到一份测试脚本,同时在不同终端和平台上执行,实现Write Once Run Anywhere,这样可以大大的降低测试自动化的维护的成本。为了达到这样的效果,除了统一的Web控件操作接口外,QT4W还提供了统一的Webview实例化框架,也就是Browser框架,更多的详情请参考《跨端跨平台测试》。
可扩展性¶
在各个QT4x的支持下,QT4W目前支持多种操作系统下,不同浏览器内核和混合客户端的Web自动化。目前支持的情况如下:
WebView | 平台或操作系统 | 说明 | 相关实现代码 |
---|---|---|---|
IE | Windows | IE浏览器和内嵌页面使用,支持IE 7~11 | 由QT4C提供 |
Chrome | Windows | Chrome浏览器和内嵌页面使用 | 由QT4C提供 |
TBS | Windows | QQ浏览器和相关内嵌页面使用 | 由QT4C提供 |
CEF | Windows | Chromium内嵌页面使用 | 由QT4C提供 |
Chrome | Linux | Linux下的Headless模式的Chrome浏览器使用 | 由chrome-headless-browser提供 |
AndroidBuildin | Android | Android系统内置浏览器和内嵌页面使用 | 由QT4A提供 |
X5 | Android | QQ移动浏览器和X5内核内嵌页面使用 | 由QT4A提供 |
XWalk | Android | XWalk内核内嵌页面使用 | 由AndroidWXMPLib提供 |
iOSBuildin | iOS | iOS系统内置浏览器和内嵌页面使用 | 由QT4i提供 |
微信小程序&微信H5 | Android微信 | 微信小程序或者微信内部的H5页面使用 | 由AndroidWXMPLib提供 |
Chrome | MacOS | Mac OS下的Chrome浏览器和内嵌页面使用 | 由QT4Mac提供 |
QT4W也支持用户自定义扩展来支持更多的兼容性。更多信息请查阅《扩展QT4W》章节。
使用场景及安装¶
QT4W可用于各个端上的Web应用或者Native应用内嵌Web页面的自动化,其不能独立使用,需要结合其他Native层的自动化框架一起使用。各端的使用场景及安装如下所示:
Web控件的标识与使用¶
Web自动化的实质就是定位控件和操作控件,控件的定位问题就是如何去标识一个控件,并且根据该标识可以找到该控件。找到该控件后就可以使用QT4W提供的接口进行操作了。本部分详细描述在QT4W中是如何来描述一个控件,并且操纵该控件,进而描述一个Web页面以及封装该页面的行为。
跨端跨平台测试¶
QT4W的Browser抽象¶
QT4W将浏览器抽象为一个IBrowser对象,在该对象中定义了两个操作:
- 打开一个URL指定的页面open_url()
- 在打开的页面中查找和指定url匹配的页面。
在实现自己的IBrowser对象后,就可以通过该对象来操作浏览器。关于如何封装IBrowser接口参见Browser实现。 为了进一步消除,不同浏览器调用时的差异,QT4W提供了Browser类,该类继承自IBrowser。该类能够根据注册的浏览器名,获取到对应的实现实例。在QT4W中一般都通过,该类来调用具体的IBrowser实现。该类的实现,如下所示:
class Browser(IBrowser):
'''对外的浏览器类
'''
browser_dict = {} # 存储浏览器类型与浏览器类的对应关系
def __init__(self, browser_name=None):
'''创建具体的Browser实例
:param browser_name: 要创建的浏览器类型
:type browser_name:
'''
self._browser_name = browser_name
self._browser = self._get_browser_cls()
def _get_browser_cls(self):
'''获取浏览器类
'''
if not self._browser_name:
# 随机选择浏览器
self._browser_name = random.choice(self.browser_dict.keys())
browser_cls_path = self.browser_dict.get(self._browser_name)
if not browser_cls_path:
raise TypeError('Browser %s is not registered' % self._browser_name)
logger.info('[Browser] Current browser type is %s' % self._browser_name)
module = __import__('.'.join(browser_cls_path.split('.')[:-1]))
for item in browser_cls_path.split('.')[1:]:
module = getattr(module, item)
return module()
@staticmethod
def register_browser(browser_name, browser_cls_path):
'''注册浏览器
:param browser_name: 浏览器名称
:type browser_name: string
:param browser_cls_path: 浏览器类路径
:type browser_cls_path: string
'''
Browser.browser_dict[browser_name] = browser_cls_path
def open_url(self, url, page_cls=None):
'''打开一个url,返回page_cls类的实例
:param url: 要打开页面的url
:type url: string
:param page_cls: 要返回的具体WebPage类,为None表示返回WebPage实例
:type page_cls: Class
'''
return self._browser.open_url(url, page_cls)
def find_by_url(self, url, page_cls=None, timeout=10):
'''在当前打开的页面中查找指定url,返回WebPage实例,如果未找到,返回None
:param url: 要查找的页面url
:type url: string
:param page_cls: 要返回的具体WebPage类,为None表示返回WebPage实例
:type page_cls: Class
:param timeout: 查找超时时间,单位:秒
:type timeout: int/float
'''
page = self._browser.find_by_url(url, page_cls, timeout)
if page == None: raise RuntimeError('Can\'t find page %s in browser %s' % (url, self._browser_name))
return page
register_browser方法实现了浏览器名和浏览器封装实现的对应关系,并将该对应关系以字典的形式存储在类变量browser_dict中;_get_browser_cls方法会根据browser_dict变量中的信息来返回浏览器名的对应浏览器实现的Object对象。如果初始化的时候未传入浏览器名,会随机选择一个已注册的浏览器实现。如果未注册任何浏览器则会报错。这里的隐式的要求,在实现自己的IBrowser封装时,初始化时没有必传的参数。在QT4W自动化中,推荐使用该类的实例来打开或者查找页面。
Browser用法示例¶
在获取Browser类的实例前,先需要使用Browser.register_browser注册浏览器,在注册时,传入了两个参数:浏览器名和XXBrowser类的路径。然后再初始化Browser对象时传入注册的浏览器名,获取指定的浏览器对象,使用方法如下所示:
#注册浏览器并获取浏览器实例
Browser.register_browser('TestBrowser', 'BrowserPath')
browser=Browser('TestBrowser')
#打开URL
browser.open_url(https://qtacore.github.io/qt4w/demo.html,PageClass)
说明: 此处注册时传入的第二个参数指示浏览器实现所在的class,此处传入的TestBrowser给出的是TestBrowser浏览器封装的实现。
实现WORA(Write Once Run Anywhere)示例¶
下面以DemoPage为示例来说说明,如何使用使用Browser类实现一份QT4W测试用例,仅需做少许修改便可以在多个端上运行。
Android端执行DemoPage测试¶
在Android端,要想使用QT4W需要QT4A的支持。在Android端我们使用QT4A中封装的系统自带的浏览器来检查Demo页面是否运行正常。因此,我们实现了如下所示的测试用例:
class DemoTest(AndroidTestBase):
'''QT4W示例测试用例
'''
owner = "testowner"
timeout = 5
priority = AndroidTestBase.EnumPriority.High
status = AndroidTestBase.EnumStatus.Ready
def pre_test(self):
Browser.register_browser('TestBrowser', 'qt4a.browser.QT4ABrowser')
def run_test(self):
self.startStep('1.设置信息并提交')
browser = Browser("TestBrowser")
page = browser.open_url('https://qtacore.github.io/qt4w/demo.html', DemoPage)
page.set_name("qta")
page.set_female()
page.set_age(str(20))
page.set_company("tencent")
page.submit()
这里在pre_test中,将TestBrowser注册为QT4A中的QT4ABrowser。在具体的测试用例中,使用Browser(“TestBrowser”)获取到对应的浏览器实例类似执行DemoPage测试。
IOS端执行DemoPage测试¶
在IOS端,可以直接复制上述代码,然后修改一下浏览器注册部分的内容,将测试的基类改成使用IOS的测试基类即可运行。修改后如下所示:
class DemoTest(iTestCase):
'''IOS QT4W示例测试用例
'''
owner = "testowner"
timeout = 5
priority = iTestCase.EnumPriority.High
status = iTestCase.EnumStatus.Ready
def pre_test(self):
Browser.register_browser('TestBrowser', 'qt4i.app.Safari')
def run_test(self):
self.startStep('1.设置信息并提交')
browser = Browser("TestBrowser")
page = browser.open_url('https://qtacore.github.io/qt4w/demo.html', DemoPage)
print page.url
page.set_name("qta")
page.set_age(str(20))
page.set_company("tencent")
page.set_female()
page.submit()
这里仅仅是将TestBrowser重新注册为了qt4i.app.Safari,同时为了用例能够在IOS平台上顺利执行,需要将DemoTest的测试基类改为继承QT4I提供的iTestCase。即可在IOS上顺利执行。
高级用法¶
更加方便的用法是在各端分别封装一个测试基类,并在测试基类中注册对应的浏览器。例如Android端可以封装如下测试基类,继承自Android自动化的测试基类AndroidTestBase:
class BrowserTestCase(AndroidTestBase):
'''Browser测试用例基类
'''
def pre_test(self):
super(BrowserTestCases, self).pre_test()
# 注册Android浏览器类路径
Browser.register_browser('TestBrowser', 'qt4a.browser.QT4ABrowser')
IOS端同样也封装一个BrowserTestCase,继承IOS自动化的测试基类iTestCase,如下:
class BrowserTestCase(iTestCase):
'''Browser测试用例基类
'''
def pre_test(self):
super(BrowserTestCases, self).pre_test()
# 注册IOS浏览器类路径
Browser.register_browser('TestBrowser', 'qt4i.app.Safari')
经过如此封装后,Android和IOS两端的DemoPage自动化便可统一使用下面的测试用例:
class DemoTest(BrowserTestCase):
'''QT4W示例测试用例
'''
owner = "testowner"
timeout = 5
priority = BrowserTestCase.EnumPriority.High
status = BrowserTestCase.EnumStatus.Ready
def run_test(self):
self.startStep('1.设置信息并提交')
browser = Browser("TestBrowser")
page = browser.open_url('https://qtacore.github.io/qt4w/demo.html', DemoPage)
page.set_name("qta")
page.set_female()
page.set_age(str(20))
page.set_company("tencent")
page.submit()
在上面的示例中,在不同端上,需要手动的去修改所引入的BrowserTestCase,Android端引入Android端封装的BrowserTestCase,IOS端运行时,需要修改为IOS端的BrowserTestCase封装。如果不想所动修改的话,可以在测试用例前使用__import__方法动态引入,package.moudle指代BrowserTestCase的路径,如果两端的实现,在同一路径下,便不需做任何更改。
BrowserTestCase=__import__(package.moudle)
跨终端的具体用例写法可以参考QT4WDemo项目
内嵌页面如何实现WORA¶
内嵌页面,实际上也是一个隐形的浏览器,内嵌页面实现WORA,同样必须为内嵌页面封装IBrowser类,然后使用Browser.register_browser(),方法注册该内嵌页面的浏览器封装即可。使用方式和上面相同。如何实现IBrowser封装,Android端可以参考QT4ABrowser的实现,IOS端可以参考Safari的实现。
扩展QT4W¶
QT4W作为一个中间层的框架,做了分层设计,提供了良好的扩展性。虽然QT4X提供的现成的封装已经能够满足大部分场景下的使用。但是某些特殊场景下,可能需要自己去扩展QT4W。 目前QT4W支持的WebView有:
WebView | 平台或操作系统 | 说明 | 相关实现代码 |
---|---|---|---|
IE | Windows | IE浏览器和内嵌页面使用,支持IE 7~11 | 由QT4C提供 |
Chrome | Windows | Chrome浏览器和内嵌页面使用 | 由QT4C提供 |
TBS | Windows | QQ浏览器和相关内嵌页面使用 | 由QT4C提供 |
CEF | Windows | Chromium内嵌页面使用 | 由QT4C提供 |
Chrome | Linux | Linux下的Headless模式的Chrome浏览器使用 | 由chrome-headless-browser提供 |
AndroidBuildin | Android | Android系统内置浏览器和内嵌页面使用 | 由QT4A提供 |
X5 | Android | QQ移动浏览器和X5内核内嵌页面使用 | 由QT4A提供 |
XWalk | Android | XWalk内核内嵌页面使用 | 由AndroidWXMPLib提供 |
iOSBuildin | iOS | iOS系统内置浏览器和内嵌页面使用 | 由QT4i提供 |
微信小程序 | Android微信 | 微信小程序使用 | 由AndroidWXMPLib提供 |
Chrome | MacOS | Mac OS下的Chrome浏览器和内嵌页面使用 | 由QT4Mac提供 |
扩展QT4W可能需要实现WebView、WebDriver或者Browser接口。例如如果需要在MAC系统上进行Chrome浏览器的自动化测试,这里就需要实现Mac上能正常运行的WebView、Browser以及WebDriver接口。下面就详细说说如何去实现这几个接口
接口文档¶
接口文档¶
qt4w package¶
Subpackages¶
qt4w.browser package¶
IBrowser接口定义
-
class
qt4w.browser.browser.
Browser
(browser_name=None, clear_data=True)¶ 基类:
qt4w.browser.browser.IBrowser
对外的浏览器类
-
browser_dict
= {}¶
-
clear_data
()¶ 清除浏览器数据
-
close
()¶ 关闭浏览器,并清理数据 :return:
-
find_by_url
(url, page_cls=None, timeout=10)¶ 在当前打开的页面中查找指定url,返回WebPage实例,如果未找到,返回None
参数: - url (string) – 要查找的页面url
- page_cls (Class) – 要返回的具体WebPage类,为None表示返回WebPage实例
- timeout (int/float) – 查找超时时间,单位:秒
-
open_url
(url, page_cls=None, invisible_mode=False)¶ 打开一个url,返回page_cls类的实例
参数: - url (string) – 要打开页面的url
- page_cls (Class) – 要返回的具体WebPage类,为None表示返回WebPage实例
- invisible_mode (Bool) – 是否开启隐身模式
-
static
register_browser
(browser_name, browser_cls_path)¶ 注册浏览器
参数: - browser_name (string) – 浏览器名称
- browser_cls_path (string) – 浏览器类路径
-
-
class
qt4w.browser.browser.
IBrowser
¶ 基类:
object
浏览器接口类
-
clear_data
()¶ 清除浏览器数据
-
close
()¶ 关闭浏览器,并清理数据 :return:
-
find_by_url
(url, page_cls=None)¶ 在当前打开的页面中查找指定url,返回page_cls类的实例,如果未找到,返回None
参数: - url (string) – 要查找的页面url
- page_cls (Class) – 要返回的具体WebPage类,为None表示返回WebPage实例
-
open_url
(url, page_cls=None, invisible_mode=False)¶ 打开一个url,返回page_cls类的实例
参数: - url (string) – 要打开页面的url
- page_cls (Class) – 要返回的具体WebPage类,为None表示返回WebPage实例
- invisible_mode (Bool) – 是否开启隐身模式
-
IBrowser接口定义及Windows实现
qt4w.webdriver package¶
IE的WebDriver实现
-
class
qt4w.webdriver.iewebdriver.
IEWebDriver
(webview)¶ 基类:
qt4w.webdriver.webdriver.WebDriverBase
IE的WebDriver实现
-
driver_script
= '/*! JSON v3.3.2 | http://bestiejs.github.io/json3 | Copyright 2012-2014, Kit Cambridge | http://kit.mit-license.org */\n(function(){function N(p,r){function q(a){if(q[a]!==w)return q[a];var c;if("bug-string-char-index"==a)c="a"!="a"[0];else if("json"==a)c=q("json-stringify")&&q("json-parse");else{var e;if("json-stringify"==a){c=r.stringify;var b="function"==typeof c&&s;if(b){(e=function(){return 1}).toJSON=e;try{b="0"===c(0)&&"0"===c(new t)&&\'""\'==c(new A)&&c(u)===w&&c(w)===w&&c()===w&&"1"===c(e)&&"[1]"==c([e])&&"[null]"==c([w])&&"null"==c(null)&&"[null,null,null]"==c([w,u,null])&&\'{"a":[1,true,false,null,"\\\\u0000\\\\b\\\\n\\\\f\\\\r\\\\t"]}\'==\nc({a:[e,!0,!1,null,"\\x00\\b\\n\\f\\r\\t"]})&&"1"===c(null,e)&&"[\\n 1,\\n 2\\n]"==c([1,2],null,1)&&\'"-271821-04-20T00:00:00.000Z"\'==c(new C(-864E13))&&\'"+275760-09-13T00:00:00.000Z"\'==c(new C(864E13))&&\'"-000001-01-01T00:00:00.000Z"\'==c(new C(-621987552E5))&&\'"1969-12-31T23:59:59.999Z"\'==c(new C(-1))}catch(f){b=!1}}c=b}if("json-parse"==a){c=r.parse;if("function"==typeof c)try{if(0===c("0")&&!c(!1)){e=c(\'{"a":[1,true,false,null,"\\\\u0000\\\\b\\\\n\\\\f\\\\r\\\\t"]}\');var n=5==e.a.length&&1===e.a[0];if(n){try{n=!c(\'"\\t"\')}catch(d){}if(n)try{n=\n1!==c("01")}catch(g){}if(n)try{n=1!==c("1.")}catch(m){}}}}catch(X){n=!1}c=n}}return q[a]=!!c}p||(p=k.Object());r||(r=k.Object());var t=p.Number||k.Number,A=p.String||k.String,H=p.Object||k.Object,C=p.Date||k.Date,G=p.SyntaxError||k.SyntaxError,K=p.TypeError||k.TypeError,L=p.Math||k.Math,I=p.JSON||k.JSON;"object"==typeof I&&I&&(r.stringify=I.stringify,r.parse=I.parse);var H=H.prototype,u=H.toString,v,B,w,s=new C(-0xc782b5b800cec);try{s=-109252==s.getUTCFullYear()&&0===s.getUTCMonth()&&1===s.getUTCDate()&&\n10==s.getUTCHours()&&37==s.getUTCMinutes()&&6==s.getUTCSeconds()&&708==s.getUTCMilliseconds()}catch(Q){}if(!q("json")){var D=q("bug-string-char-index");if(!s)var x=L.floor,M=[0,31,59,90,120,151,181,212,243,273,304,334],E=function(a,c){return M[c]+365*(a-1970)+x((a-1969+(c=+(1<c)))/4)-x((a-1901+c)/100)+x((a-1601+c)/400)};(v=H.hasOwnProperty)||(v=function(a){var c={},e;(c.__proto__=null,c.__proto__={toString:1},c).toString!=u?v=function(a){var c=this.__proto__;a=a in(this.__proto__=null,this);this.__proto__=\nc;return a}:(e=c.constructor,v=function(a){var c=(this.constructor||e).prototype;return a in this&&!(a in c&&this[a]===c[a])});c=null;return v.call(this,a)});B=function(a,c){var e=0,b,f,n;(b=function(){this.valueOf=0}).prototype.valueOf=0;f=new b;for(n in f)v.call(f,n)&&e++;b=f=null;e?B=2==e?function(a,c){var e={},b="[object Function]"==u.call(a),f;for(f in a)b&&"prototype"==f||v.call(e,f)||!(e[f]=1)||!v.call(a,f)||c(f)}:function(a,c){var e="[object Function]"==u.call(a),b,f;for(b in a)e&&"prototype"==\nb||!v.call(a,b)||(f="constructor"===b)||c(b);(f||v.call(a,b="constructor"))&&c(b)}:(f="valueOf toString toLocaleString propertyIsEnumerable isPrototypeOf hasOwnProperty constructor".split(" "),B=function(a,c){var e="[object Function]"==u.call(a),b,h=!e&&"function"!=typeof a.constructor&&F[typeof a.hasOwnProperty]&&a.hasOwnProperty||v;for(b in a)e&&"prototype"==b||!h.call(a,b)||c(b);for(e=f.length;b=f[--e];h.call(a,b)&&c(b));});return B(a,c)};if(!q("json-stringify")){var U={92:"\\\\\\\\",34:\'\\\\"\',8:"\\\\b",\n12:"\\\\f",10:"\\\\n",13:"\\\\r",9:"\\\\t"},y=function(a,c){return("000000"+(c||0)).slice(-a)},R=function(a){for(var c=\'"\',b=0,h=a.length,f=!D||10<h,n=f&&(D?a.split(""):a);b<h;b++){var d=a.charCodeAt(b);switch(d){case 8:case 9:case 10:case 12:case 13:case 34:case 92:c+=U[d];break;default:if(32>d){c+="\\\\u00"+y(2,d.toString(16));break}c+=f?n[b]:a.charAt(b)}}return c+\'"\'},O=function(a,c,b,h,f,n,d){var g,m,k,l,p,r,s,t,q;try{g=c[a]}catch(z){}if("object"==typeof g&&g)if(m=u.call(g),"[object Date]"!=m||v.call(g,\n"toJSON"))"function"==typeof g.toJSON&&("[object Number]"!=m&&"[object String]"!=m&&"[object Array]"!=m||v.call(g,"toJSON"))&&(g=g.toJSON(a));else if(g>-1/0&&g<1/0){if(E){l=x(g/864E5);for(m=x(l/365.2425)+1970-1;E(m+1,0)<=l;m++);for(k=x((l-E(m,0))/30.42);E(m,k+1)<=l;k++);l=1+l-E(m,k);p=(g%864E5+864E5)%864E5;r=x(p/36E5)%24;s=x(p/6E4)%60;t=x(p/1E3)%60;p%=1E3}else m=g.getUTCFullYear(),k=g.getUTCMonth(),l=g.getUTCDate(),r=g.getUTCHours(),s=g.getUTCMinutes(),t=g.getUTCSeconds(),p=g.getUTCMilliseconds();\ng=(0>=m||1E4<=m?(0>m?"-":"+")+y(6,0>m?-m:m):y(4,m))+"-"+y(2,k+1)+"-"+y(2,l)+"T"+y(2,r)+":"+y(2,s)+":"+y(2,t)+"."+y(3,p)+"Z"}else g=null;b&&(g=b.call(c,a,g));if(null===g)return"null";m=u.call(g);if("[object Boolean]"==m)return""+g;if("[object Number]"==m)return g>-1/0&&g<1/0?""+g:"null";if("[object String]"==m)return R(""+g);if("object"==typeof g){for(a=d.length;a--;)if(d[a]===g)throw K();d.push(g);q=[];c=n;n+=f;if("[object Array]"==m){k=0;for(a=g.length;k<a;k++)m=O(k,g,b,h,f,n,d),q.push(m===w?"null":\nm);a=q.length?f?"[\\n"+n+q.join(",\\n"+n)+"\\n"+c+"]":"["+q.join(",")+"]":"[]"}else B(h||g,function(a){var c=O(a,g,b,h,f,n,d);c!==w&&q.push(R(a)+":"+(f?" ":"")+c)}),a=q.length?f?"{\\n"+n+q.join(",\\n"+n)+"\\n"+c+"}":"{"+q.join(",")+"}":"{}";d.pop();return a}};r.stringify=function(a,c,b){var h,f,n,d;if(F[typeof c]&&c)if("[object Function]"==(d=u.call(c)))f=c;else if("[object Array]"==d){n={};for(var g=0,k=c.length,l;g<k;l=c[g++],(d=u.call(l),"[object String]"==d||"[object Number]"==d)&&(n[l]=1));}if(b)if("[object Number]"==\n(d=u.call(b))){if(0<(b-=b%1))for(h="",10<b&&(b=10);h.length<b;h+=" ");}else"[object String]"==d&&(h=10>=b.length?b:b.slice(0,10));return O("",(l={},l[""]=a,l),f,n,h,"",[])}}if(!q("json-parse")){var V=A.fromCharCode,W={92:"\\\\",34:\'"\',47:"/",98:"\\b",116:"\\t",110:"\\n",102:"\\f",114:"\\r"},b,J,l=function(){b=J=null;throw G();},z=function(){for(var a=J,c=a.length,e,h,f,k,d;b<c;)switch(d=a.charCodeAt(b),d){case 9:case 10:case 13:case 32:b++;break;case 123:case 125:case 91:case 93:case 58:case 44:return e=\nD?a.charAt(b):a[b],b++,e;case 34:e="@";for(b++;b<c;)if(d=a.charCodeAt(b),32>d)l();else if(92==d)switch(d=a.charCodeAt(++b),d){case 92:case 34:case 47:case 98:case 116:case 110:case 102:case 114:e+=W[d];b++;break;case 117:h=++b;for(f=b+4;b<f;b++)d=a.charCodeAt(b),48<=d&&57>=d||97<=d&&102>=d||65<=d&&70>=d||l();e+=V("0x"+a.slice(h,b));break;default:l()}else{if(34==d)break;d=a.charCodeAt(b);for(h=b;32<=d&&92!=d&&34!=d;)d=a.charCodeAt(++b);e+=a.slice(h,b)}if(34==a.charCodeAt(b))return b++,e;l();default:h=\nb;45==d&&(k=!0,d=a.charCodeAt(++b));if(48<=d&&57>=d){for(48==d&&(d=a.charCodeAt(b+1),48<=d&&57>=d)&&l();b<c&&(d=a.charCodeAt(b),48<=d&&57>=d);b++);if(46==a.charCodeAt(b)){for(f=++b;f<c&&(d=a.charCodeAt(f),48<=d&&57>=d);f++);f==b&&l();b=f}d=a.charCodeAt(b);if(101==d||69==d){d=a.charCodeAt(++b);43!=d&&45!=d||b++;for(f=b;f<c&&(d=a.charCodeAt(f),48<=d&&57>=d);f++);f==b&&l();b=f}return+a.slice(h,b)}k&&l();if("true"==a.slice(b,b+4))return b+=4,!0;if("false"==a.slice(b,b+5))return b+=5,!1;if("null"==a.slice(b,\nb+4))return b+=4,null;l()}return"$"},P=function(a){var c,b;"$"==a&&l();if("string"==typeof a){if("@"==(D?a.charAt(0):a[0]))return a.slice(1);if("["==a){for(c=[];;b||(b=!0)){a=z();if("]"==a)break;b&&(","==a?(a=z(),"]"==a&&l()):l());","==a&&l();c.push(P(a))}return c}if("{"==a){for(c={};;b||(b=!0)){a=z();if("}"==a)break;b&&(","==a?(a=z(),"}"==a&&l()):l());","!=a&&"string"==typeof a&&"@"==(D?a.charAt(0):a[0])&&":"==z()||l();c[a.slice(1)]=P(z())}return c}l()}return a},T=function(a,b,e){e=S(a,b,e);e===\nw?delete a[b]:a[b]=e},S=function(a,b,e){var h=a[b],f;if("object"==typeof h&&h)if("[object Array]"==u.call(h))for(f=h.length;f--;)T(h,f,e);else B(h,function(a){T(h,a,e)});return e.call(a,b,h)};r.parse=function(a,c){var e,h;b=0;J=""+a;e=P(z());"$"!=z()&&l();b=J=null;return c&&"[object Function]"==u.call(c)?S((h={},h[""]=e,h),"",c):e}}}r.runInContext=N;return r}var K=typeof define==="function"&&define.amd,F={"function":!0,object:!0},G=F[typeof exports]&&exports&&!exports.nodeType&&exports,k=F[typeof window]&&\nwindow||this,t=G&&F[typeof module]&&module&&!module.nodeType&&"object"==typeof global&&global;!t||t.global!==t&&t.window!==t&&t.self!==t||(k=t);if(G&&!K)N(k,G);else{var L=k.JSON,Q=k.JSON3,M=!1,A=N(k,k.JSON3={noConflict:function(){M||(M=!0,k.JSON=L,k.JSON3=Q,L=Q=null);return A}});k.JSON={parse:A.parse,stringify:A.stringify}}K&&define(function(){return A})}).call(this);\n\n if ( !window.Element || !Element.prototype || !(document.documentElement instanceof Element)) {\n var __Element = window.Element;\n var __prototype = window.Element ? Element.prototype : null;\n window.Element = function(){};\n if (!__prototype) {\n // copy origin attrs\n for(var key in __Element)\n Element.prototype[key] = __Element[key];\n }\n var _hookElement = function (element) {\n if (!element) return;\n if (!element.hooked) {\n console.log(\'hook \' + element);\n for(var key in Element.prototype) {\n try{\n element[key] = Element.prototype[key];\n }catch(e){\n console.warn(element.outerHTML + \':\' + key + \':\' + e.message);\n }\n }\n element.hooked = true;\n }\n }\n\n var hookAllElements = function () {\n var elements = [document.documentElement];\n while (elements.length > 0) {\n var element = elements.splice(0, 1)[0];\n if (!element) continue;\n _hookElement(element);\n if (element.children) {\n for (var i = 0; i < element.children.length; i++) {\n elements.push(element.children[i]);\n }\n }\n }\n }\n\n var hookCreateElement = function () {\n if (document.__createElement) return;\n document.__createElement = document.createElement;\n document.createElement = function (tagName) {\n console.log(\'create element \' + tagName);\n var element = document.__createElement(tagName);\n for(var key in Element.prototype)\n element[key] = Element.prototype[key];\n element.hooked = true;\n return element;\n }\n }\n\n setTimeout(function () {\n try {\n hookCreateElement();\n hookAllElements();\n } catch (e) {\n alert(\'Hook element error: \' + e.message);\n }\n }, 100);\n \n }\n\n Element.prototype.scrollIntoViewIfNeeded = function (centerIfNeeded) {\n centerIfNeeded = arguments.length === 0 ? true : !!centerIfNeeded;\n var rect = this.getBoundingClientRect();\n var clientWidth = document.documentElement.clientWidth - 20;\n var clientHeight = document.documentElement.clientHeight - 20;//\xe9\x81\xbf\xe5\x85\x8d\xe5\xad\x98\xe5\x9c\xa8\xe6\xbb\x9a\xe5\x8a\xa8\xe6\x9d\xa1\xe5\xaf\xbc\xe8\x87\xb4\xe9\x81\xae\xe6\x8c\xa1\xe6\x8e\xa7\xe4\xbb\xb6\n if(rect.left >= clientWidth || rect.right <= 0 || rect.top >= clientHeight || rect.bottom <= 0){\n this.scrollIntoView();\n }\n }\n if (!Element.prototype.dispatchEvent) {\n Element.prototype.dispatchEvent = function (event) {\n if (event.type == \'input\') {\n event.type = \'propertychange\';\n event.propertyName = \'value\';\n }\n event.srcElement = event.target = this;\n this.fireEvent(\'on\' + event.type, event);\n }\n }\n\n Array.prototype.forEach = Array.prototype.forEach || function (callback) {\n if (typeof callback != \'function\') {\n throw TypeError(typeof callback);\n }\n for(var i = 0; i < this.length; i ++) {\n callback.call(arguments[1], this[i], i, this);\n }\n };\n\n if (Function.prototype.bind && window.console && typeof console.log == "object"){\n // \xe8\xa7\xa3\xe5\x86\xb3console.log\xe6\xb2\xa1\xe6\x9c\x89apply/bind\xe6\x96\xb9\xe6\xb3\x95\xe9\x97\xae\xe9\xa2\x98\n ["log","info","warn","error","assert","dir","clear","profile","profileEnd"].forEach(function (method) {\n console[method] = this.bind(console[method], console);\n }, Function.prototype.call);\n }\n // Avoid `console` errors in browsers that lack a console.\n (function() {\n var method;\n var noop = function () {};\n var methods = [\n \'assert\', \'clear\', \'count\', \'debug\', \'dir\', \'dirxml\', \'error\',\n \'exception\', \'group\', \'groupCollapsed\', \'groupEnd\', \'info\', \'log\',\n \'markTimeline\', \'profile\', \'profileEnd\', \'table\', \'time\', \'timeEnd\',\n \'timeStamp\', \'trace\', \'warn\'\n ];\n var length = methods.length;\n var console = (window.console = window.console || {});\n\n while (length--) {\n method = methods[length];\n\n // Only stub undefined methods.\n if (!console[method]) {\n console[method] = noop;\n }\n }\n }());\n\n if (!document.createEvent) {\n document.createEvent = function (event) {\n var evt = document.createEventObject(event);\n evt.initEvent = function (type) {\n evt.type = type;\n }\n return evt;\n }\n }\n \n if (typeof window.qt4w_hook_console == \'undefined\') window.qt4w_hook_console = true;\n window[\'qt4w_driver_lib\'] = {\n getScale : function(){\n return 1;\n },\n \n getScreenSize: function(){\n var result = new Array();\n result.push(screen.availWidth);\n result.push(screen.availHeight);\n return result.toString();\n },\n \n selectNodes : function(xpath){\n var oResult = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null);\n var aNodes = new Array(); \n if (oResult != null) { \n var oElement = oResult.iterateNext(); \n while (oElement) { \n aNodes.push(oElement); \n oElement = oResult.iterateNext(); \n } \n }\n return aNodes;\n },\n \n selectNode : function(xpath){\n var nodes = this.selectNodes(xpath);\n if(nodes.length == 0) throw new Error(\'Find element \'+xpath+\' failed\');\n else if(nodes.length > 1) throw new Error(\'Find \'+nodes.length+\' elements match \'+xpath);\n return nodes[0];\n },\n \n getElementZoom: function(node){\n var scale = 1;\n while(node != null && node != document.documentElement){\n var zoom = parseFloat(window.getComputedStyle(node, null).zoom);\n if(zoom != 1){\n scale *= zoom;\n }\n node = node.parentNode;\n }\n return scale;\n },\n \n getElemRect: function(node){\n var result = new Array();\n var rect = node.getBoundingClientRect();\n var scale = this.getScale();\n scale *= this.getElementZoom(node);\n var left = rect.left;\n var top = rect.top;\n var width = rect.width;\n var height = rect.height;\n if (typeof width == \'undefined\') width = rect.right - rect.left;\n if (typeof height == \'undefined\') height = rect.bottom - rect.top;\n result.push(left * scale);\n result.push(top * scale);\n result.push(width * scale);\n result.push(height * scale);\n return result.toString();\n },\n \n initHighlightDiv: function(){\n this.bd0 = document.createElement("div");\n this.bd1 = document.createElement("div");\n this.bd2 = document.createElement("div");\n this.bd3 = document.createElement("div");\n },\n \n showDiv: function(cnt){\n if(cnt % 2 != 0){\n document.body.appendChild(this.bd0);\n document.body.appendChild(this.bd1);\n document.body.appendChild(this.bd2);\n document.body.appendChild(this.bd3);\n } else {\n document.body.removeChild(this.bd0);\n document.body.removeChild(this.bd1);\n document.body.removeChild(this.bd2);\n document.body.removeChild(this.bd3);\n }\n if (cnt){\n cnt--;\n if(window.console) console.log(\'show\' + cnt);\n setTimeout("qt4w_driver_lib.showDiv(" + cnt + ")", 200);\n }\n },\n \n highlight: function(node){\n if (!this.bd0) {\n this.initHighlightDiv();\n }\n var rect = node.getBoundingClientRect();\n var left= rect.left;\n var top = rect.top;\n var width = node.offsetWidth;\n var height = node.offsetHeight;\n if(window.console) console.log(left+\',\'+top+\',\'+width+\',\'+height);\n \n this.bd0.setAttribute("style", "left:" + (left - 1) + "px;top:" + (top - 1) + "px;z-index:32767;"\n + "width:" + (width + 2) + "px;height:2px;position:fixed;background-color: red");\n this.bd1.setAttribute("style", "left:" + (left - 1) + "px;top:" + (top - 1) + "px;z-index:32767;"\n + "width:2px;height:" + (height) + "px;position:fixed;;background-color: red");\n this.bd2.setAttribute("style", "left:" + (left + width - 1) + "px;top:" + (top) + "px;z-index:32767;"\n + "width:2px;height:" + (height) + "px;position:fixed;;background-color: red");\n this.bd3.setAttribute("style", "left:" + (left - 1) + "px;top:" + (top + height - 1) + "px;z-index:32767;"\n + "width:" + (width + 2) + "px;height:2px;position:fixed;;background-color: red");\n //console.log(\'style\'+this.bd0.getAttribute(\'style\')+\'\');\n this.showDiv(3);\n },\n \n scrollToVisible: function(node){\n if(node.scrollIntoViewIfNeeded){\n node.scrollIntoViewIfNeeded();\n }else if(node.scrollIntoView){\n node.scrollIntoView();\n }\n },\n \n logData: [],\n hookConsole: function(){\n var self = this;\n if(window.console && console.log && console.log.apply && window.JSON){\n var hookedConsoleFunc = function(){\n var timeStr = new Date().toLocaleString();\n for(var i=1;i<arguments.length;i++){\n var data = arguments[i];\n if(data instanceof Array){\n data = JSON.stringify(data);\n }else if(data instanceof Object){\n var jsonData = {};\n for(var key in data){\n jsonData[key] = data[key];\n if(jsonData[key]) jsonData[key] = jsonData[key].toString();\n }\n data = JSON.stringify(jsonData);\n }\n self.logData.push(\'[\' + timeStr + \'][console.\' + arguments[0].name + \'] \' + data);\n }\n var args = Array.prototype.slice.call(arguments);\n args.splice(0, 1);\n return arguments[0].apply(this, args);\n }\n\n var hookFunction = function (funcName) {\n if (!console[funcName]) return;\n var origFunc = console[funcName];\n console[funcName] = function(){\n var args = Array.prototype.slice.call(arguments);\n args.splice(0, 0, origFunc);\n return hookedConsoleFunc.apply(this, args);\n }\n }\n\n hookFunction(\'log\');\n hookFunction(\'dir\');\n hookFunction(\'info\');\n hookFunction(\'warn\');\n hookFunction(\'error\');\n }\n },\n \n readLogData: function (count) {\n if (count < 0) count = this.logData.length;\n return this.logData.splice(0, count);\n }\n \n };\n\n if (window.qt4w_hook_console) qt4w_driver_lib.hookConsole();\n \n window[\'qt4w_driver_lib\'][\'getScale\'] = function(){return screen.deviceXDPI / screen.logicalXDPI;};\n window[\'qt4w_driver_lib\'][\'getElementZoom\'] = function(node){\n return 1;\n };\n '¶
-
eval_script
(frame_xpaths, script)¶ 在指定frame中执行JavaScript,并返回执行结果
参数: - frame_xpaths (list) – frame元素的XPATH路径,如果是顶层页面,怎传入“[]”
- script (string) – 要执行的JavaScript语句
-
get_style
(elem_xpaths, style_name)¶ 获取元素的某一样式值
参数: - elem_xpaths (list) – 元素的XPATH路径
- style_name (string) – 样式名称
-
IWebDriver接口
-
class
qt4w.webdriver.webdriver.
IWebDriver
(webview)¶ 基类:
object
IWebDriver接口
-
eval_script
(frame_xpaths, script)¶ 在指定frame中执行JavaScript,并返回执行结果
参数: - frame_xpaths (list) – frame元素的XPATH路径,如果是顶层页面,怎传入“[]”
- script (string) – 要执行的JavaScript语句
-
get_attribute
(elem_xpaths, attr_name)¶ 获取元素属性
参数: - elem_xpaths (list) – 元素的XPATH路径
- attr_name (string) – 属性名
-
get_elem_rect
(elem_xpaths, rav=True)¶ 获取元素在页面中的坐标
参数: - elem_xpaths (list) – 元素的XPATH路径
- rav (bool) – 是否是相对于当前frame
-
get_property
(elem_xpaths, prop_name)¶ 获取元素的特定值,例如:node.innerHTML
参数: - elem_xpaths (list) – 元素的XPATH路径
- prop_name (string) – property名
-
get_style
(elem_xpaths, style_name)¶ 获取元素的某一样式值
参数: - elem_xpaths (list) – 元素的XPATH路径
- style_name (string) – 样式名称
-
highlight
(elem_xpaths)¶ 使元素高亮
参数: elem_xpaths (list) – 元素的XPATH路径
-
ready_state
¶ 页面状态
-
set_attribute
(elem_xpaths, attr_name, value)¶ 设置元素属性
参数: - elem_xpaths (list) – 元素的XPATH路径
- attr_name (string) – 属性名
- value (string) – 新的值
-
set_property
(elem_xpaths, prop_name, value)¶ 设置元素的特定值,例如:node.innerHTML
参数: - elem_xpaths (list) – 元素的XPATH路径
- prop_name (string) – property名
- value (string) – 新的值
-
-
class
qt4w.webdriver.webdriver.
WebDriverBase
(webview)¶ 基类:
qt4w.webdriver.webdriver.IWebDriver
WebDriver基类
-
static
create_driver
(webview)¶ 根据webview类型创建对应的WebDriver实例
参数: webview (object) – WebView实例
-
drag_element
(elem_xpaths, from_x, from_y, to_x, to_y)¶ 拖拽元素
参数: - elem_xpaths (list) – 元素的XPATH路径
- from_x (int/float) – 起点横坐标,相对于WebView左上角
- from_y (int/float) – 起点纵坐标,相对于WebView左上角
- to_x (int/float) – 终点横坐标,相对于WebView左上角
- to_y (int/float) – 终点纵坐标,相对于WebView左上角
-
driver_script
= '\n if (typeof window.qt4w_hook_console == \'undefined\') window.qt4w_hook_console = true;\n window[\'qt4w_driver_lib\'] = {\n getScale : function(){\n return 1;\n },\n \n getScreenSize: function(){\n var result = new Array();\n result.push(screen.availWidth);\n result.push(screen.availHeight);\n return result.toString();\n },\n \n selectNodes : function(xpath){\n var oResult = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null);\n var aNodes = new Array(); \n if (oResult != null) { \n var oElement = oResult.iterateNext(); \n while (oElement) { \n aNodes.push(oElement); \n oElement = oResult.iterateNext(); \n } \n }\n return aNodes;\n },\n \n selectNode : function(xpath){\n var nodes = this.selectNodes(xpath);\n if(nodes.length == 0) throw new Error(\'Find element \'+xpath+\' failed\');\n else if(nodes.length > 1) throw new Error(\'Find \'+nodes.length+\' elements match \'+xpath);\n return nodes[0];\n },\n \n getElementZoom: function(node){\n var scale = 1;\n while(node != null && node != document.documentElement){\n var zoom = parseFloat(window.getComputedStyle(node, null).zoom);\n if(zoom != 1){\n scale *= zoom;\n }\n node = node.parentNode;\n }\n return scale;\n },\n \n getElemRect: function(node){\n var result = new Array();\n var rect = node.getBoundingClientRect();\n var scale = this.getScale();\n scale *= this.getElementZoom(node);\n var left = rect.left;\n var top = rect.top;\n var width = rect.width;\n var height = rect.height;\n if (typeof width == \'undefined\') width = rect.right - rect.left;\n if (typeof height == \'undefined\') height = rect.bottom - rect.top;\n result.push(left * scale);\n result.push(top * scale);\n result.push(width * scale);\n result.push(height * scale);\n return result.toString();\n },\n \n initHighlightDiv: function(){\n this.bd0 = document.createElement("div");\n this.bd1 = document.createElement("div");\n this.bd2 = document.createElement("div");\n this.bd3 = document.createElement("div");\n },\n \n showDiv: function(cnt){\n if(cnt % 2 != 0){\n document.body.appendChild(this.bd0);\n document.body.appendChild(this.bd1);\n document.body.appendChild(this.bd2);\n document.body.appendChild(this.bd3);\n } else {\n document.body.removeChild(this.bd0);\n document.body.removeChild(this.bd1);\n document.body.removeChild(this.bd2);\n document.body.removeChild(this.bd3);\n }\n if (cnt){\n cnt--;\n if(window.console) console.log(\'show\' + cnt);\n setTimeout("qt4w_driver_lib.showDiv(" + cnt + ")", 200);\n }\n },\n \n highlight: function(node){\n if (!this.bd0) {\n this.initHighlightDiv();\n }\n var rect = node.getBoundingClientRect();\n var left= rect.left;\n var top = rect.top;\n var width = node.offsetWidth;\n var height = node.offsetHeight;\n if(window.console) console.log(left+\',\'+top+\',\'+width+\',\'+height);\n \n this.bd0.setAttribute("style", "left:" + (left - 1) + "px;top:" + (top - 1) + "px;z-index:32767;"\n + "width:" + (width + 2) + "px;height:2px;position:fixed;background-color: red");\n this.bd1.setAttribute("style", "left:" + (left - 1) + "px;top:" + (top - 1) + "px;z-index:32767;"\n + "width:2px;height:" + (height) + "px;position:fixed;;background-color: red");\n this.bd2.setAttribute("style", "left:" + (left + width - 1) + "px;top:" + (top) + "px;z-index:32767;"\n + "width:2px;height:" + (height) + "px;position:fixed;;background-color: red");\n this.bd3.setAttribute("style", "left:" + (left - 1) + "px;top:" + (top + height - 1) + "px;z-index:32767;"\n + "width:" + (width + 2) + "px;height:2px;position:fixed;;background-color: red");\n //console.log(\'style\'+this.bd0.getAttribute(\'style\')+\'\');\n this.showDiv(3);\n },\n \n scrollToVisible: function(node){\n if(node.scrollIntoViewIfNeeded){\n node.scrollIntoViewIfNeeded();\n }else if(node.scrollIntoView){\n node.scrollIntoView();\n }\n },\n \n logData: [],\n hookConsole: function(){\n var self = this;\n if(window.console && console.log && console.log.apply && window.JSON){\n var hookedConsoleFunc = function(){\n var timeStr = new Date().toLocaleString();\n for(var i=1;i<arguments.length;i++){\n var data = arguments[i];\n if(data instanceof Array){\n data = JSON.stringify(data);\n }else if(data instanceof Object){\n var jsonData = {};\n for(var key in data){\n jsonData[key] = data[key];\n if(jsonData[key]) jsonData[key] = jsonData[key].toString();\n }\n data = JSON.stringify(jsonData);\n }\n self.logData.push(\'[\' + timeStr + \'][console.\' + arguments[0].name + \'] \' + data);\n }\n var args = Array.prototype.slice.call(arguments);\n args.splice(0, 1);\n return arguments[0].apply(this, args);\n }\n\n var hookFunction = function (funcName) {\n if (!console[funcName]) return;\n var origFunc = console[funcName];\n console[funcName] = function(){\n var args = Array.prototype.slice.call(arguments);\n args.splice(0, 0, origFunc);\n return hookedConsoleFunc.apply(this, args);\n }\n }\n\n hookFunction(\'log\');\n hookFunction(\'dir\');\n hookFunction(\'info\');\n hookFunction(\'warn\');\n hookFunction(\'error\');\n }\n },\n \n readLogData: function (count) {\n if (count < 0) count = this.logData.length;\n return this.logData.splice(0, count);\n }\n \n };\n\n if (window.qt4w_hook_console) qt4w_driver_lib.hookConsole();\n '¶
-
eval_script
(frame_xpaths, script)¶ 在指定frame中执行JavaScript,并返回执行结果(该实现需要处理js基础库未注入情况的处理)
参数: - frame_xpaths (list) – frame元素的XPATH路径,如果是顶层页面,怎传入“[]”
- script (string) – 要执行的JavaScript语句
-
fire_event
(elem_xpaths, type)¶ 触发事件
参数: - elem_xpaths (list) – 要触发事件的元素XPATH路径
- type (string) – 事件类型
-
get_attribute
(elem_xpaths, attr_name)¶ 获取元素属性
参数: - elem_xpaths (list) – 元素的XPATH路径
- attr_name (string) – 属性名
-
get_elem_rect
(elem_xpaths, rav=True)¶ 获取元素在页面中的坐标
参数: - elem_xpaths (list) – 元素的XPATH路径
- rav (bool) – 是否是相对于当前frame
-
get_element
(elem_xpaths)¶ 获取控件,如果控件不存在抛出ControlNotFoundError
参数: elem_xpaths (list) – 元素xpath路径
-
get_element_count
(elem_xpaths)¶ 获取满足elem_xpaths的元素个数
参数: elem_xpaths (list) – 元素的XPATH路径
-
get_property
(elem_xpaths, prop_name)¶ 获取元素的特定值,例如:node.innerHTML
参数: - elem_xpaths (list) – 元素的XPATH路径
- prop_name (string) – property名
-
get_ready_state
(frame_xpaths)¶ 获取页面状态
-
get_screen_size
()¶ 获取屏幕大小
-
get_style
(elem_xpaths, style_name)¶ 获取元素的某一样式值
参数: - elem_xpaths (list) – 元素的XPATH路径
- style_name (string) – 样式名称
-
highlight
(elem_xpaths)¶ 使元素高亮
参数: elem_xpaths (list) – 元素的XPATH路径
-
is_elem_focused
(elem_xpaths)¶ 是否是当前有焦点元素
参数: elem_xpaths –
-
read_console_log
(frame_xpaths, count=1)¶ 读取指定条数的日志
参数: - frame_xpaths (list) – 当前页面的XPATH路径
- count (int) – 要读取的日志条数,默认为1
返回: 读取到的日志
-
scroll_to_visible
(elem_xpaths)¶ 将元素滚动到可见区域
参数: elem_xpaths (list) – 元素的XPATH路径
-
set_attribute
(elem_xpaths, attr_name, value)¶ 设置元素属性
参数: - elem_xpaths (list) – 元素的XPATH路径
- attr_name (string) – 属性名
- value (string) – 新的值
-
set_property
(elem_xpaths, prop_name, value)¶ 设置元素的特定值,例如:node.innerHTML
参数: - elem_xpaths (list) – 元素的XPATH路径
- prop_name (string) – property名
- value (string) – 新的值
-
static
Webkit WebDrvier实现
-
class
qt4w.webdriver.webkitwebdriver.
WebkitWebDriver
(webview)¶ 基类:
qt4w.webdriver.webdriver.WebDriverBase
Webkit WebDrvier
-
driver_script
= '\n if (typeof window.qt4w_hook_console == \'undefined\') window.qt4w_hook_console = true;\n window[\'qt4w_driver_lib\'] = {\n getScale : function(){\n return 1;\n },\n \n getScreenSize: function(){\n var result = new Array();\n result.push(screen.availWidth);\n result.push(screen.availHeight);\n return result.toString();\n },\n \n selectNodes : function(xpath){\n var oResult = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null);\n var aNodes = new Array(); \n if (oResult != null) { \n var oElement = oResult.iterateNext(); \n while (oElement) { \n aNodes.push(oElement); \n oElement = oResult.iterateNext(); \n } \n }\n return aNodes;\n },\n \n selectNode : function(xpath){\n var nodes = this.selectNodes(xpath);\n if(nodes.length == 0) throw new Error(\'Find element \'+xpath+\' failed\');\n else if(nodes.length > 1) throw new Error(\'Find \'+nodes.length+\' elements match \'+xpath);\n return nodes[0];\n },\n \n getElementZoom: function(node){\n var scale = 1;\n while(node != null && node != document.documentElement){\n var zoom = parseFloat(window.getComputedStyle(node, null).zoom);\n if(zoom != 1){\n scale *= zoom;\n }\n node = node.parentNode;\n }\n return scale;\n },\n \n getElemRect: function(node){\n var result = new Array();\n var rect = node.getBoundingClientRect();\n var scale = this.getScale();\n scale *= this.getElementZoom(node);\n var left = rect.left;\n var top = rect.top;\n var width = rect.width;\n var height = rect.height;\n if (typeof width == \'undefined\') width = rect.right - rect.left;\n if (typeof height == \'undefined\') height = rect.bottom - rect.top;\n result.push(left * scale);\n result.push(top * scale);\n result.push(width * scale);\n result.push(height * scale);\n return result.toString();\n },\n \n initHighlightDiv: function(){\n this.bd0 = document.createElement("div");\n this.bd1 = document.createElement("div");\n this.bd2 = document.createElement("div");\n this.bd3 = document.createElement("div");\n },\n \n showDiv: function(cnt){\n if(cnt % 2 != 0){\n document.body.appendChild(this.bd0);\n document.body.appendChild(this.bd1);\n document.body.appendChild(this.bd2);\n document.body.appendChild(this.bd3);\n } else {\n document.body.removeChild(this.bd0);\n document.body.removeChild(this.bd1);\n document.body.removeChild(this.bd2);\n document.body.removeChild(this.bd3);\n }\n if (cnt){\n cnt--;\n if(window.console) console.log(\'show\' + cnt);\n setTimeout("qt4w_driver_lib.showDiv(" + cnt + ")", 200);\n }\n },\n \n highlight: function(node){\n if (!this.bd0) {\n this.initHighlightDiv();\n }\n var rect = node.getBoundingClientRect();\n var left= rect.left;\n var top = rect.top;\n var width = node.offsetWidth;\n var height = node.offsetHeight;\n if(window.console) console.log(left+\',\'+top+\',\'+width+\',\'+height);\n \n this.bd0.setAttribute("style", "left:" + (left - 1) + "px;top:" + (top - 1) + "px;z-index:32767;"\n + "width:" + (width + 2) + "px;height:2px;position:fixed;background-color: red");\n this.bd1.setAttribute("style", "left:" + (left - 1) + "px;top:" + (top - 1) + "px;z-index:32767;"\n + "width:2px;height:" + (height) + "px;position:fixed;;background-color: red");\n this.bd2.setAttribute("style", "left:" + (left + width - 1) + "px;top:" + (top) + "px;z-index:32767;"\n + "width:2px;height:" + (height) + "px;position:fixed;;background-color: red");\n this.bd3.setAttribute("style", "left:" + (left - 1) + "px;top:" + (top + height - 1) + "px;z-index:32767;"\n + "width:" + (width + 2) + "px;height:2px;position:fixed;;background-color: red");\n //console.log(\'style\'+this.bd0.getAttribute(\'style\')+\'\');\n this.showDiv(3);\n },\n \n scrollToVisible: function(node){\n if(node.scrollIntoViewIfNeeded){\n node.scrollIntoViewIfNeeded();\n }else if(node.scrollIntoView){\n node.scrollIntoView();\n }\n },\n \n logData: [],\n hookConsole: function(){\n var self = this;\n if(window.console && console.log && console.log.apply && window.JSON){\n var hookedConsoleFunc = function(){\n var timeStr = new Date().toLocaleString();\n for(var i=1;i<arguments.length;i++){\n var data = arguments[i];\n if(data instanceof Array){\n data = JSON.stringify(data);\n }else if(data instanceof Object){\n var jsonData = {};\n for(var key in data){\n jsonData[key] = data[key];\n if(jsonData[key]) jsonData[key] = jsonData[key].toString();\n }\n data = JSON.stringify(jsonData);\n }\n self.logData.push(\'[\' + timeStr + \'][console.\' + arguments[0].name + \'] \' + data);\n }\n var args = Array.prototype.slice.call(arguments);\n args.splice(0, 1);\n return arguments[0].apply(this, args);\n }\n\n var hookFunction = function (funcName) {\n if (!console[funcName]) return;\n var origFunc = console[funcName];\n console[funcName] = function(){\n var args = Array.prototype.slice.call(arguments);\n args.splice(0, 0, origFunc);\n return hookedConsoleFunc.apply(this, args);\n }\n }\n\n hookFunction(\'log\');\n hookFunction(\'dir\');\n hookFunction(\'info\');\n hookFunction(\'warn\');\n hookFunction(\'error\');\n }\n },\n \n readLogData: function (count) {\n if (count < 0) count = this.logData.length;\n return this.logData.splice(0, count);\n }\n \n };\n\n if (window.qt4w_hook_console) qt4w_driver_lib.hookConsole();\n \n window.Notification = undefined; // disable Notification\n window[\'qt4w_driver_lib\'][\'getScale\'] = function(){return window.devicePixelRatio;};\n window[\'qt4w_driver_lib\'][\'getElemRect\'] = function(node) {\n var result = new Array();\n var rect = node.getBoundingClientRect();\n var scale = this.getScale();\n scale *= this.getElementZoom(node);\n var left = rect.left;\n var top = rect.top;\n if (document.documentElement.scrollWidth > document.documentElement.clientWidth) {\n // \xe9\xa1\xb5\xe9\x9d\xa2\xe6\x9c\xaa\xe9\x80\x82\xe9\x85\x8d\xe7\xbb\x88\xe7\xab\xaf\n if (window.visualViewport) {\n // above Chrome 61\n left -= window.visualViewport.offsetLeft;\n top -= window.visualViewport.offsetTop;\n } else {\n if (window.scrollX && document.documentElement.getBoundingClientRect().left == 0) {\n // getBoundingClientRect return fix position\n left -= window.scrollX;\n }\n if (window.scrollY && document.documentElement.getBoundingClientRect().top == 0) {\n top -= window.scrollY;\n }\n }\n }\n \n result.push(left * scale);\n result.push(top * scale);\n result.push(rect.width * scale);\n result.push(rect.height * scale);\n return result.toString();\n }\n document.addEventListener(\'click\', function(event){console.log(\'[ClickListener](\' + event.clientX + \', \' + event.clientY + \')\');}, true);\n '¶
-
qt4w.webview package¶
IWebView接口
-
class
qt4w.webview.webview.
IWebView
¶ 基类:
object
IWebView接口
-
click
(x_offset, y_offset)¶ 点击WebView中的某个坐标
参数: - x_offset (int/float) – 与WebView左上角的横向偏移量
- y_offset (int/float) – 与WebView左上角的纵向偏移量
-
double_click
(x_offset, y_offset)¶ 双击WebView中的某个坐标
参数: - x_offset (int/float) – 与WebView左上角的横向偏移量
- y_offset (int/float) – 与WebView左上角的纵向偏移量
-
drag
(x1, y1, x2, y2)¶ 从(x1, y1)点拖动到(x2, y2)点
参数: - x1 (int/float) – 起点横坐标
- y1 (int/float) – 起点纵坐标
- x2 (int/float) – 终点横坐标
- y2 (int/float) – 终点纵坐标
-
eval_script
(frame_xpaths, script)¶ 在指定frame中执行JavaScript,并返回执行结果
参数: - frame_xpaths (list) – frame元素的XPATH路径,如果是顶层页面,则传入“[]”
- script (string) – 要执行的JavaScript语句
-
hover
(x_offset, y_offset)¶ 参数: - x_offset (int/float) – 与WebView左上角的横向偏移量
- y_offset (int/float) – 与WebView左上角的纵向偏移量
-
long_click
(x_offset, y_offset, duration=1)¶ 长按WebView中的某个坐标
参数: - x_offset (int/float) – 与WebView左上角的横向偏移量
- y_offset (int/float) – 与WebView左上角的纵向偏移量
- duration (int/float) – 按住的持续时间
-
rect
¶ WebView控件的坐标信息
-
right_click
(x_offset, y_offset)¶ 右键点击WebView中的某个坐标
参数: - x_offset (int/float) – 与WebView左上角的横向偏移量
- y_offset (int/float) – 与WebView左上角的纵向偏移量
-
screenshot
()¶ 当前WebView的截图
返回: PIL.Image
-
scroll
(backward=True)¶ 参数: backward (bool) – 是否向后滚动,默认为True
-
send_keys
(text)¶ 发送可见字符按键
参数: text (string) – 要输入的文本
-
upload_file
(file_path)¶ 上传文件
参数: file_path (str) – 文件路径
-
visible_rect
¶ WebView控件可见区域的坐标信息
-
webdriver_class
¶ WebView对应的WebDriver类
-
IWebWiew接口定义
Submodules¶
qt4w.util module¶
QT4W公共库
-
exception
qt4w.util.
ControlAmbiguousError
¶ -
找到多个控件
-
exception
qt4w.util.
ControlNotFoundError
¶ -
Web元素未找到
-
class
qt4w.util.
Deprecated
(new_func)¶ 基类:
object
废弃函数包装
-
class
qt4w.util.
EnumKeyCode
¶ 基类:
object
按键定义
-
A
= ('KeyA', 65)¶
-
ALT
= ('Alt', 18)¶
-
B
= ('KeyB', 66)¶
-
BACKSPACE
= ('Backspace', 8)¶
-
C
= ('KeyC', 67)¶
-
CTRL
= ('Control', 17)¶
-
D
= ('KeyD', 68)¶
-
DELETE
= ('Delete', 46)¶
-
DOWN
= ('ArrowDown', 40)¶
-
E
= ('KeyE', 69)¶
-
END
= ('End', 35)¶
-
ENTER
= ('Enter', 13)¶
-
ESC
= ('Escape', 27)¶
-
F
= ('KeyF', 70)¶
-
F1
= ('F1', 112)¶
-
F10
= ('F10', 121)¶
-
F11
= ('F11', 122)¶
-
F12
= ('F12', 123)¶
-
F2
= ('F2', 113)¶
-
F3
= ('F3', 114)¶
-
F4
= ('F4', 115)¶
-
F5
= ('F5', 116)¶
-
F6
= ('F6', 117)¶
-
F7
= ('F7', 118)¶
-
F8
= ('F8', 119)¶
-
F9
= ('F9', 120)¶
-
G
= ('KeyG', 71)¶
-
H
= ('KeyH', 72)¶
-
HOME
= ('Home', 36)¶
-
I
= ('KeyI', 73)¶
-
INSERT
= ('Insert', 45)¶
-
J
= ('KeyJ', 74)¶
-
K
= ('KeyK', 75)¶
-
L
= ('KeyL', 76)¶
-
LEFT
= ('ArrowLeft', 37)¶
-
M
= ('KeyM', 77)¶
-
N
= ('KeyN', 78)¶
-
O
= ('KeyO', 79)¶
-
P
= ('KeyP', 80)¶
-
PAGEDOWN
= ('PageDown', 34)¶
-
PAGEUP
= ('PageUp', 33)¶
-
Q
= ('KeyQ', 81)¶
-
R
= ('KeyR', 82)¶
-
RIGHT
= ('ArrowRight', 39)¶
-
S
= ('KeyS', 83)¶
-
SHIFT
= ('Shift', 16)¶
-
T
= ('KeyT', 84)¶
-
TAB
= ('Tab', 9)¶
-
U
= ('KeyU', 85)¶
-
UP
= ('ArrowUp', 38)¶
-
V
= ('KeyV', 86)¶
-
W
= ('KeyW', 87)¶
-
X
= ('KeyX', 88)¶
-
Y
= ('KeyY', 89)¶
-
Z
= ('KeyZ', 90)¶
-
static
init
()¶
-
static
parse
(text)¶
-
-
class
qt4w.util.
Frame
(_id, name, url)¶ 基类:
object
frame element
-
add_child
(frame)¶ 添加子frame
-
find_child_frame
(name, url)¶ 查找子frame
-
id
¶
-
name
¶
-
url
¶
-
-
class
qt4w.util.
FrameSelector
(webdriver, root_frame)¶ 基类:
object
Frame定位
-
get_frame_by_xpath
(frame_xpaths)¶ 根据XPath对象查找frame
参数: frame_xpaths (list) – frame的xpath数组 返回: Frame object
-
-
exception
qt4w.util.
JavaScriptError
(frame, err_msg)¶ 基类:
exceptions.RuntimeError
执行JavaScript报错
-
frame
¶ 发生JS异常的frame
-
message
¶
-
-
class
qt4w.util.
LazyDict
(getter, setter=None, lister=None)¶ 基类:
object
类字典容器,本身不存储数据,只在需要时调用相应函数实现读写操作
-
exception
qt4w.util.
QT4WRuntimeError
¶ 基类:
exceptions.RuntimeError
QT4W运行时错误
-
message
¶ 解决python3上没有message属性的问题
-
-
class
qt4w.util.
Rect
(rect)¶ 基类:
object
控件坐标区域
-
Height
¶
-
Left
¶
-
Top
¶
-
Width
¶
-
height
¶ 高度
-
left
¶ 左上角横坐标
-
top
¶ 左上角纵坐标
-
width
¶ 宽度
-
-
exception
qt4w.util.
TimeoutError
¶ -
超时错误
-
class
qt4w.util.
WebElementAttributes
(getter, setter=None, lister=None)¶ -
供WebElement的Attributes属性使用的类字典容器
-
class
qt4w.util.
WebElementStyles
(getter, setter=None, lister=None)¶ -
供WebElement的Styles属性使用的类字典容器
-
qt4w.util.
encode_wrap
(func)¶ 处理函数返回值编码
-
qt4w.util.
general_encode
(s)¶ 字符串通用编码处理 python2 => utf8 python3 => unicode
-
qt4w.util.
lazy_init
(func)¶ 懒初始化
-
qt4w.util.
unicode_decode
(s)¶ 将字符串解码为unicode编码
qt4w.webcontrols module¶
WebPage、WebElement接口类
-
class
qt4w.webcontrols.
ControlContainer
¶ 基类:
object
控件容器基类
-
Controls
¶
-
control
(name)¶ 获取控件实例
-
get_metis_view
()¶ 返回MetisView
-
ui_control_type
= None¶
-
ui_map
= {}¶
-
updateLocator
(locators)¶ 更新控件定位参数
参数: locators (dict) – 定位参数,格式是 {‘控件名’:{‘type’:控件类, 控件类的参数dict列表}, …}
-
update_ui_map
(ui_map)¶ 从指定的ui_map中更新控件定义
-
-
class
qt4w.webcontrols.
FrameElement
(root, locator)¶ 基类:
qt4w.webcontrols.WebElement
frame/iframe元素
-
FramePage
¶
-
framepage
¶ frame中包含的WebPage
-
-
class
qt4w.webcontrols.
IWebElement
¶ 基类:
object
WebElement接口定义
-
Attributes
¶
-
BoundingRect
¶ 元素位置信息
-
Displayed
¶
-
InnerHtml
¶
-
InnerText
¶
-
attributes
¶ 元素的属性集合
-
click
(x_offset=None, y_offset=None)¶ 点击元素,默认点击元素中点
参数: - x_offset (int或float) – 距离控件区域左上角的横向偏移。
- y_offset (int或float) – 距离控件区域左上角的纵向偏移。
-
displayed
¶ 元素是否显示
-
double_click
(x_offset=None, y_offset=None)¶ 双击元素,默认双击元素中点
参数: - x_offset (int或float) – 距离控件区域左上角的横向偏移。
- y_offset (int或float) – 距离控件区域左上角的纵向偏移。
-
drag
(x, y)¶ 拖放元素到指定位置
参数: - x (int或float) – 拖放终点距离起点的横向偏移。
- y (int或float) – 放终点距离起点的纵向偏移。
-
exist
()¶ 元素是否存在
-
focused
¶ 元素是否有焦点
-
getElement
(*args, **kwargs)¶
-
getElements
(*args, **kwargs)¶
-
get_element
(locator)¶ 在当前元素的子孙元素中查找元素,返回第一个匹配的元素
参数: locator (string或XPath) – 元素的xpath路径
-
get_elements
(locator)¶ 在当前元素的子孙元素中查找元素,返回包含所有匹配的元素的列表
参数: locator (string或XPath) – 元素的xpath路径
-
hover
(x_offset=None, y_offset=None)¶ 鼠标悬停
参数: - x_offset (int或float) – 距离控件区域左上角的横向偏移。
- y_offset (int或float) – 距离控件区域左上角的纵向偏移。
-
inner_html
¶ 元素所包含的HTML代码
-
inner_text
¶ 元素所包含的文本
-
long_click
(x_offset=None, y_offset=None, duration=1)¶ 长按元素
参数: - x_offset (int/float) – 距离控件区域左上角的横向偏移
- y_offset (int/float) – 距离控件区域左上角的纵向偏移
- duration (int/float) – 按住的持续时间
-
page
¶ 元素所在WebPage
-
rect
¶ 元素位置信息
-
right_click
(x_offset=None, y_offset=None)¶ 右击元素,默认右击元素中点
参数: - x_offset (int或float) – 距离控件区域左上角的横向偏移。
- y_offset (int或float) – 距离控件区域左上角的纵向偏移。
-
screenshot
()¶ 设置控件截图
-
scroll
(x, y)¶ 滚动元素
参数: - x (int) – 横向滚动的偏移,负值向左,正值向右
- y (int) – 纵向滚动的偏移,负值向上,正值向下
-
sendKeys
(*args, **kwargs)¶
-
send_keys
(keys)¶ 发送按键
参数: keys (string) – 发送的按键
-
set_focus
()¶ 设为焦点
-
styles
¶ 元素的样式集合
-
visible
¶ 元素是否视觉可见
-
wait_for_attribute
(name, value, timeout=10, interval=0.5)¶ 暂停程序执行,直到当前元素的指定属性变为特定值
参数: - name (string) – 要等待的属性名
- value (string) – 要等待的属性值
:param timeout:超时时间 :type timeout: int或float :param interval:重试间隔时间 :type interval:int或float
-
wait_for_style
(name, value, timeout=10, interval=0.5)¶ 暂停程序执行,直到当前元素的指定样式变为特定值
参数: - name (string) – 要等待的样式名
- value (string) – 要等待的样式值
:param timeout:超时时间 :type timeout: int或float :param interval:重试间隔时间 :type interval:int或float
-
wait_for_text
(value, timeout=10, interval=0.5)¶ 暂停程序执行,直到当前元素的InnerText变为特定值
参数: - value (string) – 要等待的特定值
- timeout (int或float) – 超时时间
:param interval:重试间隔时间 :type interval: int或float
-
wait_for_visible
(timeout=10, interval=0.5)¶ 等待控件可见
参数: timeout (float) – 超时时间 :param interval:查询间隔时间 :type interval: float
-
-
class
qt4w.webcontrols.
IWebPage
¶ 基类:
object
WebPage接口定义
-
ReadyState
¶
-
Title
¶
-
Url
¶
-
accessible_object
¶ accessible对象
-
activate
()¶ 激活承载页面的窗口
-
browser_type
¶ 浏览器类型
-
close
()¶ 关闭承载页面的窗口
页面cookie
-
execScript
(*args, **kwargs)¶
-
exec_script
(script)¶ 在页面中执行JavaScript代码,并返回直接结果
参数: script (string) – 要执行的代码
-
getElement
(*args, **kwargs)¶
-
getElements
(*args, **kwargs)¶
-
get_element
(locator)¶ 在页面中查找元素,返回第一个匹配的元素
参数: locator (string或XPath) – 元素的xpath路径
-
get_elements
(locator)¶ 在页面中查找元素,返回包含所有匹配的元素的列表
参数: locator (string或XPath) – 元素的xpath路径
-
pull_down_refresh
()¶ 下拉刷新
-
pull_up_refresh
()¶ 上拉刷新
-
read_console_log
(timeout=None)¶ 读取一条console.log输出的日志
参数: timeout (int) – 读取日志的超时时间,为None表示不会超时
-
ready_state
¶ 页面状态
-
release
()¶ 释放占用的资源
-
screenshot
()¶ 获取page页面截图
-
scroll
(x, y)¶ 滚动
参数: - x (int) – 横向滚动的偏移,负值向左,正值向右
- y (int) – 纵向滚动的偏移,负值向上,正值向下
-
title
¶ 页面标题
-
upload_file
(file_path)¶ 上传文件
参数: file_path (str) – 文件路径
-
url
¶ 页面url
-
waitForReady
(*args, **kwargs)¶
-
wait_for_ready
(timeout=60)¶ 等待页面状态变为ready
参数: timeout (int) – 超时时间
-
-
class
qt4w.webcontrols.
InputElement
(root, locator)¶ 基类:
qt4w.webcontrols.WebElement
input元素
-
value
¶ 当前的value
-
-
class
qt4w.webcontrols.
MetisView
(page_or_elem)¶ 基类:
object
实现IMetisView接口
-
click
(offset_x=None, offset_y=None)¶ 点击 :param offset_x: 相对于该控件的坐标offset_x,百分比( 0 -> 1 ),不传入则默认该控件的中央 :type offset_x: float|None :param offset_y: 相对于该控件的坐标offset_y,百分比( 0 -> 1 ),不传入则默认该控件的中央 :type offset_y: float|None
-
double_click
(offset_x=None, offset_y=None)¶ 双击
-
long_click
(offset_x=None, offset_y=None)¶ 长按
-
os_type
¶ 系统类型,例如”android”,”ios”,”pc”
-
rect
¶ 元素相对坐标(x, y, w, h)
-
screenshot
()¶ 当前容器的区域截图 :return: PIL.image
-
send_keys
(text)¶ 发送可见字符按键
参数: text (string) – 要输入的文本
-
-
class
qt4w.webcontrols.
SelectElement
(root, locator)¶ 基类:
qt4w.webcontrols.WebElement
select元素
-
options
¶ 返回所有选项列表
-
selection
¶ 当前选择项
-
-
class
qt4w.webcontrols.
UIListBase
(root, locator)¶ 基类:
object
List控件基类
-
filter
(condition)¶ 根据条件过滤,找到满足条件的项即返回,如果找不到则抛出异常
参数: condition (dict) – 过滤条件
-
ui_control_type
= None¶
-
-
class
qt4w.webcontrols.
WebElement
(root, locator)¶ 基类:
qt4w.webcontrols.ControlContainer
,qt4w.webcontrols.IWebElement
IWebElement实现
-
BoundingRect
¶ 元素位置信息
-
attributes
¶ 元素的属性集合
-
click
(x_offset=None, y_offset=None, highlight=True)¶ 点击元素,默认点击元素中点
参数: - x_offset (int或float) – 距离控件区域左上角的横向偏移。
- y_offset (int或float) – 距离控件区域左上角的纵向偏移。
- highlight (bool) – 是否高亮元素
-
displayed
¶
-
double_click
(x_offset=None, y_offset=None)¶ 双击元素,默认双击元素中点
参数: - x_offset (int或float) – 距离控件区域左上角的横向偏移。
- y_offset (int或float) – 距离控件区域左上角的纵向偏移。
-
drag
(x, y)¶ 拖放元素到指定位置
参数: - x (int或float) – 拖放终点距离起点的横向偏移。
- y (int或float) – 放终点距离起点的纵向偏移。
-
exist
()¶ 元素是否存在
-
focused
¶
-
get_element
(locator, elem_cls=None)¶ 在当前元素的子孙元素中查找元素,返回第一个匹配的元素
参数: locator (string或XPath) – 元素的xpath路径 Paran elem_cls: 返回的元素类型
-
get_elements
(locator, elem_cls=None)¶ 在当前元素的子孙元素中查找元素,返回包含所有匹配的元素的列表
参数: locator (string或XPath) – 元素的xpath路径 Paran elem_cls: 返回的元素类型
-
highlight
(*args, **kwargs)¶
-
hover
(x_offset=None, y_offset=None)¶ 鼠标悬停
参数: - x_offset (int或float) – 距离控件区域左上角的横向偏移。
- y_offset (int或float) – 距离控件区域左上角的纵向偏移。
-
inner_html
¶
-
inner_text
¶
-
long_click
(x_offset=None, y_offset=None, duration=1)¶ 长按元素
参数: - x_offset (int/float) – 距离控件区域左上角的横向偏移
- y_offset (int/float) – 距离控件区域左上角的纵向偏移
- duration (int/float) – 按住的持续时间
-
page
¶ 元素所在WebPage
-
post_init
()¶ 窗口类自定义的初始化逻辑
-
rect
¶
-
right_click
(x_offset=None, y_offset=None)¶ 右击元素,默认右击元素中点
参数: - x_offset (int或float) – 距离控件区域左上角的横向偏移。
- y_offset (int或float) – 距离控件区域左上角的纵向偏移。
-
screenshot
()¶ 设置控件截图
-
scroll
(x, y)¶ 滚动元素
参数: - x (int) – 横向滚动的偏移,负值向左,正值向右
- y (int) – 纵向滚动的偏移,负值向上,正值向下
-
send_keys
(keys)¶ 发送按键
参数: keys (string) – 发送的按键
-
set_focus
()¶ 设为焦点
-
styles
¶ 元素的样式集合
-
ui_control_type
¶ WebElement
的别名
-
visible
¶ 元素是否视觉可见
-
wait_for_attribute
(name, value, timeout=10, interval=0.5)¶ 暂停程序执行,直到当前元素的指定属性变为特定值
参数: - name (string) – 要等待的属性名
- value (string) – 要等待的属性值
:param timeout:超时时间 :type timeout: int或float :param interval:重试间隔时间 :type interval:int或float
-
wait_for_style
(name, value, timeout=10, interval=0.5)¶ 暂停程序执行,直到当前元素的指定样式变为特定值
参数: - name (string) – 要等待的样式名
- value (string) – 要等待的样式值
:param timeout:超时时间 :type timeout: int或float :param interval:重试间隔时间 :type interval:int或float
-
wait_for_text
(text, timeout=10, interval=0.5)¶ 暂停程序执行,直到当前元素的InnerText变为特定值
参数: - text (string) – 要等待的特定值
- timeout (int或float) – 超时时间
:param interval:重试间隔时间 :type interval: int或float
-
wait_for_value
(value, timeout=10, interval=0.5)¶ 暂停程序执行,直到当前元素的InnerText变为特定值
参数: - value (string) – 要等待的特定值
- timeout (int或float) – 超时时间
:param interval:重试间隔时间 :type interval: int或float
-
wait_for_visible
(timeout=10, interval=0.5)¶ 等待控件可见
参数: timeout (float) – 超时时间 :param interval:查询间隔时间 :type interval: float
-
-
class
qt4w.webcontrols.
WebPage
(webview_or_webpage, locator=None, wait_for_ready=True)¶ 基类:
qt4w.webcontrols.ControlContainer
,qt4w.webcontrols.IWebPage
IWebPage接口实现
-
activate
()¶ 激活承载页面的窗口
-
browser_type
¶ 浏览器类型
-
close
()¶ 关闭承载页面的窗口
页面cookie
-
exec_script
(**kwargs)¶
-
get_element
(locator, elem_cls=None)¶ 在页面中查找元素,返回第一个匹配的元素
参数: locator (string或XPath) – 元素在当前页面的xpath路径 Paran elem_cls: 返回的元素类型
-
get_elements
(locator, elem_cls=None)¶ 在页面中查找元素,返回包含所有匹配的元素的列表
参数: locator (string或XPath) – 元素的xpath路径 Paran elem_cls: 返回的元素类型
-
pull_down_refresh
()¶ 下拉刷新
-
pull_up_refresh
()¶ 上拉刷新
-
read_console_log
(timeout=None)¶ 读取一条console.log输出的日志
参数: timeout (int) – 读取日志的超时时间,为None表示不会超时
-
ready_state
¶ 页面状态
-
release
()¶ 释放占用的资源
-
screenshot
()¶ 获取page页面截图
返回: PIL.Image
-
scroll
(x, y)¶ 滚动
参数: - x (int) – 横向滚动的偏移,负值向左,正值向右
- y (int) – 纵向滚动的偏移,负值向上,正值向下
-
title
¶ 页面标题
-
ui_control_type
¶ WebElement
的别名
-
upload_file
(file_path)¶ 上传文件
参数: file_path (str) – 文件路径
-
url
¶ 页面url
-
wait_for_ready
(timeout=60)¶ 等待页面状态变为ready
参数: timeout (int) – 超时时间
-
-
qt4w.webcontrols.
ui_list
(control_cls)¶ 列表类型