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)
}

 

 

 

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