Geb UI 自动化入门
- 1.元素定位
- `使用正则表达式进行模式匹配`
- `Navigator对象的find()和$()方法可以用来查找子元素`
- `Navigator对象的filter(), not(), has(), hasNOt() 方法可以用来减少匹配的元素`
- `在单元素导航器上调用tag(), text(), classes() 方法可以分别获取元素的标签,文本以及类名,使用@语法或attr()方法可以获取对应属性的值`
- `常用CSS定位语法入门:id, class, attribute,后代元素, 子元素`
- 2.Browser
- `Geb执行的入口点是Browser对象。每个Browser对象底层都绑定了一个WebDriver实例用于驱动浏览器,并且具有“当前页面”的概念,表示浏览器当前所处页面`
- `Browser实例内维护了一个baseUrl属性,该属性用于解析所有相对的URL。/的位置对URL解析有很大影响,最理想的方式就是书写基准URL时使用后缀的/,书写相对URL时不使用前缀的/`
- `当你的Web 应用涉及多Tab或Window时,(例如:当点击一个设置了target属性的链接时打开了一个新标签页),你可以使用withWindow()或withNewWindow()方法在其他窗口的上下文中执行操作`
- 3.Page
- `Geb构建了一套模版风格的用于定义页面内容的领域专用语言,支持非常简洁而又灵活的页面内容定义。Page对象定义了一个叫做content的静态闭包属性来表示页面的内容。`
- `页面内容模版可以返回任何值。在大部分使用场景下,它们都是通过使用$()方法进而返回一个Navigator对象,但其实它们可以返回任何值`
- `认识到<>`代码是在页面对象上执行的,这一点很重要。这使得像?这样在定义页面内容时重用已定义页面内容是允许的
- `不仅可以使用已定义的页面内容,使用类型中定义的其他东西(如:属性或方法)也可以的:`
- `页面内容定义模版选项`
1.元素定位
$()方法是访问浏览器中页面内容的入口点。它返回一个与jQuery对象累屎的Navigator导航器对象。它们的相似之处在于Navigator导航器对象代表了一个或者多个页面元素,并且能够用来进一步提炼或查询匹配的页面内容。
$()方法的签名如下:
$(<<css selector>>, <<index or ranger>>, <<attribute/text matchers>>)
具体例子:
$('h1', 2, class:'heading')
注: 这将会查找到页面上第3个(下标索引从0开始) class属性是heading的h1元素。
$()方法的所有参数都是可选的,也就是说下面这些形式都是合法的:
$('div p', 0)
$('div p', title: 'something')
$(0)
$(title: 'something')
注:$()方法有一个别名方法:“find”
,如果使用$不合你味口,完全可以使用find()方法来替换
使用正则表达式进行模式匹配
上面我们看到可以通过将text或属性的值设置为String类型来精确匹配元素的整个文本或者属性值,除此之外,我们还可以给这些属性或text传递正则表达式(pattern)来进行模式匹配:
assert $('p', text: ~/p./).size() == 2
Geb本身还自带了一堆模式匹配的快捷方法:
assert $('p', text: startsWith('p')).size() == 2
assert $('p', text: endsWith('2')).size() == 1
Navigator对象的find()和$()方法可以用来查找子元素
<div class="a">
<p class="c">geb</p>
</div>
<div class="b">
<input type="text"/>
</div>
我们可以通过下面的方式选择p.c:$(“div p.c”)
$("div").find(".c")
$("div").$(".c")
Navigator对象的filter(), not(), has(), hasNOt() 方法可以用来减少匹配的元素
我们可以使用?的方法选择包含p元素的div容器:
$("div").has("p")
或者像?这样选择包含一个type属性是“text”的input元素的div:
$("div").has("input", type: "text")
?这样选择不包含p元素的div:
$("div").hasNot("p")
?这样选择不包含一个type属性是“text”的input元素的div:
$("div").hasNot("input", type: "text")
在单元素导航器上调用tag(), text(), classes() 方法可以分别获取元素的标签,文本以及类名,使用@语法或attr()方法可以获取对应属性的值
?这段HTML为例
<div id=the_id>
<p titile="aaa" class="a para">a1</p>
<p titile="bbbb" class="b para">b1</p>
<p titile="cccc" class="c para">c1</p>
</div>
?这些断言都会通过
assert $(".a").text() == "a"
assert $(".a").tag() == 'p'
assert $(".a").@titile == 'aaa'
assert $('.a').classes() == ['a', 'para']
常用CSS定位语法入门:id, class, attribute,后代元素, 子元素
id选择器:div#the_id
类选择器:p.a.para
属性选择器:p[title=‘aaa’][属性2]
后代选择器:div#the_id p
子元素选择器:div#the_id > p
2.Browser
Geb执行的入口点是Browser对象。每个Browser对象底层都绑定了一个WebDriver实例用于驱动浏览器,并且具有“当前页面”的概念,表示浏览器当前所处页面
Browser类有一个静态的driver方法能使编写Geb脚本更加方便。
Browser.driver{
go "signup"
assert $("h1").text() == "Signup Page"
}
?与?的写法等效:
def browser = new Broswer()
browser.go "signup"
assert browser.$("h1").text() == "Signup Page"
Browser实例内维护了一个baseUrl属性,该属性用于解析所有相对的URL。/的位置对URL解析有很大影响,最理想的方式就是书写基准URL时使用后缀的/,书写相对URL时不使用前缀的/
Page对象中可以定义一个url属性,当导航到指定的Page对象时就会用到这个url属性。导航到Page对象所代表的页面时通过Browser对象的to()或via()方法实现的。
class SignupPage extends Page {
static utl = 'signup'
}
void 'test' {
Browser.driver {
to SignupPage
assert $('h1').text() == 'Signup Page'
assert page instanceof SignupPage
}
}
to()和via()方法将会向解析后的URL发起请求,并把Browser对象中的page实例设置为它们所访问的Page类型的一个实例。绝大部分的Geb脚本或测试都时从一个to()或via()方法调用开始的。
Page中可以定义at检查器,Browser对象可以使用at检查器来校验它当前是否处于某个页面。
class SignupPage extends Page {
static url = 'signup'
static at = {
$('h1').text() == 'Signup Page'
}
}
void 'test' {
Browser.driver {
to SignupPage
}
}
注意:最好不要在at检查器中使用显示的return语句。Geb会对at检查器中的所有检查点进行转换,使得检查器中的每条语句都被加上断言(就像Spock中的then:语句块一样)。多亏这个机制,它使得at检查器失败的时候,能立刻看到检查器求值失败的原因
to()方法接受一个页面类型或页面实例作为参数,并且会验证浏览器最终会停留在其参数所指定的页面。如果在发起到某个页面的请求过程中会发生重定向,最终把浏览器带到一个不同的页面,我们就应该使用via()方法:
Browser.driver {
via SecurePage
at AccessDeniedPage
}
当你的Web 应用涉及多Tab或Window时,(例如:当点击一个设置了target属性的链接时打开了一个新标签页),你可以使用withWindow()或withNewWindow()方法在其他窗口的上下文中执行操作
如果你已经指定你想操作的窗口的名称,就可以使用withWindow(String windowName, Closure block)方法在该窗口的上下文中执行操作。
假设下面这段HTML代码是在baseUrl指定的页面中渲染的:
<a href="http://www.gebish.org" target="myWindow">Geb</a>
下面这段代码将能执行成功:
Browswe.driver {
go()
$("a").click()
withWindow("myWindow") {
assert title = "Geb - Very Groovy Browser Automation"
}
}
如果你想在执行操作过程中新打开的窗口的上下文中执行代码,可以使用withNewWindow(Closure windowOpeningBlock, Closure block)方法来处理。
Browser.driver {
go()
withNewWindow({$('a').click()}) {
assert title == 'Geb - Very Groovy Browser Automation'
}
}
3.Page
Geb构建了一套模版风格的用于定义页面内容的领域专用语言,支持非常简洁而又灵活的页面内容定义。Page对象定义了一个叫做content的静态闭包属性来表示页面的内容。
给定下面的HTML:
<div id="a">aaa</div>
我们可以像?这样来定义这块页面内容:
class PageWithDiv extends Page {
static content = {
theDiv {$('div', id: 'a')}
}
}
可以看出,定义页面内容的DSL的通用格式如下:
<<name>> {<<definition>>}
其中<>表示一段Groovy代码,它会在页面实例上执行(即其中的未定义的方法调用或属性访问都会到页面对象上去解析)。下面代码展示了如何使用定义好的页面内容:
Browser.driver {
to PageWithDiv
assert theDiv.text() == 'aaa'
}
页面内容定义DSL实际上定义的是页面内容模版,下面的例子可以很好的展示这一点:
class TemplatedPageWithDiv extends Page {
static content = {
theDiv {id -> $('div', id: theId) }
}
}
Browser.driver {
to TemplatedPageWithDiv
assert theDiv('a').text() == 'aaa'
}
这里我们看到,定义了一个叫做theDiv的页面元素,随着我们传递给它的参数不同,它就代表页面上不同的元素,所以说theDiv实际上是任意满足标签为‘div’,id为变量id的页面元素的模版。可以传递给页面内容模版的参数也是没有限制的。
页面内容模版可以返回任何值。在大部分使用场景下,它们都是通过使用$()方法进而返回一个Navigator对象,但其实它们可以返回任何值
class PageWithStringContent extends Page {
static content = {
theDivText { $('div#a').text() }
}
}
Browser.driver {
to PageWithStringContent
assert theDivText == 'aaa'
}
认识到<<definition>>
代码是在页面对象上执行的,这一点很重要。这使得像?这样在定义页面内容时重用已定义页面内容是允许的
class PageWithContentReuse extends Page {
static content = {
theDiv { $('div#a') }
theDivText { theDiv.text() }
}
}
不仅可以使用已定义的页面内容,使用类型中定义的其他东西(如:属性或方法)也可以的:
class PageWithContentUsingAField extends Page {
def divId = 'a'
static content = {
theDiv { $('div', id: divId) }
}
}
或者
class PageWithContentUsingAMethod extends Page {
static content = {
theDiv { $('div', id: divId()) }
}
def divId() {
'a'
}
}
页面内容定义模版选项
to
默认值:null
to选项用来指明如果页面内容被点击后,浏览器应该跳转到哪个页面。
to选项的值将会隐士的作为被点击的页面内容的click()方法的参数,这将会有效的设置点击后浏览器当前页面对象的类型并且会验证该页面的at检查器。该选项也支持所有能够作为Browser.page()方法参数的各种参数形式:
- 一个Page的实例
- 一个Page子类组成的列表
- 一个Page实例组成的列表
使用列表变体时(此处显示页面类)
static content = {
loginButton(to: [LoginSuccessfulPage, LoginFailedPage]) { $("input.loginButton") }
}
然后,在单击时,浏览器的页面将设置为检查器通过的列表中的第一页。这相当于更改页面一节中介绍的浏览器page(Class<? extends Page>[ ] 和 page(Page[ ]))浏览器方法。
使用该to选项的任何变体时传入的页面实例的所有页面类和类必须定义“at”检查器,否则UndefinedAtCheckException将抛出。
预定义的表单控件模块(直接查看blog的12种表单控件模块):