NỘI DUNG BÀI HỌC
✅Cách sử dụng Implicit Wait
✅Cách sử dụng Explicit Wait
✅Cách sử dụng Plugin Wait từ Appium 2x
✅Các loại Waits khi sử dụng Appium chờ đợi element trên mobile app
Trong Appium Java 2.x, có hai loại Waits phổ biến khi làm việc với các phần tử trên ứng dụng:
-
Implicit Wait (Chờ ngầm định)
- Là thời gian chờ chung mà WebDriver áp dụng cho tất cả các phần tử.
- Nếu phần tử không xuất hiện ngay lập tức để thao tác thì WebDriver sẽ thử lại trong khoảng thời gian tối đa đã chỉ định trước khi ném ra lỗi
NoSuchElementException
. - Áp dụng chờ đợi ngay khi chúng ta gọi hàm findElement hoặc findElements.
- Không khuyến khích sử dụng nếu cần kiểm soát tốt hơn khi chờ phần tử.
-
Explicit Wait (Chờ rõ ràng)
- Chờ một điều kiện cụ thể xảy ra trước khi tiếp tục thực hiện hành động.
- Áp dụng chờ đợi cho từng trường hợp khi chúng ta gọi điều kiện sử dụng cụ thể cho từng element.
- Sử dụng
WebDriverWait
hoặcFluentWait
để kiểm tra điều kiện một cách linh hoạt. - Giúp kiểm soát tốt hơn so với
Implicit Wait
, chỉ chờ khi cần thiết.
Ngoài ta từ Appium 2x chúng ta còn có thể sử dụng Plugin để cài đặt cơ chế waits thông qua command line khi khởi động Appium Server. Tuy nhiên thì cách này cũng không hay bằng Explicit Wait. Nên vì thế chúng ta sẽ tìm hiểu kĩ và sử dụng Explicit Wait thôi là đủ.
✅Cách sử dụng Implicit Wait
Khai báo một dòng code duy nhất để thiết lập wait toàn cục cho tất cả các câu lệnh tìm kiếm element (findElement/findElements). Chúng ta sẽ đặt dòng code này tại class BaseTest.
// Ví dụ đặt thời gian chờ implicit là 10 giây
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
Triển khai vào code cho cả Android và iOS:
import io.appium.java_client.AppiumBy;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import io.appium.java_client.ios.IOSDriver;
import io.appium.java_client.ios.options.XCUITestOptions;
import org.openqa.selenium.WebElement;
import org.testng.annotations.Test;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
public class ApplyImplicitWait {
@Test
public void applyImplicitWaitAndroid() throws MalformedURLException {
UiAutomator2Options options = new UiAutomator2Options();
options.setDeviceName("emulator-5554");
options.setPlatformName("Android");
options.setAutomationName("UiAutomator2");
options.setApp("/path/to/app.apk");
AndroidDriver driver = new AndroidDriver(new URL("http://localhost:4723"), options);
// Đặt thời gian chờ implicit là 10 giây
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
// Tìm kiếm phần tử (sẽ chờ tối đa 10 giây nếu không tìm thấy)
// Nếu tìm thấy tại giây vào thì xử lý ngay tại giây đó
WebElement button = driver.findElement(AppiumBy.id("com.example:id/button_login"));
button.click();
driver.quit();
}
@Test
public void applyImplicitWaitIOS() throws MalformedURLException {
XCUITestOptions options = new XCUITestOptions();
options.setDeviceName("iPhone 16");
options.setPlatformName("iOS");
options.setAutomationName("XCUITest");
options.setApp("/path/to/app.ipa");
IOSDriver driver = new IOSDriver(new URL("http://localhost:4723"), options);
// Đặt thời gian chờ implicit là 10 giây
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
// Tìm kiếm phần tử (sẽ chờ tối đa 10 giây nếu không tìm thấy)
// Nếu tìm thấy tại giây vào thì xử lý ngay tại giây đó
WebElement button = driver.findElement(AppiumBy.id("loginButton"));
button.click();
driver.quit();
}
}
Demo cụ thể đối với My Demo App:
import com.anhtester.common.BaseTest;
import com.anhtester.drivers.DriverManager;
import com.anhtester.keywords.MobileUI;
import io.appium.java_client.AppiumBy;
import org.openqa.selenium.WebElement;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.time.Duration;
public class DemoImplicitWait extends BaseTest {
@Test
public void testImplicitWait() {
//Set implicit wait toàn cục cho tất cả các lệnh findElement tối đa là 3 giây
DriverManager.getDriver().manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
DriverManager.getDriver().findElement(AppiumBy.accessibilityId("View menu")).click();
//Ví dụ chỗ này sai locator, thì sau 3 giây nó sẽ báo lỗi
DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Login Menu Item 123")).click();
WebElement headerLoginPage = DriverManager.getDriver().findElement(AppiumBy.id("com.saucelabs.mydemoapp.android:id/loginTV"));
Assert.assertTrue(headerLoginPage.isDisplayed());
String headerLoginText = headerLoginPage.getText();
System.out.println("headerLoginText: " + headerLoginText);
Assert.assertEquals(headerLoginText, "Login");
DriverManager.getDriver().findElement(AppiumBy.xpath("//android.widget.EditText[@resource-id=\"com.saucelabs.mydemoapp.android:id/nameET\"]")).clear();
DriverManager.getDriver().findElement(AppiumBy.xpath("//android.widget.EditText[@resource-id=\"com.saucelabs.mydemoapp.android:id/nameET\"]")).sendKeys("bod@example.com");
DriverManager.getDriver().findElement(AppiumBy.xpath("//android.widget.EditText[@resource-id=\"com.saucelabs.mydemoapp.android:id/passwordET\"]")).clear();
DriverManager.getDriver().findElement(AppiumBy.xpath("//android.widget.EditText[@resource-id=\"com.saucelabs.mydemoapp.android:id/passwordET\"]")).sendKeys("10203040");
DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Tap to login with given credentials")).click();
MobileUI.sleep(2);
WebElement headerCatalogPage = DriverManager.getDriver().findElement(AppiumBy.accessibilityId("title"));
Assert.assertTrue(headerCatalogPage.isDisplayed());
String headerCatalogText = headerCatalogPage.getText();
System.out.println("headerCatalogText: " + headerCatalogText);
Assert.assertEquals(headerCatalogText, "Products");
System.out.println("Login success.");
}
}
✅Cách sử dụng Explicit Wait
Chúng ta sẽ sử dụng class WebDriverWait để khai báo đối tượng class và thiết lặp thời gian chờ, khai báo cụ thể trong từng class tại vị trí sử dụng, thay vì class chung BaseTest.
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
Tiếp theo thì gọi các điều kiện chờ cụ thể, trong Appium 2.x, WebDriverWait
hỗ trợ nhiều điều kiện chờ (ExpectedConditions) giúp bạn kiểm soát quá trình chờ một cách hiệu quả hơn. Dưới đây là danh sách các hàm phổ biến và cách sử dụng chúng.
1. Chờ phần tử xuất hiện (visibilityOfElementLocated
)
🔹Chờ một phần tử hiển thị trên màn hình (có trong DOM và có thể nhìn thấy).
👉 Dùng khi: Cần đợi một phần tử xuất hiện trước khi thao tác.
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("com.example:id/button_login")));
Sau đó có thể lấy element.click()
Triển khai vào code cho cả Android và iOS:
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import io.appium.java_client.ios.IOSDriver;
import io.appium.java_client.ios.options.XCUITestOptions;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.annotations.Test;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
public class ApplyExplicitWait {
@Test
public void applyExplicitWaitAndroid() throws MalformedURLException {
UiAutomator2Options options = new UiAutomator2Options();
options.setDeviceName("emulator-5554");
options.setPlatformName("Android");
options.setAutomationName("UiAutomator2");
options.setApp("/path/to/app.apk");
AppiumDriver driver = new AndroidDriver(new URL("http://localhost:4723"), options);
// Khởi tạo WebDriverWait
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
// Chờ nút Login xuất hiện và có thể click
WebElement loginButton = wait.until(ExpectedConditions.elementToBeClickable(By.id("com.example:id/button_login")));
loginButton.click();
driver.quit();
}
@Test
public void applyExplicitWaitIOS() throws MalformedURLException {
XCUITestOptions options = new XCUITestOptions();
options.setDeviceName("iPhone 15");
options.setPlatformName("iOS");
options.setAutomationName("XCUITest");
options.setApp("/path/to/app.app");
IOSDriver driver = new IOSDriver(new URL("http://localhost:4723"), options);
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5));
// Chờ label "Welcome" hiển thị
WebElement welcomeLabel = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("welcome_label")));
System.out.println(welcomeLabel.getText());
driver.quit();
}
}
2. Chờ phần tử biến mất (invisibilityOfElementLocated
)
🔹 Chờ một phần tử biến mất khỏi màn hình (không hiển thị hoặc không còn trong DOM).
👉 Dùng khi: Chờ một màn hình loading hoặc một dialog cụ thể biến mất trước khi tiếp tục.
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.id("com.example:id/loading_spinner")));
3. Chờ phần tử có thể click (elementToBeClickable
)
🔹 Chờ phần tử có thể click được (hiển thị và có thể tương tác).
👉 Dùng khi: button nào đó có thể sẵn sàng để click vào vì nhiều khi nó đang bị disable, tránh lỗi "Element Not Clickable".
WebElement button = wait.until(ExpectedConditions.elementToBeClickable(By.id("com.example:id/button_login")));
button.click();
4. Chờ phần tử tồn tại trong DOM (presenceOfElementLocated
)
🔹 Chờ một phần tử có trong DOM, nhưng chưa chắc đã hiển thị.
👉 Dùng khi: Cần kiểm tra sự tồn tại của phần tử trong DOM, dù nó có thể đang bị ẩn. (display: none)
WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(By.id("com.example:id/hidden_element")));
5. Chờ phần tử có text cụ thể (textToBePresentInElement
)
🔹 Chờ đến khi phần tử hiển thị và có chứa text cụ thể.
👉 Dùng khi: Kiểm tra các text của các message trạng thái như "Success", "Completed", "Failed"...
wait.until(ExpectedConditions.textToBePresentInElementLocated(By.id("com.example:id/status"), "Login Success."));
6. Chờ một phần tử có attribute cụ thể (attributeToBe
)
🔹 Chờ đến khi phần tử có một giá trị thuộc tính cụ thể.
👉 Dùng khi: Chờ một nút chuyển từ disabled
sang enabled
.
wait.until(ExpectedConditions.attributeToBe(By.id("com.example:id/button"), "enabled", "true"));
7. Chờ số lượng phần tử nhất định (numberOfElementsToBe
)
🔹 Chờ đến khi một danh sách phần tử đạt số lượng mong muốn.
👉 Dùng khi: Chờ danh sách items xuất hiện trên danh sách.
wait.until(ExpectedConditions.numberOfElementsToBe(By.className("android.widget.TextView"), 5));
8. Chờ URL chứa đoạn text cụ thể (urlContains
)
🔹 Chờ URL chứa một phần cụ thể (dành cho ứng dụng web/hybrid). Native app/Flutter app không cần đến.
👉 Dùng khi: Kiểm tra app đã chuyển đến trang mong muốn.
wait.until(ExpectedConditions.urlContains("dashboard"));
9. Chờ cửa sổ thứ hai xuất hiện (numberOfWindowsToBe
)
🔹 Chờ đến khi ứng dụng mở thêm một cửa sổ mới (dùng cho web/hybrid). Native app/Flutter app không cần đến.
👉 Dùng khi:Chờ app mở popup hoặc tab mới.
wait.until(ExpectedConditions.numberOfWindowsToBe(2));
10. Chờ JavaScript hoàn thành (jsReturnsValue
)
🔹 Chờ một đoạn JavaScript trả về giá trị (áp dụng cho hybrid/web). Native app/Flutter app không cần đến.
👉 Dùng khi:Chờ trang web load xong.
wait.until(ExpectedConditions.jsReturnsValue("return document.readyState === 'complete'"));
Ngoài ra còn rất nhiều hàm khác chúng ta tìm hiểu và sử dụng thêm từ class ExpectedConditions hỗ trợ.
Xem thêm tại đây: https://www.selenium.dev/selenium/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html
Cần chú ý các hàm cùng tên khác tham số (đa hình trong Java) thì có 2 dạng là kiểu By và WebElement.
Ví dụ:
elementToBeClickable(By locator)
🔹 Chờ phần tử có thể click dựa trên locator
từ đầu luôn.
🔹 Appium tự động tìm và kiểm tra nếu phần tử sẵn sàng click.
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
// Chờ nút login có thể click
WebElement loginButton = wait.until(ExpectedConditions.elementToBeClickable(By.id("com.example:id/button_login")));
loginButton.click();
elementToBeClickable(WebElement element)
🔹 Dùng khi bạn đã tìm thấy phần tử ở bước trước đó, nhưng nó chưa sẵn sàng để click.
🔹 Appium sẽ chờ đến khi phần tử có thể tương tác được.
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
// Tìm phần tử trước
WebElement loginButton = driver.findElement(By.id("com.example:id/button_login"));
// Chờ phần tử này có thể click
wait.until(ExpectedConditions.elementToBeClickable(loginButton)).click();
Demo cụ thể đối với My Demo App:
import com.anhtester.common.BaseTest;
import com.anhtester.drivers.DriverManager;
import com.anhtester.keywords.MobileUI;
import io.appium.java_client.AppiumBy;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.time.Duration;
public class DemoExplicitWait extends BaseTest {
@Test
public void testExplicitWait() {
//Set implicit wait về giá trị 0 để vô hiệu hoá
DriverManager.getDriver().manage().timeouts().implicitlyWait(Duration.ofSeconds(0));
DriverManager.getDriver().findElement(AppiumBy.accessibilityId("View menu")).click();
//Khai báo đối tượng wait thuộc WebDriverWait (Explicit)
WebDriverWait wait = new WebDriverWait(DriverManager.getDriver(), Duration.ofSeconds(2));
wait.until(ExpectedConditions.visibilityOfElementLocated(AppiumBy.accessibilityId("Login Menu Item 123")));
//Ví dụ chỗ này sai locator, cần wait locator visible sau đó mới thao tác sau
//Nếu nó không visible thì báo lỗi, không thao tác bước sau được
DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Login Menu Item 123")).click();
//Wait header present trước khi kểm tra displayed
wait.until(ExpectedConditions.presenceOfElementLocated(AppiumBy.id("com.saucelabs.mydemoapp.android:id/loginTV")));
WebElement headerLoginPage = DriverManager.getDriver().findElement(AppiumBy.id("com.saucelabs.mydemoapp.android:id/loginTV"));
Assert.assertTrue(headerLoginPage.isDisplayed());
String headerLoginText = headerLoginPage.getText();
System.out.println("headerLoginText: " + headerLoginText);
Assert.assertEquals(headerLoginText, "Login");
//Khai báo wait thứ 2 để setup lại thời gian khác wait trước
WebDriverWait wait2 = new WebDriverWait(DriverManager.getDriver(), Duration.ofSeconds(5));
//Wait email locator visible trước khi thao tác điền email
wait2.until(ExpectedConditions.visibilityOfElementLocated(AppiumBy.xpath("//android.widget.EditText[@resource-id=\"com.saucelabs.mydemoapp.android:id/nameET12345\"]")));
DriverManager.getDriver().findElement(AppiumBy.xpath("//android.widget.EditText[@resource-id=\"com.saucelabs.mydemoapp.android:id/nameET\"]")).clear();
DriverManager.getDriver().findElement(AppiumBy.xpath("//android.widget.EditText[@resource-id=\"com.saucelabs.mydemoapp.android:id/nameET\"]")).sendKeys("bod@example.com");
DriverManager.getDriver().findElement(AppiumBy.xpath("//android.widget.EditText[@resource-id=\"com.saucelabs.mydemoapp.android:id/passwordET\"]")).clear();
DriverManager.getDriver().findElement(AppiumBy.xpath("//android.widget.EditText[@resource-id=\"com.saucelabs.mydemoapp.android:id/passwordET\"]")).sendKeys("10203040");
DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Tap to login with given credentials")).click();
MobileUI.sleep(2);
WebElement headerCatalogPage = DriverManager.getDriver().findElement(AppiumBy.accessibilityId("title"));
Assert.assertTrue(headerCatalogPage.isDisplayed());
String headerCatalogText = headerCatalogPage.getText();
System.out.println("headerCatalogText: " + headerCatalogText);
Assert.assertEquals(headerCatalogText, "Products");
System.out.println("Login success.");
}
}
🔆Triển khai các hàm waits từ Explicit thành hàm chung
Nếu mà mỗi lần wait phải khai báo lại đối tượng wait và các hàm điều kiện thì nó hơi dài dòng, nên chúng ta có thể xây dựng hàm chung cho nó tại class MobileUI hổm rài chúng ta đã từng khai báo. Hoặc một class nào đó bất kỳ, hướng đến xây dựng class keyword chung để dùng lại sau này.
MobileUI.java
package com.anhtester.keywords;
import org.openqa.selenium.By;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.Point;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Pause;
import org.openqa.selenium.interactions.PointerInput;
import org.openqa.selenium.interactions.Sequence;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;
import java.util.*;
import static com.anhtester.drivers.DriverManager.getDriver;
public class MobileUI {
public static void sleep(double second) {
try {
Thread.sleep((long) (1000 * second));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static void swipe(int startX, int startY, int endX, int endY, int durationMillis) {
PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Sequence swipe = new Sequence(finger, 1);
swipe.addAction(finger.createPointerMove(Duration.ofMillis(0), PointerInput.Origin.viewport(), startX, startY));
swipe.addAction(finger.createPointerDown(0));
swipe.addAction(finger.createPointerMove(Duration.ofMillis(durationMillis), PointerInput.Origin.viewport(), endX, endY));
swipe.addAction(finger.createPointerUp(0));
getDriver().perform(Collections.singletonList(swipe));
}
public static void swipeLeft() {
Dimension size = getDriver().manage().window().getSize();
int startX = (int) (size.width * 0.8);
//int startY = size.height / 2; // Ở chính giữa màn hình
int startY = (int) (size.height * 0.3); // 1/3 bên trên màn hình
int endX = (int) (size.width * 0.2);
int endY = startY; // Giữ nguyên độ cao
int duration = 200;
swipe(startX, startY, endX, endY, duration);
}
public static void swipeRight() {
Dimension size = getDriver().manage().window().getSize();
int startX = (int) (size.width * 0.2);
//int startY = size.height / 2; // Ở chính giữa màn hình
int startY = (int) (size.height * 0.3); // 1/3 bên trên màn hình
int endX = (int) (size.width * 0.8);
int endY = startY; // Giữ nguyên độ cao
int duration = 200;
swipe(startX, startY, endX, endY, duration);
}
private static Point getCenterOfElement(Point location, Dimension size) {
return new Point(location.getX() + size.getWidth() / 2,
location.getY() + size.getHeight() / 2);
}
public static void tap(WebElement element) {
Point location = element.getLocation();
Dimension size = element.getSize();
Point centerOfElement = getCenterOfElement(location, size);
PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Sequence sequence = new Sequence(finger, 1)
.addAction(finger.createPointerMove(Duration.ZERO, PointerInput.Origin.viewport(), centerOfElement))
.addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()))
.addAction(new Pause(finger, Duration.ofMillis(500)))
.addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
getDriver().perform(Collections.singletonList(sequence));
}
public static void tap(int x, int y) {
PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Sequence tap = new Sequence(finger, 1);
tap.addAction(finger.createPointerMove(Duration.ZERO, PointerInput.Origin.viewport(), x, y));
tap.addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()));
tap.addAction(new Pause(finger, Duration.ofMillis(200))); //Chạm nhẹ nhanh
tap.addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
getDriver().perform(Arrays.asList(tap));
}
public static void tap(int x, int y, int second) {
PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Sequence tap = new Sequence(finger, 1);
tap.addAction(finger.createPointerMove(Duration.ZERO, PointerInput.Origin.viewport(), x, y));
tap.addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()));
tap.addAction(new Pause(finger, Duration.ofMillis(second))); //Chạm vào với thời gian chỉ định
tap.addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
getDriver().perform(Arrays.asList(tap));
}
public static void zoom(WebElement element, double scale) {
int centerX = element.getLocation().getX() + element.getSize().getWidth() / 2;
int centerY = element.getLocation().getY() + element.getSize().getHeight() / 2;
int distance = 100; // Khoảng cách giữa hai ngón tay
PointerInput finger1 = new PointerInput(PointerInput.Kind.TOUCH, "finger1");
PointerInput finger2 = new PointerInput(PointerInput.Kind.TOUCH, "finger2");
Sequence zoom = new Sequence(finger1, 1);
zoom.addAction(finger1.createPointerMove(Duration.ofMillis(0), PointerInput.Origin.viewport(), centerX - distance, centerY));
zoom.addAction(finger1.createPointerDown(0));
Sequence zoom2 = new Sequence(finger2, 1);
zoom2.addAction(finger2.createPointerMove(Duration.ofMillis(0), PointerInput.Origin.viewport(), centerX + distance, centerY));
zoom2.addAction(finger2.createPointerDown(0));
if (scale > 1) { // Phóng to
for (int i = 0; i < 10; i++) {
zoom.addAction(finger1.createPointerMove(Duration.ofMillis(50), PointerInput.Origin.viewport(), centerX - distance / 2, centerY));
zoom2.addAction(finger2.createPointerMove(Duration.ofMillis(50), PointerInput.Origin.viewport(), centerX + distance / 2, centerY));
}
} else { // Thu nhỏ
for (int i = 0; i < 10; i++) {
zoom.addAction(finger1.createPointerMove(Duration.ofMillis(50), PointerInput.Origin.viewport(), centerX - distance * 2, centerY));
zoom2.addAction(finger2.createPointerMove(Duration.ofMillis(50), PointerInput.Origin.viewport(), centerX + distance * 2, centerY));
}
}
zoom.addAction(finger1.createPointerUp(0));
zoom2.addAction(finger2.createPointerUp(0));
getDriver().perform(Arrays.asList(zoom, zoom2));
}
public static void scroll(int startX, int startY, int endX, int endY, int durationMillis) {
PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Sequence swipe = new Sequence(finger, 1);
swipe.addAction(finger.createPointerMove(Duration.ofMillis(0), PointerInput.Origin.viewport(), startX, startY));
swipe.addAction(finger.createPointerDown(0));
swipe.addAction(finger.createPointerMove(Duration.ofMillis(durationMillis), PointerInput.Origin.viewport(), endX, endY));
swipe.addAction(finger.createPointerUp(0));
getDriver().perform(Collections.singletonList(swipe));
}
public static void scrollGestureCommand() {
// Scroll gesture cho Android
Map<String, Object> scrollParams = new HashMap<>();
scrollParams.put("left", 670); //vị trí mép trái vùng cuộn cách mép trái màn hình
scrollParams.put("top", 500); //xác định mép trên của vùng cuộn
scrollParams.put("width", 200); //chiều ngang của vùng kéo
scrollParams.put("height", 2000); //chiều dài của vùng kéo
scrollParams.put("direction", "down"); //Scroll theo chiều từ trên xuống dưới (up, down, left, right)
scrollParams.put("percent", 1); //Scroll 100% của vùng kéo được chỉ định (width, height)
// Thực hiện scroll gesture
getDriver().executeScript("mobile: scrollGesture", scrollParams);
}
// Chờ phần tử có thể click (dùng By locator)
public static WebElement waitForElementToBeClickable(By locator, int timeout) {
WebDriverWait wait = new WebDriverWait(getDriver(), Duration.ofSeconds(timeout));
return wait.until(ExpectedConditions.elementToBeClickable(locator));
}
// Chờ phần tử có thể click (dùng WebElement)
public static WebElement waitForElementToBeClickable(WebElement element, int timeout) {
WebDriverWait wait = new WebDriverWait(getDriver(), Duration.ofSeconds(timeout));
return wait.until(ExpectedConditions.elementToBeClickable(element));
}
// Chờ phần tử xuất hiện trên màn hình
public static WebElement waitForElementVisibe(By locator, int timeout) {
WebDriverWait wait = new WebDriverWait(getDriver(), Duration.ofSeconds(timeout));
return wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
}
public static WebElement waitForElementVisibe(WebElement element, int timeout) {
WebDriverWait wait = new WebDriverWait(getDriver(), Duration.ofSeconds(timeout));
return wait.until(ExpectedConditions.visibilityOf(element));
}
// Chờ phần tử biến mất khỏi màn hình
public static boolean waitForElementInvisibe(By locator, int timeout) {
WebDriverWait wait = new WebDriverWait(getDriver(), Duration.ofSeconds(timeout));
return wait.until(ExpectedConditions.invisibilityOfElementLocated(locator));
}
// Chờ phần tử tồn tại trong DOM
public static WebElement waitForElementPresent(By locator, int timeout) {
WebDriverWait wait = new WebDriverWait(getDriver(), Duration.ofSeconds(timeout));
return wait.until(ExpectedConditions.presenceOfElementLocated(locator));
}
// Chờ phần tử có một text cụ thể
public static boolean waitForTextToBePresent(By locator, String text, int timeout) {
WebDriverWait wait = new WebDriverWait(getDriver(), Duration.ofSeconds(timeout));
return wait.until(ExpectedConditions.textToBePresentInElementLocated(locator, text));
}
public static boolean waitForTextToBePresent(WebElement element, String text, int timeout) {
WebDriverWait wait = new WebDriverWait(getDriver(), Duration.ofSeconds(timeout));
return wait.until(ExpectedConditions.textToBePresentInElement(element, text));
}
// Chờ phần tử có một thuộc tính cụ thể
public static boolean waitForAttributeToBe(By locator, String attribute, String value, int timeout) {
WebDriverWait wait = new WebDriverWait(getDriver(), Duration.ofSeconds(timeout));
return wait.until(ExpectedConditions.attributeToBe(locator, attribute, value));
}
public static boolean waitForAttributeToBe(WebElement element, String attribute, String value, int timeout) {
WebDriverWait wait = new WebDriverWait(getDriver(), Duration.ofSeconds(timeout));
return wait.until(ExpectedConditions.attributeToBe(element, attribute, value));
}
// Chờ số lượng phần tử trong danh sách
public static List<WebElement> waitForNumberOfElements(By locator, int expectedCount, int timeout) {
WebDriverWait wait = new WebDriverWait(getDriver(), Duration.ofSeconds(timeout));
return wait.until(ExpectedConditions.numberOfElementsToBe(locator, expectedCount));
}
// Chờ đến khi URL chứa một đoạn text cụ thể (dành cho hybrid/web app)
public static boolean waitForUrlContains(String text, int timeout) {
WebDriverWait wait = new WebDriverWait(getDriver(), Duration.ofSeconds(timeout));
return wait.until(ExpectedConditions.urlContains(text));
}
// Chờ đến khi số lượng cửa sổ mở ra đạt một số lượng nhất định (dành cho hybrid/web app)
public static boolean waitForNumberOfWindows(int expectedWindows, int timeout) {
WebDriverWait wait = new WebDriverWait(getDriver(), Duration.ofSeconds(timeout));
return wait.until(ExpectedConditions.numberOfWindowsToBe(expectedWindows));
}
}
🚀 Sử dụng các hàm wait từ class MobileUI
Lấy tên class MobileUI chấm gọi tên hàm trực tiếp mà không cần khởi tạo đối tượng class.
//Ví dụ 1: Chờ một nút có thể click và bấm vào nó
WebElement loginButton = MobileUI.waitForElementToBeClickable(By.id("com.example:id/button_login"), 5);
loginButton.click();
//Ví dụ 2: Chờ cho phần tử hiển thị rồi lấy text
WebElement welcomeText = MobileUI.waitForVisibility(By.id("com.example:id/welcome_message"), 7);
System.out.println("Welcome message: " + welcomeText.getText());