Lấy phần tử khớp cuối cùng
Chọn phần tử cuối cùng trong danh sách bằng index selector và JavaScript.
Trong một flow, nhắm vào một view cụ thể thường đơn giản nhờ index. Ví dụ, nếu muốn nút Add to Basket thứ ba, bạn chỉ cần dùng index: 2:
- tapOn:
text: Add to Basket
index: 2 # Mục thứ 3. Index bắt đầu từ 0Nhưng nếu bạn đang test một danh sách động, như giỏ hàng, chuỗi tin nhắn hay thư viện ảnh, nơi số mục thay đổi thì sao? Nếu cần tương tác với mục cuối cùng nhưng không biết tổng số, bạn cần một cách tiếp cận mang tính thuật toán hơn.
Ghi công
Helper đánh index khéo léo này được Ben Sussman thuộc team Pomelo Care chia sẻ với cộng đồng.
Quy trình
Vì Astra Scout không có selector index: last có sẵn, bạn phải tự tìm nó bằng cách duyệt qua danh sách. Logic hoạt động như một vòng lặp quét:
- Bắt đầu từ
index: 0. - Kiểm tra phần tử có tồn tại ở index hiện tại không.
- Nếu có, tăng bộ đếm và kiểm tra
indextiếp theo. - Ngay khi một
indextrả vềnot visible, bạn biếtindextrước đó là cái cuối cùng.
1. Flow tìm kiếm tái sử dụng
Tạo một subflow tiện ích (ví dụ utils/find_last_element.yaml) chứa toàn bộ logic vòng lặp quét. Subflow này xử lý phần việc nặng của vòng lặp và quản lý trạng thái, cuối cùng xuất index đúng ra flow chính của bạn.
# utils/find_last_element.yaml
# Cách dùng: truyền SELECTOR_TYPE ("id" hoặc "text") và SELECTOR_VALUE
# Kết quả: index được lưu trong ${output.lastElementIndex}
# Bước 1: trước hết cần khởi tạo trạng thái tìm kiếm
- evalScript: ${output.lastElementIndex = -1}
- evalScript: ${output._findLastElement_complete = false}
# Bước 2: kiểm tra loại đầu vào để tránh lỗi
# Ví dụ này chỉ hỗ trợ selector "id" và "text"
- runFlow:
when:
true: ${SELECTOR_TYPE != "id" && SELECTOR_TYPE != "text"}
commands:
- evalScript: |
throw new Error("Invalid SELECTOR_TYPE: " + SELECTOR_TYPE + ". Must be 'id' or 'text'");
# Bước 3: vòng lặp tìm kiếm
# Lặp cho đến khi gặp một index không tồn tại.
- repeat:
while:
true: ${!output._findLastElement_complete}
commands:
# Kiểm tra phần tử có tồn tại ở index hiện tại (selector id)
- runFlow:
when:
true: ${SELECTOR_TYPE == "id"}
commands:
- runFlow:
when:
visible:
id: "${SELECTOR_VALUE}"
index: ${output.lastElementIndex + 1}
commands:
# Tìm thấy phần tử, cập nhật index và tiếp tục
- evalScript: ${output.lastElementIndex = output.lastElementIndex + 1}
- runFlow:
when:
notVisible:
id: "${SELECTOR_VALUE}"
index: ${output.lastElementIndex + 1}
commands:
# Không thấy phần tử. Việc tìm kiếm kết thúc
- evalScript: ${output._findLastElement_complete = true}
# Kiểm tra phần tử có tồn tại ở index hiện tại (selector text)
- runFlow:
when:
true: ${SELECTOR_TYPE == "text"}
commands:
- runFlow:
when:
visible:
text: "${SELECTOR_VALUE}"
index: ${output.lastElementIndex + 1}
commands:
# Tìm thấy phần tử, cập nhật index và tiếp tục
- evalScript: ${output.lastElementIndex = output.lastElementIndex + 1}
- runFlow:
when:
notVisible:
text: "${SELECTOR_VALUE}"
index: ${output.lastElementIndex + 1}
commands:
# Không thấy phần tử. Việc tìm kiếm kết thúc
- evalScript: ${output._findLastElement_complete = true}
# Bước 4: kiểm tra cuối cùng
- runFlow:
when:
true: ${output.lastElementIndex == -1}
commands:
- evalScript: |
throw new Error("No elements found matching: " + SELECTOR_VALUE);Script này về bản chất là một thuật toán tìm kiếm tuyến tính viết bằng Maestro YAML. Dưới đây là giải thích các điểm cốt lõi của flow:
- Nó dùng các biến
outputđể theo dõi bộ đếm. VìevalScripttồn tại xuyên suốt quá trình chạy flow, ta có thể tăng index an toàn và dùng nó ở lần lặp tiếp theo. - Bằng cách dùng lệnh
repeatvới điều kiệnwhiledựa trên một cờ boolean (_findLastElement_complete, được khởi tạo làfalse), ta có thể tiếp tục tìm kiếm cho đến khi hết danh sách, nên tương thích với danh sách có kích thước bất kỳ. - Trong trường hợp có error, nếu script kết thúc mà index vẫn là
-1, nó ném một JavaScript error. Điều này ngăn test tiếp tục với trạng thái hỏng và cho bạn thông báo error rõ ràng.
2. Triển khai
Để dùng helper này, gọi nó như một subflow rồi dùng ${output.lastElementIndex} kết quả trong lệnh tiếp theo. Bạn phải chỉ định loại selector (id hoặc text) và giá trị để xác định thành phần.
# main_test.yaml
# Tìm nút "Remove" cuối cùng trong giỏ hàng và chạm vào nó
- runFlow:
file: ../utils/find_last_element.yaml
env:
SELECTOR_TYPE: "text"
SELECTOR_VALUE: "Remove"
- tapOn:
text: "Remove"
index: ${output.lastElementIndex}Nội dung liên quan
Nếu bạn muốn khám phá các khối xây dựng của cách tiếp cận thuật toán này, hãy xem các trang sau:
- Vòng lặp: tìm hiểu các cách lặp khác nhau trong flow, gồm vòng lặp
whilevà vòng lặp theo số lần. - Tổng quan JavaScript: khám phá cách dùng JavaScript để quản lý trạng thái và logic phức tạp trong file YAML.
- Cách dùng selector: xem lại các nguyên tắc cơ bản về cách Astra Scout nhận diện và phân biệt các phần tử UI giống nhau.
