Geb UI 自动化手册(4: 页面内容交互)

4. 页面内容交互

        Geb 通过 Navigator API 提供了一个简洁而又强大的操作浏览器中页面内容和控件的接口。Navigator API 使用了类似于 jQuery 的机制来查找、过滤以及和 DOM 元素进行交互。

 

4.1 $() 方法

        $() 方法是访问浏览器中页面内容的入口点。它返回一个与 jQuery 对象类似的 Navigator 导航器对象。他们的相似之处在于 Navigator 导航器对象代表了一个或多个页面元素,并且能够用来进一步提炼或查询匹配的页面内容。如果一个 $() 方法被调用后,没有匹配任何页面元素,就会返回一个 “空” 的导航器对象,表示没有匹配内容。在空导航器上执行的操作要么返回 null 要么返回另一个空导航器,或者其他合理的值(比如 size() 方法将返回 0)。

$() 方法的签名如下:

$(«css selector», «index or range», «attribute / text matchers»)

下面是一个具体的例子:

$("h1", 2, class: "heading")

 这将会查找到页面上第 3 个(下标索引从 0 开始) class 属性是 heading 的 h1 元素。

$() 方法中的所有参数都是可选的,也就是说下面这些形式都是合法的:

$("div p", 0)
$("div p", title: "something")
$(0)
$(title: "something")

注:$() 方法有一个别名方法: “find”,如果使用 $ 不合你味口,完全可以使用 find() 方法来替换

 

4.1.1 CSS 选择器

        Geb 支持使用底层 WebDriver 能够支持任何的 CSS 选择器:

$('div.some-class p:first-child[title^="someth"]')

 HTMLUnit 驱动仅部分支持 CSS 3。后续版本的 HTMLUnit 驱动可能会有更好的 CSS 3 支持。

 

4.1.2 使用 WebDriver 的 By 类作为选择器

        $() 方法的接受 String 类型的 CSS 选择器作为参数的各种签名形式,都有一个与之对应的接受 WebDriver 的 By 类实例作为参数签名形式。相比于使用 By 选择器,使用 CSS 选择器是 Geb 更加推崇的元素定位方式。因为除了某些 xpath 形式的 By 选择器外(这也是 Geb 支持使用 By 选择器的原因),几乎总能使用 CSS 选择器来定位到那些 By 选择器能够定位到的元素。

下面是一些使用 By 选择器的例子:

$(By.id("some-id"))
$(By.className("some-class"))
$(By.xpath('//p[@class="xpath"]'))

 

4.1.3 元素索引和范围

        当进行页面元素匹配时,可以向 $() 方法传入一个非负整数或整数范围来通过索引限制匹配内容。假设有下面这段 HTML:

<p>a</p>
<p>b</p>
<p>c</p>

我们可以像下面这样通过索引来匹配内容:

assert $("p", 0).text() == "a"
assert $("p", 2).text() == "c"
assert $("p", 0..1)*.text() == ["a", "b"]
assert $("p", 1..2)*.text() == ["b", "c"]

下面内容会进一步介绍 text() 以及 展开点(*.)操作符的使用。

 

4.1.4 属性和文本匹配

        $() 方法中,支持使用 Groovy 的具名参数语法来通过元素的属性或元素节点的文本进行元素匹配。text 这个名称比较特殊,指定 text 就会被认为会通过元素节点的文本值进行元素匹配。除了 text 以外的其他所有的名称都会被认为是使用元素的相关属性进行匹配。

来看下面这段 HTML:

<p attr1="a" attr2="b">p1</p>
<p attr1="a" attr2="c">p2</p>

我们可以向下面这样使用属性来匹配页面元素:

assert $("p", attr1: "a").size() == 2
assert $("p", attr2: "c").size() == 1

多个属性值之间是 “与(and)” 关系:

assert $("p", attr1: "a", attr2: "b").size() == 1

我们可以向下面这样使用元素节点的文本来匹配页面元素:

assert $("p", text: "p1").size() == 1

当然也可以混合使用属性和文本匹配:

assert $("p", text: "p1", attr1: "a").size() == 1

使用正则表达式进行模式匹配

        上面我们看到可以通过将 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

下面是这些快捷方法的完整列表:

大小写敏感 大小写不敏感 描述

startsWith

iStartsWith

Matches values that start with the given value

contains

iContains

Matches values that contain the given value anywhere

endsWith

iEndsWith

Matches values that end with the given value

containsWord

iContainsWord

Matches values that contain the given value surrounded by either whitespace or the beginning or end of the value

notStartsWith

iNotStartsWith

Matches values that DO NOT start with the given value

notContains

iNotContains

Matches values that DO NOT contain the given value anywhere

notEndsWith

iNotEndsWith

Matches values that DO NOT end with the given value

notContainsWord

iNotContainsWord

Matches values that DO NOT contain the given value surrounded by either whitespace or the beginning or end of the value

以上列表中的各个方法自身又可以接受 String 或 Pattern 作为参数:

assert $("p", text: contains(~/\d/)).size() == 2

注:你可能想知道这些神奇的方法是如何工作的,比如,它们是从哪里来的,可以用在什么地方。它们其实是 geb.Page 类中的方法,他们可以用在任何可以使用 $() 方法的地方。这些方法其实就是简单的返回正则表达式模式对象。

 

4.1.5 导航器是可迭代的

        导航器对象实现了 Java 的 Iterable 接口,这样我们可以在导航器上使用 Groovy 提供的许多便捷方法,如 max() 等。

来看下面这个 HTML:

<p>1</p>
<p>2</p>

你可以在导航器实例上像这样使用 max() 方法:

assert $("p").max { it.text() }.text() == "2"

这也意味着 Navigator 对象可以和 Groovy 的展开操作符一起工作:

assert $("p")*.text().max() == "2"

当把导航器当作 Iterable 来使用时,被迭代的内容是导航器精确匹配的页面元素,不包括这些元素的子元素。

 

4.1.6 equals() 和 hashcode()

        Navigator 对象支持相等比较。比较规则也很简单:两个空导航器总是相等的;两个非空导航器仅当他们包含完全相同的元素,并且元素的先后顺序也相同时,才是相等的。

假设有下面这段 HTML:

<p class="a"></p>
<p class="b"></p>

下面这些导航器实例是相等的:

assert $("div") == $(".foo")             //1
assert $(".a") == $(".a")                //2
assert $(".a") == $("p").not(".b")       //3
assert $("p") == $("p")                  //4
assert $("p") == $(".a").add(".b")       //5

//1: 两个空导航器

//2: 两个包含相同元素的单元素导航器

//3: 使用不同方式创建的两个包含相同元素的单元素导航器

//4: 两个包含相同元素的多元素导航器

//5: 使用不同方式创建的两个包含相同元素的多元素导航器

 

下面这些导航器是不等的:

assert $("div") != $("p")                         //1
assert $(".a") != $(".b")                         //2
assert $(".a").add(".b") != $(".b").add(".a")     //3 

//1: 空导航器和非空导航器

//2: 包含不同元素的两个单元素导航器

//3: 包含相同元素但不同顺序但两个多元素导航器

 

4.2 查找和过滤

        Navigator 对象的 find() 和 $() 方法可以用来查找子元素;filter() 和 not() 方法可以用来减少匹配的元素。给定下面这个HTML:

<div class="a">
    <p class="b">geb</p>
</div>
<div class="b">
    <input type="text"/>
</div>

我们可以通过下面的方式选择 p.b :

$("div").find(".b")
$("div").$(".b")

我们可以通过下面的方式选择 div.b :

$("div").filter(".b")

或者:

$(".b").not("p")

我们可以使用下面的方法选择 包含 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")

或者可以像下面这样选择不包含 type 属性是 “submit” 的 input 元素的两个div:

$("div").hasNot("input", type: "submit")

Navigator 的 find() 和 $() 方法能够支持和前面介绍的 geb.Page 上的 $() 方法能够接受的所有参数形式。

filter(),not(),has(),hasNot() 方法具有相同的签名:它们接受一个字符串形式的选择器,一个 map,或者两者都有。

这些方法都返回一个新的导航器对象,表示过滤后新内容。

 

4.3 构造导航器

        Geb 允许使用其他已有的导航器对象来合成新的导航器对象,这在你无法用一次查询来表示所有想要获取的页面内容时,特别有用。合成导航器很简单,只需要将合成导航器的所需要的那些导航器对象传递给 $() 即可。

我们来看下面这段 HTML:

<p class="a">1</p>
<p class="b">2</p>
<p class="c">3</p>

你可以使用下面的方式创建一个同时代表段落 a 和 b 的导航器:

assert $($("p.a"), $("p.b"))*.text() == ["1", "2"]

另一种创建的方法是使用 Navigator 对象的 add() 方法,它接受一个 String 或 WebDriver 的 By 选择器作为参数:

assert $("p.a").add("p.b").add(By.className("c"))*.text() == ["1", "2", "3"]

最后,你还可以使用页面内容来构造导航器,给定下面的页面内容定义:

static content = {
    pElement { pClass -> $('p', class: pClass) }
}

你可以使用下面的方式来构造导航器对象:

assert $(pElement("a"), pElement("b"))*.text() == ["1", "2"]

 

4.4 遍历页面内容

        导航器对象上提供了方法来选取导航器所匹配内容周围的那些页面内容。假设给定下面的 HTML:

<div class="a">
    <div class="b">
        <p class="c"></p>
        <p class="d"></p>
        <p class="e"></p>
    </div>
    <div class="f"></div>
</div>

你可以像下面这样选择 p.d 周围的页面内容:

assert $("p.d").previous() == $("p.c")
assert $("p.e").prevAll() == $("p.c").add("p.d")
assert $("p.d").next() == $("p.e")
assert $("p.c").nextAll() == $("p.d").add("p.e")
assert $("p.d").parent() == $("div.b")
assert $("p.c").siblings() == $("p.d").add("p.e")
assert $("div.a").children() == $("div.b").add("div.f")

再来看下面这段 HTML:

<div class="a">
    <p class="a"></p>
    <p class="b"></p>
    <p class="c"></p>
</div>

下面这段代码将会选中 p.b & p.c:

assert $("p").next() == $("p.b").add("p.c")

Navigator 对象上的方法 previous(),prevAll(),next(),nextAll(),parent(),parents(),closest(),siblings(),children() 还可以接受 CSS 选择器和属性匹配器作为参数。还是上面这段相同的 HTML,下面这段代码将会选中 p.c:

assert $("p").next(".c") == $("p.c")
assert $("p").next(class: "c") == $("p.c")
assert $("p").next("p", class: "c") == $("p.c")

同样的,对于下面这段 HTML:

<div class="a">
    <div class="b">
        <p></p>
    </div>
</div>

下面这段代码将会选中 div.b:

assert $("p").parent(".b") == $("div.b")
assert $("p").parent(class: "b") == $("div.b")
assert $("p").parent("div", class: "b") == $("div.b")

Navigator 对象的 closest() 方法比较特殊,它会选中当前导航器的第一个匹配给定选择器的祖先。并且 closest() 方法没有无参数的版本。例如下面这些代码将会选中 div.a:

assert $("p").closest(".a") == $("div.a")
assert $("p").closest(class: "a") == $("div.a")
assert $("p").closest("div", class: "a") == $("div.a")

所有上面的这些导航器方法都不接受索引作为参数,因为他们都会自动选中第一个匹配的页面内容。如果想选中多个元素,你可以使用 prevAll(),nextAll(),parents() 方法,他们都有无参数版本和接受选择器作为参数的版本。

Navigator 对象的 nextUntil(),prevUntil(),parentsUntil() 方法会返回路径上的所有节点,直到遇到第一个匹配给定选择器或属性的元素为止。以下面这段 HTML 为例:

<div class="a"></div>
<div class="b"></div>
<div class="c"></div>
<div class="d"></div>

下面这些代码都会选中 div.b 和 div.c:

assert $(".a").nextUntil(".d") == $("div.b").add("div.c")
assert $(".a").nextUntil(class: "d") == $("div.b").add("div.c")
assert $(".a").nextUntil("div", class: "d") == $("div.b").add("div.c")

 

4.5 点击 Clicking

        导航器对象实现了 click() 方法,该方法会指示浏览器点击该导航器对象所匹配的单个页面元素。如果在一个包含多个元素的导航器对象上调用 click(),就会抛出 SingleElementNavigatorOnlyMethodException 异常。

click() 方法还有几个变种:click(Class),click(Page),click(List),它们的功能分别类似于 Browser 对象的 page(Class<? extends Page>),page(Page),page(Class<? extends Page> [])。这样可以允许在点击发生时,同时指明页面该如何变化。例如:

$("a.login").click(LoginPage)

将会点击 a.login 元素,然后等效的调用 browser.page(LoginPage),并且验证浏览器当前的确处在给定的页面 LoginPage。

当使用 click 的 List 版本时,所有传如的 Page 对象都必须定义 at 检查器,否则将会抛出 UndefinedAtCheckerException 异常。

 

4.6 元素的可见性

        Navigator 对象有一个 displayed 属性,用于表明当前导航器所代表的元素对用户是否是可见的。没有匹配任何页面内容的导航器对象的 displayed 属性总是 false。

 

4.7 获得焦点

        可以使用 Navigator 对象的 focused 属性来判断给定的导航器对象是否包含当前获得焦点的元素。不匹配任何元素的导航器对象的 focused 属性为 false。在匹配多个元素的导航器上获取 focused 属性,将会抛出 SingleElementNavigatorOnlyMethodException 异常

 

4.8 元素位置和大小

        可以通过导航器对象的 height 和 weight 属性来获取当前导航器所代表的页面元素的大小(单位为:像素)。还可以通过 x 和 y 属性来获取元素的座标,他表示此 Navigator 对象代表的元素的左上角到当前页面(或页面 frame)左上角的距离。所有这些属性都只适用於单元素导航器,如果在多元素导航器上访问这些属性将会抛出 SingleElementNavigatorOnlyMethodException 异常。

我们来看下面这个 HTML:

<div style="height: 20px; width: 40px; position: absolute; left: 20px; top: 10px"></div>
<div style="height: 40px; width: 100px; position: absolute; left: 30px; top: 150px"></div>

下面这些条件都将成立:

assert $("div", 0).height == 20
assert $("div", 0).width == 40
assert $("div", 0).x == 20
assert $("div", 0).y == 10

如果想获取所有匹配元素的这些属性中的某个属性,可以使用 Groovy 的展开操作符:

assert $("div")*.height == [20, 40]
assert $("div")*.width == [40, 100]
assert $("div")*.x == [20, 30]
assert $("div")*.y == [10, 150]

 

4.9 访问页面元素的标签名,属性,文本和类

        在单元素导航器上调用 tag(),text(),classes() 方法可以分别获取元素的标签,文本以及类名,使用 @ 语法或 attr() 方法可以获取对应属性的值。如果在多元素导航器上调用这些方法,将会抛出 SingleElementNavigatorOnlyException 异常。classes() 方法返回元素的类名,返回值是 java.lang.List 类型的,且按照字典顺序排序。

以下面这段 HTML 为例:

<p title="a" class="a para">a</p>
<p title="b" class="b para">b</p>
<p title="c" class="c para">c</p>

下面这些断言都会通过:

assert $(".a").text() == "a"
assert $(".a").tag() == "p"
assert $(".a").@title == "a"
assert $(".a").classes() == ["a", "para"]

如果想获取导航器中匹配的所有页面元素的某个属性,可以使用 Groovy 的展开操作符:

assert $("p")*.text() == ["a", "b", "c"]
assert $("p")*.tag() == ["p", "p", "p"]
assert $("p")*.@title == ["a", "b", "c"]
assert $("p")*.classes() == [["a", "para"], ["b", "para"], ["c", "para"]]

 

4.10 CSS 属性

        可以使用导航器对象的 css() 方法来访问单元素导航器的 CSS 属性。如果在多元素导航器上调用 css() 方法将会抛出 SingleElementNavigatorOnlyMethod 异常。

来看下面这段 HTML:

<div style="float: left">text</div>

你可以像下面这样获取 css 属性 float 的值:

assert $("div").css("float") == "left"

⚠️使用导航器对象来提取 css 属性时有一些限制:颜色值应该使用 rgba 字符串返回,假设在 HTML 源码里 background-color 属性被设置成了绿色(green),返回值将是 rgba(0, 255, 0, 1)。此外为了与 DOM CSS2 标准一致,缩写形式的 CSS 属性,如:backgroundfontborderborder-topmarginmargin-toppaddingpadding-toplist-styleoutlinepausecue 等,将不会返回,所以你应该直接使用完整形式的属性名称(如:background-color)来获取想要的属性值。

 

4.11 发送按键

        假设有下面这段 HTML:

<input type="text"/>

你可以使用左移操作符 << 向 input(或者任何其他页面内容)来发送按键,这里 << 操作符其实是 WebDriver 中 sendKeys() 方法的缩写。

$("input") << "foo"
assert $("input").value() == "foo"

至于发送按键后,页面元素会如何响应,这取决于具体是什么页面元素。

不可打印字符(如:删除键,组合按键)

我们可以通过 WebDriver 的 Keys 枚举来向页面元素发送不可打印字符:

import org.openqa.selenium.Keys

$("input") << Keys.chord(Keys.CONTROL, "c")

这里我们向 input 的元素发送了 “control + c”

可以查看 Keys 的文档来获取其他可用的按键。

 

4.12 访问输入类(input)页面元素的值

        可以使用 value() 方法来获取或设置 input,select 和 textarea 等元素的值。在单元素导航器上调用无参数的 value() 方法,将会返回字符串值。如果在多元素导航器上调用该方法将会抛出 SingleElementNavigatorOnlyMethod 异常。调用带参数形式的 value(arg) 将会设置导航器对象中所以元素的 value。参数可以是任何类型,在必要时会自动将其转换成字符串。这里有些例外是:当设置 checkbox 元素的 value 时,需要传入 boolean 类型的值(或一个已存在的 checkbox 值 或 标签 label),设置多选的 select 元素时,需要传入 Collection 类型的值。

 

4.13 操作表单控件的快捷方式

        和表单控件(如,input,select 等)交互是 Web 自动化测试中极其常见的操作,Geb 对这些常见的操作提供了方便易用的快捷操作。处理表单控件时,Geb 支持下面这些快捷的操作方式,来看下面这段 HTML:

<form>
    <input type="text" name="geb" value="testing" />
</form>

可以使用类似于属性操作的语法来获取和设置 input 元素的 value:

assert $("form").geb == "testing"
$("form").geb = "goodness"
assert $("form").geb == "goodness"

上面这些写法实质上是下面这些常规写法的快捷方式,他们是等效的:

assert $("form").find("input", name: "geb").value() == "testing"
$("form").find("input", name: "geb").value("goodness")
assert $("form").find("input", name: "geb").value() == "goodness"

Geb 还提供了使用控件 name 来获取导航器的快捷方式:

assert $("form").geb() == $("form").find("input", name: "geb")

注:在上面和下面这些关于表单控件的例子中,我们都使用了这样的代码 $('form').someInput 来表示某个控件,实际上如果页面上只有一个控件的 name 为 someInput 时,我们可以直接使用 someInput 来表示这个控件。在例子中,为了表意更清晰,我们还是选择使用 $('form').someInput

如果你定义的页面内容(可能是在 page 或 module 中定义的)所代表的实际上是一个 input,select 或 textarea元素,那么你也可以像上面 form 那样,使用同样的方式来获取或设置(把他们放在赋值操作的左侧即是设置值,其他情况是获取值)他们的值。对于上面的 HTML,我们假设定义了下面这样的 page 和 module:

class ShortcutModule extends Module {
    static content = {
        geb { $('form').geb() }
    }
}

class ShortcutPage extends Page {
    static content = {
        geb { $('form').geb() }
        shortcutModule { module ShortcutModule }
    }
}

下面这些操作将能通过:

page ShortcutPage
assert geb == "testing"
geb = "goodness"
assert geb == "goodness"

下面这些也同样:

page ShortcutPage
assert shortcutModule.geb == "testing"
shortcutModule.geb = "goodness"
assert shortcutModule.geb == "goodness"

 

4.14 设置表单控件的值

        约定:下面这些例子中描述表单控件时,我们都使用 $('form').someInput 形式。假设你定义了 myContent { $('form').someInput } 这种形式的页面内容,你也可以在例子中使用 $('form').someInput 的地方使用 myContent 来替换。

注意,如果想在非 input,select 或 textarea 元素上设置 value 将会抛出 UnableToSetElementException 异常。

 

4.14.1 单选 Select 元素

        设置 Select 元素的值时,我们只要把需要选择的选项的 值或文本 赋值给该元素即可。被赋的值会别自动转换成 String。请看下面的例子:

<form>
    <select name="artist">
        <option value="1">Ima Robot</option>
        <option value="2">Edward Sharpe and the Magnetic Zeros</option>
        <option value="3">Alexander</option>
    </select>
</form>

我们可以像下面这样选中某个选项:

$("form").artist = "1"             //1
$("form").artist = 2               //2
$("form").artist = "Alexander"     //3

//1: 通过第一个选项的值来选中它

//2: 通过第二个对象的值来选中它,这里会自动把 2 转换成 String

//3: 通过第三个选项的文本来选中它

 

如果你想设置的值并不包含在该 select 元素下的几个 option 元素的 值或文本 中,就会抛出 IllegalArgumentException 异常。

 

4.14.2 多选 Select 元素

        如果页面上的 select 元素具有 multiple 属性,那么它是可多选的,我们可以给他赋值数组或待选值的集合来设置选项的选中状态,值没有在集合中的选项将不会被选中。请看下例:

<form>
    <select name="genres" multiple>
        <option value="1">Alt folk</option>
        <option value="2">Chiptunes</option>
        <option value="3">Electroclash</option>
        <option value="4">G-Funk</option>
        <option value="5">Hair metal</option>
    </select>
</form>

我们可以像下面这样来选中选项:

$("form").genres = ["2", "3"]                     //1
$("form").genres = [1, 4, 5]                      //2
$("form").genres = ["Alt folk", "Hair metal"]     //3
$("form").genres = []                             //4

//1: 通过第二和第三个选项的值来选中他们

//2: 将列表中的值转换成字符串后,通过第一,四,五个选项的值来选中他们

//3: 通过第一和第五个选中的文本来选中他们

//4: 所有选项都不选中

 

如果赋值时使用的集合中包含某个值,它既不在 select 元素下的所有选项的值中,也不在他们的文本中,那么将会抛出 IllegalArgumentException 异常。

 

4.14.3 复选框 Checkbox

        可以将 checkbox 元素的 value 设置成 true 或 false 来设置他们的选中状态,设置为 true 时选中,false 即不选中。我们来看下面的 HTML:

<form>
    <input type="checkbox" name="pet" value="dog" />
</form>

你可以像下面这样来选中它:

$("form").pet = true

在已经被勾选的 checkbox 元素上调用 value() 方法将会返回该 checkbox 的 value 属性的值:

<form>
    <input type="checkbox" name="pet" value="dog" checked/>
</form>
assert $("input", name: "pet").value() == "dog"
assert $("form").pet == "dog"

在未勾选的 checkbox 元素上调用 value() 方法将会返回 null:

<form>
    <input type="checkbox" name="pet" value="dog"/>
</form>
assert $("input", name: 'pet').value() == null
assert $("form").pet == null

一般来说,如果想检查一个 checkbox 是否被选中的话,你应该使用 Groovy Truth 来判断:

<form>
    <input type="checkbox" name="checkedByDefault" value="checkedByDefaulValue" checked/>
    <input type="checkbox" name="uncheckedByDefault" value="uncheckedByDefaulValue"/>
</form>
assert $("form").checkedByDefault
assert !$("form").uncheckedByDefault

 

4.14.4 多个复选框

        你也可用通过显式设置 checkbox 的 value 属性或使用它的标签来勾选它。当你有多个相同 name 的 checkbox 时这会很有用,例如:

<form>
    <label for="dog-checkbox">Canis familiaris</label>
    <input type="checkbox" name="pet" value="dog" id="dog-checkbox"/>
    <label for="cat-checkbox">Felis catus</label>
    <input type="checkbox" name="pet" value="cat" id="cat-checkbox" />
    <label for="lizard-checkbox">Lacerta</label>
    <input type="checkbox" name="pet" value="lizard" id="lizard-checkbox" />
</form>

你可以像下面这样选择狗作为你的宠物:

$("form").pet = "dog"
$("form").pet = "Canis familiaris"

如果你想一次勾选多个 checkbox,你可以使用列表作为值赋给它们:

$("form").pet = ["dog", "lizard"]
$("form").pet = ["Canis familiaris", "Lacerta"]

如果想检查某个 checkbox 是否被勾选,而此时有多个同名的 checkbox,那么在调用 value() 方法前,一定要确保你使用的导航器只包含你要检查的那个 checkbox:

<form>
    <input type="checkbox" name="pet" value="dog" checked/>
    <input type="checkbox" name="pet" value="cat" />
</form>
assert $("input", name: "pet", value: "dog").value()
assert !$("input", name: "pet", value: "cat").value()

 

4.14.5 单选按钮 Radio

        可以将想要选择的单选按钮的 value 值或与该按钮相关的标签文本赋值给代表单选按钮的页面元素来选中它。请看下面这段包含单选按钮的 HTML:

<form>
    <label for="site-current">Search this site</label>
    <input type="radio" id="site-current" name="site" value="current">

    <label>Search Google
        <input type="radio" name="site" value="google">
    </label>
</form>

我们可以通过 value 来选中单选按钮:

$("form").site = "current"
assert $("form").site == "current"
$("form").site = "google"
assert $("form").site == "google"

也可以通过标签文本来选中:

$("form").site = "Search this site"
assert $("form").site == "current"
$("form").site = "Search Google"
assert $("form").site == "google"

 

4.14.6 文本输入 input 和文本区域 textarea

        对 input 或 textarea 元素进行赋值,所赋的值就会变长他们的 value 属性:

<form>
    <input type="text" name="language"/>
    <input type="text" name="description"/>
</form>
$("form").language = "gro"
$("form").description = "Optionally statically typed dynamic lang"
assert $("form").language == "gro"
assert $("form").description == "Optionally statically typed dynamic lang"

也可以使用 sendKeys 方法的快捷方式 << 来追加文本:

$("form").language() << "ovy"
$("form").description() << "uage"
assert $("form").language == "groovy"
assert $("form").description == "Optionally statically typed dynamic language"

追加输入不可打印字符也是可以的:

import org.openqa.selenium.Keys

$("form").language() << Keys.BACK_SPACE
assert $("form").language == "groov"

注意:WebDriver 处理 textarea 和 周围有空白字符的输入时有些问题。有些 driver 实现会隐式的去掉开头和结尾的空白字符,你可以在这看这个问题的细节

 

4.14.7 文件上传

        目前使用 WebDriver 是无法模拟用户点击文件上传控件,然后通过文件选择器选择要上传的文件这一过程的。但是,你可以直接把文件上传控件的 value 设置成想要上传的文件的绝对路径,然后在表单提交时文件就会被上传。所以,如果你的 HTML 看起来像这样:

<input type="file" name="csvFile"/>

假设有一个叫做 uploadFile 的变量,它里面是一个 File 类型的实例,表示你要上传的文件,那么你就可以像下面这样设置文件上传控件的值:

$("form").csvFile = uploadedFile.absolutePath

 

4.15 复杂的交互

        除了简单的点击或输入文本之外,WebDriver 还支持更复杂的交互,如:拖拽。你可以直接在 Geb 中使用 WebDriver 提供的这些 API 还完成这种交互,或者可以使用更加 Geb 友好的 interact DSL 来完成这些任务。

 

4.15.1 直接使用 WebDriver 的 Actions API

        Geb 中的导航器(Navigator)对象是构建在 WebDriver 的 WebElement 对象集合之上的。可以通过下面的这些 Navigaotr 实例方法来直接访问其包含的 WebElement 对象:

WebElement singleElement()
WebElement firstElement()
WebElement lastElement()
Collection<WebElement> allElements()

可以在 WebDriver 的 Actions 类中使用这些 WebElement 实例,来模拟复杂的用户交互。首先,你需要通过 WebDriver 的实例 driver 来创建一个 Actions 类的实例:

def actions = new Actions(driver)

然后就可以使用 Actions 类中提供的方法来组装一系列用户动作,左后调用 build() 方法来创建一个具体的 Action:

WebElement someItem = $("li.clicky").firstElement()
def shiftClick = actions.keyDown(Keys.SHIFT).click(someItem).keyUp(Keys.SHIFT).build()

最后,在生成的 Action 对象上调用 perform() 方法来执行这些动作:

shiftClick.perform()

 

4.15.2 使用 interact() 来实现

        为了避免像上面一节中那样构建 Actions,获取 WebElement 然后构造 Action 最后 perform 这样复杂的过程,Geb 为此提供来 interact() 方法来简化操作。当使用 interact 方法时,Geb 会隐式的构造一个 Actions 实例,然后编排 Action,最后执行 perform。传递给 interact() 方法的闭包对象的委派(delegate)是 InteractDelegate 类的实例,该类中声明了和 Actions 类中一样的方法,只是在所以接受 WebElement 作为参数的方法中,都改为接受导航器 Navigator 作为参数。

来看如何使用 interact() 方法来实现上一小节中的复杂用户操作:

interact {
    keyDown Keys.SHIFT
    click $("li.clicky")
    keyUp Keys.SHIFT
}

请查看 InteractDelegate 类的文档来获取可以用来模拟用户操作的完整方法列表。

 

4.15.3 复杂交互举例

拖拽 Drag and drop

        可以使用 clickAndHold(),moveByOffset(),release() 方法组成的序列来完成页面上的元素拖拽动作:

interact {
    clickAndHold($('#draggable'))
    moveByOffset(150, 200)
    release()
}

也可以使用 Actions API 中提供的便捷方法 dragAndDropBy() 来实现拖拽:

interact {
    dragAndDropBy($("#draggable"), 150, 200)
}

在这里,该元素将被拖拽到向右距它 150 像素,向下距离它 200 像素的地方。

注意:目前 HTMLUnit driver 不支持模拟鼠标拖拽到任意位置,只支持拖拽到某个具体元素上。

 

按下 Control 后点击 (control-clicking)

        按下 Ctrl 键后再点击一系列元素的操作,和按下 Shif 后点击操作没有什么两样,操作都很相似:

import org.openqa.selenium.Keys

interact {
    keyDown(Keys.CONTROL)
    click($("ul.multiselect li", text: "Order 1"))
    click($("ul.multiselect li", text: "Order 2"))
    click($("ul.multiselect li", text: "Order 3"))
    keyUp(Keys.CONTROL)
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章