---
# System prepended metadata

title: Waits 自動測試
tags: [Test, Shannon]

---

# 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文件內的各個標籤，像是文字或是圖片等定義成物件，這些物件會形成樹狀結構(如下)
![](https://www.w3schools.com/js/pic_htmltree.gif)

## 為何需要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
![](https://i.imgur.com/quZtbSB.jpg)

## DOM API
* 通常就是透過element的屬性去抓取元素

資源:https://ithelp.ithome.com.tw/articles/10202689