# Waits 自動測試
###### tags: `Shannon`,`Test`
# 未知
- out-of-process library
# 介紹
通常WebDriver會被說有一個會blocking的API,`waits`會指示browser要做甚麼,他是一個out-of-process的library,WebDriver不會去追蹤動作或是目前的DOM狀態
舉個例子
> document
```typescript
<!doctype html>
<meta charset=utf-8>
<title>Race Condition Example</title>
<script>
var initialised = false;
window.addEventListener("load", function() {
var newElement = document.createElement("p");
newElement.textContent = "Hello from JavaScript!";
document.body.appendChild(newElement);
initialised = true;
});
</script>
```
> WebDriver
```typescript
driver.get("file:///race_condition.html");
WebElement element = driver.findElement(By.tagName("p"));
assertEquals(element.getText(), "Hello from JavaScript!");
```
WebDriver是看document.readyState是否是complete,上面的例子`<p></p>`會因為document已經loading進去了(driver.get)所以來不及加進去,這時候webdriver會就混亂找不到p,因此我們應該要等待他載完,再來呼叫Driver去找p,有幾種方法。
## Explicit wait 明確等待
你會設定條件,直到條件符合之前,它會讓你的程序暫停或定節。但如果一直沒有符合條件,直到超過的規定時間,他就會傳回錯誤。你可以等待條件發生後再執行,因此很適合用在DOM和WebDriver之間的同步
```typescript
WebDriver driver = new ChromeDriver();
driver.get("https://google.com/ncr");
driver.findElement(By.name("q")).sendKeys("cheese" + Keys.ENTER);
// Initialize and wait till element(link) became clickable - timeout in 10 seconds
WebElement firstResult = new WebDriverWait(driver, Duration.ofSeconds(10)).until(ExpectedConditions.elementToBeClickable(By.xpath("//a/h3")));
// Print the first result
System.out.println(firstResult.getText());
```
> `WebElement firstResult = new WebDriverWait(driver, Duration.ofSeconds(10)).until(ExpectedConditions.elementToBeClickable(By.xpath("//a/h3")))`
* 將條件作為函數引用進去,這個wait會一直重複執行直到其返回值為true,但當條件為false或是超時,條件的返回值就會成為wait的返回值。
## Implicit Wait 隱式等待
WebDriver再找任何element的時候都會詢問DOM,當網頁上某些元素無法立即使用並且需要一些時間加載時就很好用。
默認的狀態下,implicit wait是不能用的,必須透過session啟用,但千萬不要跟explicit混用,可能會導致無法預測的等待時間。inplicit wait告訴webDriver在找element的時候,去詢問DOM for a certain amount of time.
```typescript
WebDriver driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.get("http://somedomain/url_that_delays_loading");
WebElement myDynamicElement = driver.findElement(By.id("myDynamicElement"));
```
## FluentWait 流利等待
他定義了等待條件的最長時間,以及檢查條件的頻率。他可以設定說忽略某些exception像是`NoSuchElementException` when searching for an element on the page.
```typescript
// Waiting 30 seconds for an element to be present on the page, checking
// for its presence once every 5 seconds.
Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
.withTimeout(Duration.ofSeconds(30))
.pollingEvery(Duration.ofSeconds(5))
.ignoring(NoSuchElementException.class);
WebElement foo = wait.until(new Function<WebDriver, WebElement>() {
public WebElement apply(WebDriver driver) {
return driver.findElement(By.id("foo"));
}
});
```
> 更簡潔
```typescript
WebElement foo = new WebDriverWait(driver, Duration.ofSeconds(3))
.until(driver -> driver.findElement(By.name("q")));
assertEquals(foo.getText(), "Hello from JavaScript!");
```
## 程式碼改編 不用使用Thread.sleep
1. 使用Implicit Wait` driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
`
```typescript
@BeforeAll
public static void beforeAll(){
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
}
```
2. 使用Explicit Wait
# 補充: Page Loading Strategy
當Selenium WebDriver載入一個要loading很久的畫面時,通常會建議不要下載額外的資源,像是images, css, js等等。
* `document.readyState`一個document描述的屬性,會顯示目前document loading的狀態。
* by default, webDriver在回應`driver.get()`or`driver.navigate().to()`的時候會延遲停止,直到document state is complete.
* In SPA application(like Angular, react, Ember), 一但動態內容載入完畢(pageloadstrategy status is COMPLETE),就算click link or do sth action,網頁都不會寄request to server,因為內容是在客戶端加載的,沒有刷新頁面。SPA Application可以不透過任何server request去load很多動態的view,因此pageloadstrategy總是顯示`complete`status直到我們做一個新的driver.get()或是driver.navigate().to()
## normal
* 它會讓Selenium WebDriver去等待,直到所有的頁面載入完畢!!
* selenium會等待整個介面加載完成(html, 子資源的下載與解析ex. js, images)
* When set to normal, selenium webdriver waits until the load event fire is returned.
```typescript
import org.openqa.selenium.PageLoadStrategy;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.chrome.ChromeDriver;
public class pageLoadStrategy {
public static void main(String[] args) {
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.setPageLoadStrategy(PageLoadStrategy.NORMAL);
WebDriver driver = new ChromeDriver(chromeOptions);
try {
// Navigate to Url
driver.get("https://google.com");
} finally {
driver.quit();
}
}
}
```
## eager
* 如果使用`PageLoadStrategy.EAGER`他就會讓selenium webdriver去等待直到原本的html document已經完全載入並解析,而且拋棄載入stylesheets, images, subframs
* 簡單來說: 等待整個DOM tree加載完成,
```typescript
import org.openqa.selenium.PageLoadStrategy;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.chrome.ChromeDriver;
public class pageLoadStrategy {
public static void main(String[] args) {
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.setPageLoadStrategy(PageLoadStrategy.EAGER);
WebDriver driver = new ChromeDriver(chromeOptions);
try {
// Navigate to Url
driver.get("https://google.com");
} finally {
driver.quit();
}
}
}
```
## none
* `PageLoadStrategy.NONE`當設定成none後selenium webdriver只會等帶到page下載完畢
* 當html下載完成之後,不需等待解析完成selenim會直接返回
```typescript
import org.openqa.selenium.PageLoadStrategy;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.chrome.ChromeDriver;
public class pageLoadStrategy {
public static void main(String[] args) {
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.setPageLoadStrategy(PageLoadStrategy.NONE);
WebDriver driver = new ChromeDriver(chromeOptions);
try {
// Navigate to Url
driver.get("https://google.com");
} finally {
driver.quit();
}
}
}
```
## selenium的幾種等待方式
1. Thread.sleep(1000)
2. 使用WebDriver wait
* 最多等待10秒,一旦等到XXX元素出现即退出等待执行后面操作,10秒后仍然没有出现XXX元素则抛出异常。
```typescript
WebDriverWait wait=new WebDriverWait(driver,10);
wait.until(ExpectedConditions.presenceOfElementLocated(By.id("XXX")));
```
3. 所有的findElement方式都會隱式等待10s
```typescript
driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS);
```
網頁來源:
* https://www.cnblogs.com/mabingxue/p/11057366.html
* https://www.selenium.dev/documentation/en/webdriver/page_loading_strategy/
# 補充: DOM是甚麼?
Document Object Model
簡單來說就是把HTML文件內的各個標籤,像是文字或是圖片等定義成物件,這些物件會形成樹狀結構(如下)

## 為何需要DOM
瀏覽器就像一個編譯器,去負責編譯我們寫的網頁程式,通常會先訂好一個規則讓各個瀏覽器按照規則去編譯我們的程式,W3C就定義了很多網頁的規則讓大家可以依照規則去設計瀏覽器,其中DOM就是其中一個規則。
## DOM 解析
DOM就是一個樹狀結構,最重要的一個重點就是node節點。在DOM裡面每個element, text都是一個節點
> 節點通常分為以下四點
* Document
* HTML檔案的開端 所有的一切從document開始往下執行
* Element
* 標籤 html tag
* Text
* 被標籤包起來的文字
* Attribute
* 各個標籤的相關屬性, class, id 等等
> html
```typescript
<html>
<head>
<title>example</title>
</head>
<body>
<h1 class="txt">Hello World</h1>
</body>
</html>
```
> DOM Tree

## DOM API
* 通常就是透過element的屬性去抓取元素
資源:https://ithelp.ithome.com.tw/articles/10202689