# 第一章 Selenium 概述
## 1.1.Selenium 發展史
Selenium是一系列基於Web的自動化工具,提供一套測試函式,用於支援Web自動化測試。函式非常靈活,能夠完成介面元素定位、視窗跳轉、結果比較。具有如下特點:
- 多瀏覽器支援
- 如IE、Firefox、Safari、Chrome、Android手機瀏覽器等。
- 支援多語言
- 如Java、C#、Python、Ruby、PHP等。
- 支援多作業系統
- 如Windows、Linux、IOS、Android等。
- 開源免費
- 官網:http://www.seleniumhg.org/
Selenium框架由多個工具組成,包括:Selenium IDE,Selenium RC,Selenium WebDriver和SeleniumRC。
發展到如今 Selenium 已經發布到了 3.0 版本了,以下是簡單總結髮展過程以及一些變化:
- **Selenium 1.0**
Jason Huggins在2004年發起的 Selenium 專案,使用 JavaScript 編寫的一個類庫,這個 JavaScript 類庫就是Selenium core,同時也是seleniumRC、Selenium IDE的核心元件。Selenium由此誕生。

- **Selenium 2.0**
因為Selenium和Webdriver的合併,所以,Selenium 2.0由此誕生。
簡單用公式表示為:Selenium 2.0 = Selenium 1.0 + WebDriver
需要強調的是,在Selenium 2.0中主推的是WebDriver,可以將其看作Selenium RC的替代品。因為Selenium為了保持向下的相容性,所以在Selenium 2.0中並沒有徹底地拋棄Selenium RC。
所以,我們在學習Selenium2.0的時候,核心是學習WebDriver。它的工作原理是這樣的:

- **Selenium 3.0**
Selenium 3.0做了一些不大不小的更新:
- 1、終於去掉了RC,簡單用公式表示為:Selenium 3.0 = Selenium 2.0 - Selenium RC(Remote Control)
- 2、Selenium3.0只支援Java8版本以上。
- 3、Selenium3.0中的Firefox瀏覽器驅動獨立了,以前裝完selenium2就可以驅動Firefox瀏覽器了,現在和Chrome一樣,必須下載和設定瀏覽器驅動。
- 4、MAC OS 整合Safari的瀏覽器驅動。預設在/usr/bin/safaridriver 目錄下。
- 5、只支援IE 9.0版本以上。
## 1.2.Selenium WebDriver原理
將 WebDriver 驅動瀏覽器類比成開出租車的場景。
在開出租車時有三個角色:
- **乘客:**他/她告訴計程車司機去哪裡,大概怎麼走。
- **計程車司機:**他按照乘客的要求來操控計程車。
- **計程車:**計程車按照司機的操控完成真正的行駛,把乘客送到目的地。

在WebDriver中也有類似的三個角色:
- **工程師寫的自動化測試程式碼**:自動化測試程式碼傳送請求給瀏覽器的驅動(比如火狐驅動、谷歌驅動)
- **瀏覽器的驅動**:它來解析這些自動化測試的程式碼,解析後把它們傳送給瀏覽器
- **瀏覽器**:執行瀏覽器驅動發來的指令,並最終完成工程師想要的操作。

# 第二章 Selenium 環境搭建
## 2.1.java 環境
想要通過 java 語言來使用 selenium 框架,前提要完成 jdk 環境的安裝。
詳細安裝教程見:https://blog.csdn.net/shengmer/article/details/78836255
## 2.2.selenium 環境
**簡單 java 工程:**
直接匯入 selenium的 jar 包就可以了。
jar包下載地址:[ Selenium各個版本jar下載](https://npm.taobao.org/mirrors/selenium) , 下載對應的版本即可
**maven工程:**
在pom檔案中引入對應的依賴即可:
maven倉庫:https://mvnrepository.com/
```xml
org.seleniumhq.selenium
selenium-java
3.4.0
```
## 2.3.selenium3 對應瀏覽器驅動下載
當selenium升級到3.0之後,對不同的瀏覽器驅動進行了規範。如果想使用selenium驅動不同的瀏覽器,必須單獨下載並設定不同的瀏覽器驅動。
各瀏覽器下載地址:
- Firefox瀏覽器驅動:[geckodriver](https://github.com/mozilla/geckodriver/releases)
- Chrome瀏覽器驅動:[chromedriver](https://sites.google.com/a/chromium.org/chromedriver/home)[taobao備用地址](https://npm.taobao.org/mirrors/chromedriver)
- IE瀏覽器驅動:[IEDriverServer](http://selenium-release.storage.googleapis.com/index.html)
- Edge瀏覽器驅動:[MicrosoftWebDriver](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/)
- Opera瀏覽器驅動:[operadriver](https://github.com/operasoftware/operachromiumdriver/releases)
- PhantomJS瀏覽器驅動:[phantomjs](http://phantomjs.org/)
注:部分瀏覽器驅動地址需要梯子。
**設定瀏覽器驅動**
設定瀏覽器的地址非常簡單。 我們可以手動建立一個存放瀏覽器驅動的目錄,如: C:\driver , 將下載的瀏覽器驅動檔案(例如:chromedriver、geckodriver)丟到該目錄下。
我的電腦–>屬性–>系統設定–>高階–>環境變數–>系統變數–>Path,將“C:\driver”目錄新增到Path的值中。
**驗證瀏覽器驅動**
驗證不同的瀏覽器驅動是否正常使用。
```java
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.opera.OperaDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriver;
WebDriver driver = new ChromeDriver(); //Chrome瀏覽器
WebDriver driver = new FirefoxDriver(); //Firefox瀏覽器
WebDriver driver = new EdgeDriver(); //Edge瀏覽器
WebDriver driver = new InternetExplorerDriver(); // Internet Explorer瀏覽器
WebDriver driver = new OperaDriver(); //Opera瀏覽器
WebDriver driver = new PhantomJSDriver(); //PhantomJS
```
# 第三章 Selenium 簡單示例
- **開啟百度進行搜尋:**
```java
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
/**
* @Description: 通過selenium操作瀏覽器開啟百度進行搜尋
* selenium版本:3.12.0; 通過maven管理jar包
* 開發工具:IDEA
* jdk:1.8
* 瀏覽器:chrome 75+
* @Author: ggf
* @Date: 2020/03/22
*/
public class BaiduSearch {
public static void main(String[] args) {
// 1.建立webdriver驅動
WebDriver driver = new ChromeDriver();
// 2.開啟百度首頁
driver.get("https://www.baidu.com");
// 3.獲取輸入框,輸入selenium
driver.findElement(By.id("kw")).sendKeys("selenium");
// 4.獲取“百度一下”按鈕,進行搜尋
driver.findElement(By.id("su")).click();
// 5.退出瀏覽器
driver.quit();
}
}
```
# 第四章 八大元素定位
- **為什麼要進行元素定位?**
我們必須告訴 selenium 怎麼去定位元素,用來模擬使用者的動作,或者檢視元素的屬性和狀態,以便於我們可以執行檢查。例如:我們要搜尋一個產品,首先要找到搜尋框與搜尋按鈕,接著通過鍵盤輸入要查詢的關鍵字,最後滑鼠單擊搜尋按鈕,提交搜尋請求。
正如上述的人工操作步驟一樣,我們也希望 selenium 能模擬這樣的動作,然而,selenium 並不能理解類似在搜尋框中輸入關鍵字或者點選搜尋按鈕這樣的圖形化的操作。所以需要我們程式化的告訴 selenium 如何定位搜尋框和搜尋按鈕,從而模擬鍵盤和滑鼠的操作。
## 4.1.定位方式
**selenium 提供了8種的定位方式:**
- id
- name
- class name
- tag name
- link text
- partial link text
- xpath
- css selector
這8種定位方式在java selenium 中對應的方法為:
| 方法 | 描述 | 引數 | 示例 |
| --------------------------------- | --------------------------------------------- | --------------- | ------------------------------------------ |
| findElement(By.id()) | 通過元素的 id 屬性值來定位元素 | 對應的id屬性值 | findElement(By.id("kw")) |
| findElement(By.name()) | 通過元素的 name 屬性值來定位元素 | 對應的name值 | findElement(By.name("user")) |
| findElement(By.className()) | 通過元素的 class 名來定位元素 | 對應的class類名 | findElement(By.className("passworld")) |
| findElement(By.tagName()) | 通過元素的 tag 標籤名來定位元素 | 對應的標籤名 | findElement(By.tagName("input")) |
| findElement(By.linkText()) | 通過元素標籤對之間的文字資訊來定位元素 | 文字內容 | findElement(By.linkText("登入")) |
| findElement(By.partialLinkText()) | 通過元素標籤對之間的部分文字資訊來定位元素 | 部分文字內容 | findElement(By.partialLinkText("百度")) |
| findElement(By.xpath()) | 通過xpath語法來定位元素 | xpath表示式 | findElement(By.xpath("//input[@id='kw']")) |
| findElement(By.cssSelector()) | 通過css選擇器來定位元素 | css元素選擇器 | findElement(By.cssSelector("#kw")) |
同時這8種方法都對應有著返回複數元素的方法,分別在呼叫的方法findElements(By.id()) 加上一個s:
- findElements(By.id())
- findElements(By.name())
- findElements(By.className())
- findElements(By.tagName())
- findElements(By.linkText())
- findElements(By.partialLinkText())
- findElements(By.xpath())
- findElements(By.cssSelector())
## 4.2.定位方式的用法
假如我們有一個Web頁面,通過前端工具(如,Firebug)檢視到一個元素的屬性是這樣的。
```html
按鈕
```
我們的目的是要定位input標籤的輸入框。
- 通過id定位:
```java
driver.findElement(By.id("kw"))
```
- 通過name定位:
```java
driver.findElement(By.name("wd"))
```
- 通過class name定位:
```java
driver.findElement(By.className("s_ipt"))
```
- 通過tag name定位:
```java
driver.findElement(By.tagName("input"))
```
- 通過xpath定位,xpath定位有N種寫法,這裡列幾個常用寫法:
```java
driver.findElement(By.xpath("//*[@id='kw']")) // id定位
driver.findElement(By.xpath("//*[@name='wd']")) // 屬性值定位
driver.findElement(By.xpath("//span[text()='按鈕']")) // 文字定位
driver.findElement(By.xpath("//input[@class='s_ipt']")) // class屬性定位
driver.findElement(By.xpath("/html/body/form/span/input")) // 絕對路徑定位
driver.findElement(By.xpath("//span[@class='soutu-btn']/input")) // 相對路徑定位
driver.findElement(By.xpath("//form[@id='form']/span/input"))
driver.findElement(By.xpath("//input[@id='kw' and @name='wd']")) // 多組合屬性定位
driver.findElement(By.xpath("//span[contains(text(),'按鈕')]")) // 是否包含文字
```
- 通過css定位,css定位有N種寫法,這裡列幾個常用寫法:
```java
driver.findElement(By.cssSelector("#kw") // id定位
driver.findElement(By.cssSelector("[name=wd]") // name屬性值定位
driver.findElement(By.cssSelector(".s_ipt") // class地位
driver.findElement(By.cssSelector("html > body > form > span > input") // css層級定位
driver.findElement(By.cssSelector("span.soutu-btn> input#kw")
driver.findElement(By.cssSelector("form#form > span > input")
```
接下來,我們的頁面上有一組文字連結。
```HTML
新聞
hao123
```
- 通過linkText定位:
```java
driver.findElement(By.linkText("新聞")
driver.findElement(By.linkText("hao123")
```
- 通過 partialLinkText 定位:
```java
driver.findElement(By.partialLinkText("新")
driver.findElement(By.partialLinkText("hao")
driver.findElement(By.partialLinkText("123")
```
## 4.3.xpath進階-軸定位
- parent::div 上層父節點,你那叫div的親生爸爸,最多有一個;
- child::div 下層所有子節點,你的所有親兒子中叫div的;
- ancestor::div 上面所有直系節點,是你親生爸爸或者你親爹或者你親爹的爸爸中叫div的;
- descendant::div 下面所有節點,你的後代中叫div的,不包括你弟弟的後代;
- following::div 自你以下頁面中所有節點叫div的;
- following-sibling::div 同層下節點,你所有的親弟弟中叫div的;
- preceding::div 同層上節點,你所有的親哥哥以及他們的後代中叫div的;
- preceding-sibling::div 同層上節點,你所有的親哥哥中叫div的;
詳細操作可參考:https://www.cnblogs.com/wangyi0419/p/11638652.html
關於 xpaht 和 css 的定位比較複雜,請參考: [xpath語法](http://www.w3school.com.cn/xpath/xpath_syntax.asp)、 [css選擇器](http://www.w3school.com.cn/cssref/css_selectors.asp)
# 第五章 Selenium API
## 5.1.WebDriver 常用 API
WebDriver 提供了一系列的 API 來和瀏覽器進行互動,如下:
| 方法 | 描述 |
| ------------------ | --------------------------- |
| get(String url) | 訪問目標 url 地址,開啟網頁 |
| getCurrentUrl() | 獲取當前頁面 url 地址 |
| getTitle() | 獲取頁面標題 |
| getPageSource() | 獲取頁面原始碼 |
| close() | 關閉瀏覽器當前開啟的視窗 |
| quit() | 關閉瀏覽器所有的視窗 |
| findElement(by) | 查詢單個元素 |
| findElements(by) | 查到元素列表,返回一個集合 |
| getWindowHandle() | 獲取當前視窗控制代碼 |
| getWindowHandles() | 獲取所有視窗的控制代碼 |
## 5.2.WebElement 常用 API
通過 WebElement 實現與網站頁面上元素的互動,這些元素包含文字框、文字域、按鈕、單選框、div等,WebElement提供了一系列的方法對這些元素進行操作:
| 方法 | 描述 |
| --------------------------- | ------------------------------------------ |
| click() | 對元素進行點選 |
| clear() | 清空內容(如文字框內容) |
| sendKeys(...) | 寫入內容與模擬按鍵操作 |
| isDisplayed() | 元素是否可見(true:可見,false:不可見) |
| isEnabled() | 元素是否啟用 |
| isSelected() | 元素是否已選擇 |
| getTagName() | 獲取元素標籤名 |
| getAttribute(attributeName) | 獲取元素對應的屬性值 |
| getText() | 獲取元素文字值(元素可見狀態下才能獲取到) |
| submit() | 表單提交 |
#### 5.2.1.程式碼示例
```java
public class BaiduSearch {
public static void main(String[] args) {
// 1.建立webdriver驅動
WebDriver driver = new ChromeDriver();
// 2.開啟百度首頁
driver.get("https://www.baidu.com");
// 獲取搜尋框元素
WebElement inputElem = driver.findElement(By.id("kw"));
// clear()方法,清空輸入框內容
inputElem.clear();
// sendKeys()方法,在搜尋框中輸入搜尋內容
inputElem.sendKeys("selenium");
// 元素是否顯示
boolean displayed = inputElem.isDisplayed();
System.out.println(displayed); // 輸出true
// 元素是否啟用
boolean enabled = inputElem.isEnabled();
System.out.println(enabled); // 輸出true
// 判斷元素是否被選中狀態,一般用在Radio(單選),Checkbox(多選),Select(下拉選)
// 在輸入框中使用無意義
boolean selected = inputElem.isSelected();
System.out.println(selected); // 輸出fasle
// 獲取標籤名
String tagName = inputElem.getTagName();
System.out.println(tagName); // 輸出input
// 獲取屬性名(name屬性)
String name = inputElem.getAttribute("name");
System.out.println(name); // 輸出wd
// 獲取文字值
String text = inputElem.getText();
System.out.println(text); // 輸出selenium
// 通過submit提交
driver.findElement(By.id("su")).submit();
// click()方法,點選百度一下按鈕
driver.findElement(By.id("su")).click();
// 退出瀏覽器
driver.quit();
}
}
```
# 第六章 元素等待機制
在對元素進行定位時,有時候網頁載入時間比較長,元素還沒有加載出來,這個時候去查詢這個元素的話程式中就會丟擲異常,所以我們在編寫程式碼時需要考慮延時問題,在selenium中有幾種延時機制可以使用如下:
## 6.1.硬性等待
硬性等待就是不管你瀏覽器元素是否載入完成,都要進行等待設定好的時間,利用 java 語言中的執行緒類 Thread 中的 sleep 方法,進行強制等待。
`Thread.sleep(long millis) 該方法會讓執行緒進行休眠。`
如:Thread.sleep(3000) 表示程式執行的執行緒暫停 3 秒鐘。
這種方法在一定的程度上是可以解決元素載入過慢的情況,但是不建議使用該方法,因為一般情況下我們無法判斷網頁到底需要多長時間載入完成,如果我們設定的時間過長,非常影響效率。
## 6.2.隱式等待
隱式等待的理解,就是我們通過程式碼設定一個等待時間,如果在這個等待時間內,網頁載入完成後就執行下一步,否則一直等待到時間截止。
程式碼表示:
`driver.manage.timeouts.implicitlyWait(long time, TimeUtil unit);`
這種方法相對於硬性等待顯的會靈活一點,但是隱式等待也有個弊端,因為這個設定是全域性的,程式需要等待整個頁面載入完成,直到超時,有時候我需要找的那個元素早就載入完成了,只是頁面上有個別其他元素載入比較慢,程式還是會一直等待下去。直到所有的元素載入完成在執行下一步。
## 6.3.顯式等待
顯示等待是等待指定元素設定的等待時間,在設定時間內,預設每隔0.5s檢測一次當前的頁面這個元素是否存在,如果在規定的時間內找到了元素則執行相關操作,如果超過設定時間檢測不到則丟擲異常。預設丟擲異常為:NoSuchElementException。推薦使用顯示等待。
程式碼表示:
```java
WebDriberWait wait = new WebDriverWait(dirver, timeOutInSeconds);
wait.nutil(expectCondition);
```
具體使用案例:
1.查詢元素是否已經加載出來
```java
WebDriverWait wait = new WebDriverWait(driver, 5);
// 查詢id為“kw"的元素是否加載出來了(已經在頁面DOM中存在)
wait.until(ExpectedConditions.presenceOfElementLocated(By.id("kw")));
// 在設定時間內找到後就返回,超時直接拋異常
```
2.查詢元素是否可見
```java
WebDriverWait wait = new WebDriverWait(driver, 5);
// 查詢id為"kw"的元素是否可見
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("kw")));
```
3.查詢元素是否可點選
```java
WebDriverWait wait = new WebDriverWait(driver, 5);
// 查詢id為"kw"的元素是否可以點選
wait.until(ExpectedConditions.elementToBeClickable(By.id("kw")));
```
4.自定義方法,重寫ExpectedCondition中的apply方法
```java
/*
自定義查詢元素的方法,對元素查詢方法進行二次封裝,更加的靈活,可以加上自己邏輯。
*/
public WebElement getElement(long timeOutInSecond, By by) {
WebDriverWait wait = new WebDriverWait(driver, timeOutInSecond);
WebElement element = wait.until(new ExpectedCondition
() {
@NullableDecl
@Override
public WebElement apply(@NullableDecl WebDriver webDriver) {
return webDriver.findElement(by);
}
});
return element;
}
```
#### 6.3.1.ExpectedConditions類中常用方法
| 方法 | 描述 |
| ------------------------------------------------------ | ------------------------------------------------------------ |
| presenceOfElementLocated(By locator) | 判斷某個元素是否被加到了dom樹裡,並不代表該元素一定可見; |
| visibilityOfElementLocated(By locator) | 判斷某個元素是否可見(代表元素非隱藏,元素的寬和高都不等於0); |
| elementToBeClickable(By locator) | 判斷某個元素中是否可見並且是enable的且可點選; |
| elementToBeSelected(By locator) | 判斷某個元素是否被選中了,一般用在下拉列表; |
| alertIsPresent() | 判斷頁面上是否存在alert; |
| titleIs(String title) | 判斷當前頁面的title是否精確等於預期; |
| titleContains(String title) | 判斷當前頁面的title是否包含預期字串; |
| textToBePresentInElement(By locator, String text) | 判斷某個元素中的text是否包含了預期的字串; |
| textToBePresentInElementValue(By locator, String text) | 判斷某個元素中的value屬性是否包含了預期的字串; |
| invisibilityOfElementLocated(By locator) | 判斷某個元素中是否不存在於dom樹或不可見; |
| frameToBeAvailableAndSwitchToIt(By) | 判斷iframe可用,並且切換到iframe中 |
## 6.4.頁面載入超時設定
通過TimeOuts 物件進行全域性頁面載入超時的設定,該設定必須放置get 方法之前。如下程式碼:
```java
driver.manage().timeouts().pageLoadTimeout(5, TimeUnit.SECONDS);
driver.get("https://www.baidu.com");
```
如果百度首頁在超過5秒鐘沒有載入完畢,程式就會丟擲異常,如果在 2秒就載入完了,就直接往下執行,如果需要對頁面載入時間有要求的,可以用這個設定進行檢驗。
# 第七章 特殊元素操作
## 7.1.彈出框處理(alert、confirm)
操作alert、confirm彈出框,可以通過Alert 物件來進行操作,Alert類包含了確認、取消、輸入和獲取彈出窗內容。
Alert對應屬性和方法:
| 方法 | 描述 |
| ------------------------ | ------------------------------------ |
| Alert.getText() | 獲取彈出框內容。 |
| Alert.accept() | 接受彈窗的提示,相當於點選確認按鈕。 |
| Alert.dismiss() | 取消提示窗。 |
| Alert.sendKeys(String s) | 給彈窗輸入內容。 |
簡單使用示例:
```java
// 首先需要切換到彈出框中,獲取Alert物件。
Alert alert = driver.switchTo().alert();
// 獲取彈窗文字內容
alert.getText();
// 點選確定按鈕
alert.accept();
// 點選取消按鈕
alert.dismiss();
```
注:如果彈出框不是 js 原生的 alert 彈窗,我們還是按照原來的獲取元素的方法。
## 7.2.iframe 切換
有時候我們定位元素的時候,發現怎麼都定位不了。 這時候你需要查一查你要定位的元素是否在iframe裡面。
**什麼是iframe?**
iframe 就是HTML 中,用於網頁巢狀網頁的。 一個網頁可以巢狀到另一個網頁中,可以巢狀很多層。
例如:
**main.html**
```html
FrameTest
this is main page's div!
```
**frame.html**
```html
this is a frame!
this is iframes div,
```
使用selenium 操作瀏覽器時,如果需要操作iframe中的元素,首先需要切換到對應的內聯框架中。
selenium 給我們提供了三個過載的方法,進行操作iframe;
**切換方法:**
```java
// 方法一:通過 iframe的索引值,在頁面中的位置
driver.switchTo().frame(index);
// 方法二:通過 iframe 的name 或者id
driver.switchTo().frame(nameOrId);
// 方法三:通過iframe 對應的webElement
driver.switchTo().frame(frameElement);
```
**selenium 程式碼:**
```java
public static void testIframe(WebDriver driver){
// 在 主視窗的時候
driver.findElement(By.id("maininput")).sendKeys("main input");
// 此時 沒有進入到iframe, 以下語句會報錯
//driver.findElement(By.id("iframeinput")).sendKeys("iframe input");
driver.switchTo().frame("frameA");
driver.findElement(By.id("iframeinput")).sendKeys("iframe input");
// 此時沒有在主視窗,下面語句會報錯
//driver.findElement(By.id("maininput")).sendKeys("main input");
// 回到主視窗
driver.switchTo().defaultContent();
driver.findElement(By.id("maininput")).sendKeys("main input");
}
```
注:如果已經切換進入了其中的一個 iframe 中,再想對 iframe 外的元素進行操作,需要切換回到預設的頁面中,否則會找不到元素。
```java
// 切換到預設內容頁面
driver.switchTo().defaultContent();
```
## 7.3.瀏覽器視窗的切換
有時候後在操作瀏覽器,可能打開了一個新的視窗,這個時候如果要對新視窗的元素進行操作,需要切換到新視窗中去,怎麼去切換呢?在 selenium 中有個叫**控制代碼**的概念。
什麼是**控制代碼**,簡單理解就是瀏覽器視窗的一個標識,瀏覽器開啟的每個視窗都有唯一的一個標識,也就是控制代碼,我們可以通過控制代碼來進行視窗之間的切換,從而來達到我們操作不同視窗的元素。
WebDriver 中提供了兩個 API 來獲取視窗的相關控制代碼:
```java
// 獲取當前視窗的控制代碼
String handle = driver.getWindowHandle();
// 獲取所有視窗的控制代碼,返回一個集合
Set handles = driver.getWindowHandles();
```
獲取到控制代碼後,通過對應的方法進行切換:
```java
// 切換到視窗
driver.switchTo.windwo(String handle);
```
多視窗之間的切換方法:
```java
/**
* 切換視窗的方法
* 通過傳入一個標題來找到我們需要的視窗。
* @param title 視窗的標題
*/
public void switchWindow(String title){
Set handles = driver.getWindowHandles();
// 切換視窗的方式--迴圈遍歷handles集合
for (String handle : handles) {
//判斷是哪一個頁面的控制代碼??--根據什麼來判斷???title
if(driver.getTitle().equals(title)){
break;
}else{
//切換視窗--根據視窗標識來切換
driver.switchTo().window(handle);
}
}
```
## 7.4.select 下拉框處理
如果一個頁面元素是一個下拉框(select),對應下拉框的操作,selenium有專門的類 Select 進行處理。其中包含了單選和多選下拉框的各種操作,如獲得所有的選項、選擇某一項、取消選中某一項、是否是多選下拉框等。
**Select類常用的一些方法:**
| 方法 | 說明 |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| void [deselectAll](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/Select.html#deselectAll--)() | 取消所有選擇項,僅對下拉框的多選模式有效,若下拉不支援多選模式,則會丟擲異常 UnsupportedOperationException(不支援的操作) |
| void [deselectByIndex](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/Select.html#deselectByIndex-int-)(int index) | 取消指定index的選擇,index從零開始,僅對多選模式有效,否則丟擲異常 UnsupportedOperationException(不支援的操作) |
| void [deselectByValue](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/Select.html#deselectByValue-java.lang.String-)(String value) | 取消Select標籤中,value為指定值的選擇,僅對多選模式有效,否則丟擲異常 UnsupportedOperationException(不支援的操作) |
| void [deselectByVisibleText](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/Select.html#deselectByVisibleText-java.lang.String-)(String Text) | 取消項的文字為指定值的項,例如指定值為Bar,項的html為 ,僅對多選模式有效,單選模式無效,但不會丟擲異常 |
| List`getAllSelectedOptions()` | 獲得所有選中項,單選多選模式均有效,但沒有一個被選中時,返回空列表,不會丟擲異常 |
| WebElement `getFirstSelectedOption()` | 獲得第一個被選中的項,單選多選模式均有效,當多選模式下,沒有一個被選中時,會丟擲[NoSuchElementException](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/NoSuchElementException.html)異常 |
| List`getOptions()` | 獲得下拉框的所有項,單選多選模式均有效,當下拉框沒有任何項時,返回空列表,不會丟擲異常 |
| boolean `isMultiple()` | 判斷下拉框是否多選模式 |
| void [selectByIndex](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/Select.html#selectByIndex-int-)(int index) | 選中指定index的項,單選多選均有效,當index超出範圍時,丟擲[NoSuchElementException](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/NoSuchElementException.html)異常 |
| void [selectByValue](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/Select.html#selectByValue-java.lang.String-)(String value) | 選中所有Select標籤中,value為指定值的所有項,單選多選均有效,當沒有適合的項時,丟擲[NoSuchElementException](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/NoSuchElementException.html)異常 |
| void [selectByVisibleText](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/Select.html#selectByVisibleText-java.lang.String-)(String text) | 選中所有項的文字為指定值的項,與deselectByValue相反,但單選多選模式均有效,當沒有適合的項時,丟擲[NoSuchElementException](http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/NoSuchElementException.html)異常 |
**示例:2345網址導航首頁的城市省份切換。**
1.進入2345.com首頁,點選頭部【切換】進行城市切換,我們切換省份為北京。

2.HTML頁面DOM結構。

3.程式碼編寫,這裡需要注意下拉選是在一個iframe中,需要先切換到這個iframe後再操作。
```java
// 建立驅動
WebDriver driver = new ChromeDriver();
// 開啟2345網站
driver.get("https://www.2345.com");
// 切換城市
driver.findElement(By.linkText("切換")).click();
// 切換到iframe內聯框架中
driver.switchTo().frame("city_set_ifr");
// 定位到省份下拉框
WebElement province = driver.findElement(By.id("province"));
province.click();
// 建立Select物件
Select select = new Select(province);
// 根據文字來獲取下拉值
select.selectByVisibleText("B 北京");
driver.quit();
```
## 7.5.帶 readonly 屬性的元素操作
標籤元素如果帶有 readonly 屬性,表示只讀不能進行編輯,如果我們需要操作這樣的元素,需要把這個 readonly 屬性進行移除後,再進行操作。刪除標籤屬性的話,webdriver 沒有對應的 API,我們使用 JavaScript 指令碼來進行操作。
**示例:12306 網站購票頁面日期。**

selenium 程式碼實現:
```java
// 建立驅動
WebDriver driver = new ChromeDriver();
// 開啟12306網站
driver.get("https://www.12306.cn/index/");
// 通過js來移除readonly屬性
String removeAttr = "document.getElementById('train_date').removeAttribute('readonly');";
// 執行js
((JavascriptExecutor)driver).executeScript(removeAttr);
// 獲取日期日曆輸入框
WebElement train_date = driver.findElement(By.id("train_date"));
// 清除原來的值
train_date.clear();
// 輸入內容
train_date.sendKeys("2020-03-30");
driver.quit();
```
## 7.6.日期控制元件操作
對於頁面中出現時間控制元件選擇時,一般分為兩種:
(1)控制元件沒有限制手動填寫的,我們直接使用 sendKeys() 方法進行賦值即可。
```java
driver.findElement(By).sendKeys("2020-03-30");
```
(2)控制元件限制了手動輸入的,只能通過點選控制元件時間進行輸入的,我們就需要使用 js 指令碼進行操作了。
```java
// 獲取js執行器
JavaScriptExecutor js = (JavaScriptExecutor)driver;
// 對時間輸入框進入賦值
String script = "document.getElementById('xxx').value='2020-03-30';";
// 執行
js.executeScript(script);
```
注:需要注意的是,不管使用哪種方式進行時間的賦值,一點要注意輸入時間的格式是否符合系統的要求;
## 7.7.檔案上傳
對於通過input標籤實現的上傳功能,可以將其看作是一個輸入框,即通過sendKeys()指定本地檔案路徑的方式實現檔案上傳。
建立upfile.html檔案,程式碼如下:
```html
upload_file