# 高級用戶交互
原文:<https://code.google.com/p/selenium/wiki/AdvancedUserInteractions>
##入門
高級用戶交互API提供了一個更新更完善的機制來定義并描述用戶在一個網頁上的各種操作。這些操作包括:拖拽、按住CTRL鍵選擇多個元素等等。
### 快速上手
為了生成一連串的動作,我們使用Actions來建立。首先,我們先配置操作:
Actions builder = new Actions(driver);
builder.keyDown(Keys.CONTROL)
.click(someElement)
.click(someOtherElement)
.keyUp(Keys.CONTROL);
然后,獲得操作(Action):
Action selectMultiple = builder.build();
最后,執行這個動作:
selectMultiple.perform();
這一系列的動作應該盡量的短。在使用中最好在執行一個簡短的動作后驗證頁面是否處于正確的狀態,然后再執行下面的動作。下一節將會列出所有可用的動作(Action),并且說明它們如何進行擴展。
### 鍵盤交互(Keyboard interactions)
鍵盤交互是發生在一個特定的頁面元素的,而webdriver會確保這個頁面元素在執行鍵盤動作時處于正確的狀態。這個正確的狀態,包括頁面元素滾動到可視區域并定位到這個頁面元素。
既然這個新的API是面向用戶(user-oriental)的接口,那么對于一個用戶,在對一個元素輸入文本前做顯式的交互就更加的符合邏輯。這意味著,當想定位到相鄰的頁面元素時,可能需要點擊一下元素或按下Tab(`Keys.TAB`)鍵。
The new interactions API will (first) support keyboard actions without a provided element. The additional work to focus on an element before sending it keyboard events will be added later on.
### 鼠標交互(Mouse interactions)
鼠標操作有一個上下文-鼠標的當前位置。因此,當為幾個鼠標操作設定一個上下文時,第一個操作的上下文就是元素的相對位置,下一個操作的上下文就上一個操作后的鼠標相對位置。
## 支持情況
這個針對操作以及動作生成器的API已經(絕大部分)完成。HtmlUnit和Firefox已經完全支持,Opera和IE正在支持中。
## 大綱
### 單個動作
所有的動作都實現了`Action`接口,這個接口只有一個方法:`perform()`。每個動作所需要的信息都通過Constructor傳入。當調用這個動作的時候,動作知道如何與頁面交互(如,找到活動的元素并輸入文本或者計算出在屏幕上的點擊坐標)并且調用底層實現來實現這個交互。
下面是一些動作:
- ButtonReleaseAction - 釋放鼠標左鍵
- ClickAction - 相當于 WebElement.click()
- ClickAndHoldAction - 按下鼠標左鍵并保持
- ContextClickAction - 一般就是鼠標右鍵,通常調出右鍵菜單用。
- DoubleClickAction - 雙擊某個元素
- KeyDownAction - 按下修飾鍵(SHIFT,CTRL,ALT,WIN)
- KeyUpAction - 釋放修飾鍵
- MoveMouseAction - 移動鼠標從當前位置到另外的元素.
- MoveToOffsetAction - 移動鼠標到一個元素的偏移位置(偏移可以為負,元素是鼠標剛移動到的那個元素)。
- SendKeysAction - 相當于 WebElement.sendKey(...)
`CompositeAction`包含一系列的動作,當被調用的時候,它會調用它所包含的所有動作的perform方法。通常,這個動作通常都不是直接建立的,一般是使用`ActionChainsGenerator`。
### 生成動作鏈
`Actions`鏈生成器實現了創建者模式來新建一個包含一組動作的`CompositeAction`。使用Actions生成器可以很容易的生成動作并調用`build()`方法來獲得復雜的操作。
Actions builder = new Actions(driver);
Action dragAndDrop = builder.clickAndHold(someElement)
.moveToElement(otherElement)
.release(otherElement)
.build();
dragAndDrop.perform();
有一個對`Actions`進行擴展的計劃,給`Actions`類添加一個方法,這個方法可以追加任何動作到其擁有的動作列表上。這將允許添加擴展的動作,而不用人工創建CompositeAction。關于擴展`Actions`,請往下看。
### 擴展Action接口的指導
Action接口只有一個方法-`perform()`。除了實際的交互本身,所有的條件判斷也都應該在這個這個方法里實現。在動作創建和動作實際執行這段時間內,很可能頁面的狀態已經發生了變化,比如元素的可視情況已經坐標已經不能找到了。
## 實現細節
為了達到每個操作的執行與具體實現的分離,所有的動作都依賴2個接口:`Mouse`和`Keyboard`。這些接口被所有支持高級用戶接口API的driver實現了。需要注意的是,這些接口是為了讓動作使用的,而不是最終用戶。本節的信息,主要是針對想擴展WebDriver的開發者的。
### 一字警告
`Keyboard`和`Mouse`接口是設計用來支持各種Action類的。有鑒于此,它們的API沒有Actions鏈生成器API穩定。直接使用這些接口可能達不到期望的結果,因為Action類做了額外的工作來確保在實際事件觸發時處于正確的環境條件。這些準備工作包括定位在正確的元素上或者鼠標交互前保證元素是可見的。
### Keyboard接口
Keyboard接口包含3個方法:
- void sendKeys(CharSequence... keysToSend) - 與 sendKeys(...)相似.
- void pressKey(Keys keyToPress) - 按下一個鍵并保持。鍵僅限于修飾鍵(Control, Alt and Shift).
- void releaseKey(Keys keyToRelease) - 釋放修飾鍵.
至于如何在調用之間保存修飾鍵的狀態是Keyboard接口實現類的職責。只有活躍的元素才會接收到這些事件。
### Mouse接口
`Mouse`接口包含以下方法(有可能不久之后會有變化):
- void click(WebElement onElement) - 同click()方法一樣.
- void doubleClick(WebElement onElement) - 雙擊一個元素.
- void mouseDown(WebElement onElement) - 在一個元素上按下左鍵并保持
Action selectMultiple = builder.build();
- void mouseUp(WebElement onElement) - 在一個元素上釋放左鍵.
- void mouseMove(WebElement toElement) - 從當前位置移動到一個元素
- void mouseMove(WebElement toElement, long xOffset, long yOffset) - 從當前位置移動到一個元素的偏移坐標
- void contextClick(WebElement onElement) - 在一個元素上做一個右鍵操作
### Native events(原生事件) VS synthetic events(合成事件)
WebDriver提供的高級用戶接口,要么是直接模擬的Javascript事件(即合成事件),要么就是讓瀏覽器生成Javascript事件(即原生事件)。原生事件能更好的模擬用戶交互,而合成事件則是平臺獨立的,這使得使用了替代的窗口管理器的linux系統顯得尤其重要,具體參加[native events on Linux](https://code.google.com/p/selenium/wiki/NativeEventsOnLinux)。原生事件無論什么時候總是應該盡可能的使用。
下面的表格展示了瀏覽器對事件的支持情況。
<table border="1px">
<tr>
<td>瀏覽器</td><td>操作系統</td><td>原生事件</td><td>合成事件</td>
</tr>
<tr>
<td>Firefox</td><td>Linux</td><td>支持</td><td>支持(默認)</td>
</tr>
<tr>
<td>Firefox</td><td>Windows</td><td>支持(默認)</td><td>支持</td>
</tr>
<tr>
<td>Internet Explorer</td><td>Windows</td><td>支持(默認)</td><td>不支持</td>
</tr>
<tr>
<td>Chrome</td><td>Linux/Windows</td><td>支持*</td><td>不支持</td>
</tr>
<tr>
<td>Opera</td><td>Linux/Windows</td><td>支持(默認)</td><td>不支持</td>
</tr>
<tr>
<td>HtmlUnit</td><td>Linux/Windows</td><td>支持(默認)</td><td>不支持</td>
</tr>
</table>
ChromeDriver提供了2種模式來支持原生事件:Webkit事件和原始事件。其中Webkit事件是使用Webkit函數來觸發的Javascript事件,而原始事件模式則使用的是操作系統級別的事件。
FirefoxDriver中,原生事件可以使用FirefoxProfile來進行開關控制。
FirefoxProfile profile = new FirefoxProfile();
profile.setEnableNativeEvents(true);
FirefoxDriver driver = new FirefoxDriver(profile);
#### 例子
以下是原生事件與合成事件表現不同的一些例子:
- 使用合成事件,點擊隱藏在其他元素下面的元素是可以的。使用原生事件,瀏覽器會將點擊事件作用在所給坐標最外層的元素上,就像是用戶點擊在特定的位置一樣。
- 當一個用戶,按下TAB鍵希望焦點從當前元素定位到下一個元素,瀏覽器是可以做到的。使用合成事件的話,瀏覽器并不知道TAB鍵被按下了,因此也不會改變焦點。而使用原生事件,瀏覽器則會表現正確。