NỘI DUNG BÀI HỌC
✅Cách xử lý Tap, Swipe, Zoom
✅Cách xử lý Radio, Checkbox, Switch
Hình dạng của Radio, Checkbox và Switch tuy khác nhau nhưng cách xử lý thì giống nhau.
- Để tích chọn hoặc bật tắt chúng ta dùng hàm click()
- Để kiểm tra trạng thái thì dùng hàm isSelected()
hoặc getAttribute("checked")
Ví dụ xử lý Radio và Checkbox:
@Test
public void testCheckbox() {
WebElement checkbox = driver.findElement(AppiumBy.id("checkbox_id"));
if (!checkbox.isSelected()) {
checkbox.click();
}
System.out.println(checkbox.isSelected());
}
@Test
public void testRadioButton() {
WebElement radio = driver.findElement(AppiumBy.xpath("//android.widget.RadioButton[@text='Option 1']"));
radio.click();
System.out.println(radio.isSelected());
}
Ví dụ xử lý Switch trên Taurus App:
package com.anhtester.Bao10_Radio_Checkbox;
import com.anhtester.common.BaseTestTaurusApp;
import com.anhtester.drivers.DriverManager;
import com.anhtester.keywords.MobileUI;
import io.appium.java_client.AppiumBy;
import org.openqa.selenium.WebElement;
import org.testng.annotations.Test;
public class DemoRadioCheckboxSwitch extends BaseTestTaurusApp {
@Test(priority = 1)
public void testAddTable() {
loginTaurusApp();
//Navigate to config
DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Config")).click();
//Add table
DriverManager.getDriver().findElement(AppiumBy.xpath("//android.widget.Button[contains(@content-desc,'Tables management')]")).click();
DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Add Table")).click();
//Check status
String checkBeforeClick = DriverManager.getDriver().findElement(AppiumBy.xpath("//android.widget.Switch")).getAttribute("checked");
System.out.println(checkBeforeClick);
MobileUI.sleep(1);
//Click to turn on
DriverManager.getDriver().findElement(AppiumBy.xpath("//android.widget.Switch")).click();
MobileUI.sleep(1);
//Check status
String checkAfterClick = DriverManager.getDriver().findElement(AppiumBy.xpath("//android.widget.Switch")).getAttribute("checked");
System.out.println(checkAfterClick);
}
}
Class BaseTestTaurusApp có chứa 2 hàm sau:
public void loginTaurusApp() {
MobileUI.sleep(2);
WebElement inputUsername = DriverManager.getDriver().findElement(AppiumBy.xpath("//android.view.View[@content-desc=\"Mobile App Flutter Beta\"]/following-sibling::android.widget.EditText[1]"));
inputUsername.click();
inputUsername.sendKeys("admin");
MobileUI.sleep(1);
WebElement inputPassword = DriverManager.getDriver().findElement(AppiumBy.xpath("//android.view.View[@content-desc=\"Mobile App Flutter Beta\"]/following-sibling::android.widget.EditText[2]"));
inputPassword.click();
inputPassword.sendKeys("admin");
DriverManager.getDriver().findElement(AppiumBy.xpath("//android.widget.Button[@content-desc=\"Sign in\"]")).click();
MobileUI.sleep(2);
WebElement menuMenu = DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Menu"));
if (menuMenu.isDisplayed()) {
System.out.println("Login success.");
} else {
System.out.println("Login failed.");
}
}
public void downloadDataFromServer(int dataNumber) {
//Navigate to config to download database demo
DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Config")).click();
DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Server database")).click();
MobileUI.sleep(2);
DriverManager.getDriver().findElement(AppiumBy.xpath("//android.view.View[contains(@content-desc,'Data " + dataNumber + "')]/android.widget.Button")).click();
DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Replace")).click();
MobileUI.sleep(1);
//Handle Alert Message, check displayed hoặc getText/getAttribute để kiểm tra nội dung message
if (DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Downloaded")).isDisplayed()) {
System.out.println("Database demo downloaded.");
} else {
System.out.println("Warning!! Can not download Database demo.");
}
MobileUI.sleep(2);
}
Class MobileUI:
package com.anhtester.keywords;
public class MobileUI {
public static void sleep(double second){
try {
Thread.sleep((long) (1000*second));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
✅Cách xử lý Tap, Swipe, Zoom
3 cái tên nói trên là 3 thao tác hành động:
- Tap là chạm một ngón tay vào một vị trí cụ thể, có thể đè giữ một thời gian (long tap)
- Swipe là hành động vuốt màn hình từ dưới lên hoặc trên xuống hoặc trái phải.
- Zoom là dùng 2 ngón tay thu hẹp hoặc phóng rộng ra (để xem hình chẳng hạn)
Tất cả 3 hành động trên đều dùng chung các class hỗ trợ xử lý từ Selenium và Appium: PointerInput
và Sequence
🔆Xử lý Tap (chạm)
package com.anhtester.Bao10_Radio_Checkbox;
import com.anhtester.common.BaseTestTaurusApp;
import com.anhtester.drivers.DriverManager;
import com.anhtester.keywords.MobileUI;
import io.appium.java_client.AppiumBy;
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.testng.annotations.Test;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
public class DemoTap extends BaseTestTaurusApp {
private Point getCenterOfElement(Point location, Dimension size) {
return new Point(location.getX() + size.getWidth() / 2,
location.getY() + size.getHeight() / 2);
}
public 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()));
DriverManager.getDriver().perform(Collections.singletonList(sequence));
}
public 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()));
DriverManager.getDriver().perform(Arrays.asList(tap));
}
public 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()));
DriverManager.getDriver().perform(Arrays.asList(tap));
}
@Test(priority = 1)
public void demoTap1() {
loginTaurusApp();
//Navigate to config
WebElement menuConfig = DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Config"));
tap(menuConfig);
MobileUI.sleep(3);
}
}
Giải thích chi tiết
element.getLocation()
: Lấy tọa độ của phần tử trên màn hình.getCenterOfElement
(): Lấy vị trí chính giữa của element từ hàmgetLocation()
trênPointerInput
: Đại diện cho một thiết bị đầu vào, trong trường hợp này là ngón tay (PointerInput.Kind.TOUCH
).Sequence
: Tập hợp các hành động được thực hiện bởi mộtPointerInput
.createPointerMove()
: Di chuyển con trỏ đến vị trí của phần tử.PointerInput.Origin.viewport()
xác định hệ tọa độ dựa trên viewport (khung nhìn) của ứng dụng.createPointerDown()
: Mô phỏng việc chạm vào màn hình. Tham số0
chỉ định ngón tay đầu tiên (thường là ngón tay duy nhất được sử dụng).createPointerUp()
: Mô phỏng việc nhấc ngón tay khỏi màn hình.driver.perform()
: Thực hiện chuỗi hành động được định nghĩa trongSequence
.
Lưu ý
- Bạn cần import các lớp
PointerInput
,Sequence
,Point
vàDuration
từorg.openqa.selenium.
vàjava.time
.
Chúng ta có thể Tap theo tạo độ khi không có cách nào xác định được element.
Trên Appium Inspector các bạn chuyển sang tuỳ chọn Tap/Swipe By Coordinates để hiển thị toạ độ của vị trí chuột.
Lưu ý: muốn chính xác toạ độ thì phải dùng cùng 1 thiết bị để bắt toạ độ và chạy automation.
Ví dụ option Table management (12) có toạ độ là (460, 954) trên device Pixel 9 Pro XL 34 có độ phân giải là 1344x2992
package com.anhtester.Bao10_Radio_Checkbox;
import com.anhtester.common.BaseTestTaurusApp;
import com.anhtester.drivers.DriverManager;
import com.anhtester.keywords.MobileUI;
import io.appium.java_client.AppiumBy;
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.testng.annotations.Test;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
public class DemoTap extends BaseTestTaurusApp {
private Point getCenterOfElement(Point location, Dimension size) {
return new Point(location.getX() + size.getWidth() / 2,
location.getY() + size.getHeight() / 2);
}
public 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()));
DriverManager.getDriver().perform(Collections.singletonList(sequence));
}
public 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()));
DriverManager.getDriver().perform(Arrays.asList(tap));
}
public 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()));
DriverManager.getDriver().perform(Arrays.asList(tap));
}
@Test(priority = 2)
public void demoTap2() {
loginTaurusApp();
//Navigate to config
WebElement menuConfig = DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Config"));
tap(menuConfig);
MobileUI.sleep(3);
//Click option Table management
tap(456, 954);
MobileUI.sleep(2);
//Click Back
tap(82, 155);
MobileUI.sleep(2);
//Click menu Profile
tap(924, 2777);
MobileUI.sleep(2);
//Click Edit button
tap(297, 1199);
MobileUI.sleep(3);
}
}
🔆Xử lý Swipe (vuốt)
Ví dụ vào app Taurus, vào trang Menu hiện danh sách Tables.
- Thực hiện vuốt từ dưới lên để hiện phần table ẩn bên dưới:
package com.anhtester.Bao10_Radio_Checkbox;
import com.anhtester.common.BaseTestTaurusApp;
import com.anhtester.drivers.DriverManager;
import com.anhtester.keywords.MobileUI;
import io.appium.java_client.AppiumBy;
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.testng.annotations.Test;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
public class DemoSwipe extends BaseTestTaurusApp {
public 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));
DriverManager.getDriver().perform(Collections.singletonList(swipe));
}
@Test(priority = 1)
public void demoSwipe1() {
loginTaurusApp();
downloadDataFromServer(1);
DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Back")).click();
MobileUI.sleep(2);
DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Menu")).click();
MobileUI.sleep(4);
swipe(671, 1956, 671, 1229, 500);
MobileUI.sleep(3);
}
}
- Chuyển sang menu Date để thực hiện vuốt sang trái và phải:
package com.anhtester.Bao10_Radio_Checkbox;
import com.anhtester.common.BaseTestTaurusApp;
import com.anhtester.drivers.DriverManager;
import com.anhtester.keywords.MobileUI;
import io.appium.java_client.AppiumBy;
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.testng.annotations.Test;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
public class DemoSwipe extends BaseTestTaurusApp {
public 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));
DriverManager.getDriver().perform(Collections.singletonList(swipe));
}
public void swipeLeft() {
Dimension size = DriverManager.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 void swipeRight() {
Dimension size = DriverManager.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);
}
@Test(priority = 2)
public void demoSwipe2() {
loginTaurusApp();
DriverManager.getDriver().findElement(AppiumBy.accessibilityId("Date")).click();
MobileUI.sleep(4);
swipeLeft();
MobileUI.sleep(2);
swipeRight();
MobileUI.sleep(2);
//Nếu vị trí cố định ấy không dùng được thì chúng ta sẽ bắt toạ độ cụ thể
//Từ trái qua phải
swipe(95, 1032, 1255, 1032, 200);
MobileUI.sleep(2);
//Từ phải qua trái
swipe(1255, 1032, 95, 1032, 200);
MobileUI.sleep(3);
}
}
🔆Xử lý ZOOM (phóng to, thu nhỏ)
package com.anhtester.Bao10_Radio_Checkbox;
import com.anhtester.common.BaseTestTaurusApp;
import com.anhtester.drivers.DriverManager;
import com.anhtester.keywords.MobileUI;
import io.appium.java_client.AppiumBy;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.PointerInput;
import org.openqa.selenium.interactions.Sequence;
import org.testng.annotations.Test;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
public class DemoZoom extends BaseTestTaurusApp {
public 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));
DriverManager.getDriver().perform(Arrays.asList(zoom, zoom2));
}
@Test(priority = 1)
public void demoZoomIn() {
loginTaurusApp();
MobileUI.sleep(2);
WebElement inputSearch = DriverManager.getDriver().findElement(AppiumBy.xpath("//android.widget.EditText"));
zoom(inputSearch, 2); // Phóng to gấp 2 lần
MobileUI.sleep(3);
}
@Test(priority = 2)
public void demoZoomOut() {
loginTaurusApp();
MobileUI.sleep(2);
WebElement inputSearch = DriverManager.getDriver().findElement(AppiumBy.xpath("//android.widget.EditText"));
zoom(inputSearch, 0.5); // Thu nhỏ 2 lần
MobileUI.sleep(3);
}
}
Giải thích chi tiết
element.getLocation()
vàelement.getSize()
: Lấy tọa độ và kích thước của phần tử để tính toán tâm của nó.centerX
vàcenterY
: Tọa độ tâm của phần tử.distance
: Khoảng cách ban đầu giữa hai ngón tay.PointerInput
: Đại diện cho hai thiết bị đầu vào, mỗi thiết bị đại diện cho một ngón tay (PointerInput.Kind.TOUCH
).Sequence
: Tập hợp các hành động được thực hiện bởi mỗiPointerInput
.createPointerMove()
: Di chuyển con trỏ đến vị trí tương ứng.createPointerDown()
: Mô phỏng việc chạm vào màn hình.createPointerUp()
: Mô phỏng việc nhấc ngón tay khỏi màn hình.driver.perform()
: Thực hiện chuỗi hành động được định nghĩa trongSequence
.scale
: Hệ số zoom. Nếuscale > 1
, phóng to. Nếuscale < 1
, thu nhỏ.
Chỗ này muốn nhìn rõ thì kiếm cái tấm hình, click vào xong thu phóng sẽ dễ nhìn hơn. Vì các element khác dạng cố định nên có thể không nhìn thấy sự thu phóng.