Chuyển đến nội dung chính

Bộ chọn

WebDriver Protocol cung cấp một số chiến lược bộ chọn để truy vấn một phần tử. WebdriverIO đơn giản hóa chúng để giữ cho việc chọn phần tử trở nên đơn giản. Xin lưu ý rằng mặc dù lệnh để truy vấn phần tử được gọi là $$$, chúng không liên quan gì đến jQuery hoặc Sizzle Selector Engine.

Mặc dù có rất nhiều bộ chọn khác nhau, chỉ một vài trong số chúng cung cấp cách đáng tin cậy để tìm đúng phần tử. Ví dụ, cho nút sau:

<button
id="main"
class="btn btn-large"
name="submission"
role="button"
data-testid="submit"
>
Submit
</button>

Chúng tôi khuyênkhông khuyên sử dụng các bộ chọn sau:

Bộ chọnKhuyến nghịGhi chú
$('button')🚨 Không bao giờTệ nhất - quá chung chung, không có ngữ cảnh.
$('.btn.btn-large')🚨 Không bao giờTệ. Gắn liền với kiểu dáng. Rất dễ thay đổi.
$('#main')⚠️ Thỉnh thoảngTốt hơn. Nhưng vẫn gắn liền với kiểu dáng hoặc trình lắng nghe sự kiện JS.
$(() => document.queryElement('button'))⚠️ Thỉnh thoảngTruy vấn hiệu quả, phức tạp để viết.
$('button[name="submission"]')⚠️ Thỉnh thoảngGắn liền với thuộc tính name có ngữ nghĩa HTML.
$('button[data-testid="submit"]')✅ TốtYêu cầu thuộc tính bổ sung, không liên quan đến a11y.
$('aria/Submit')✅ TốtTốt. Giống cách người dùng tương tác với trang. Nên sử dụng tệp dịch để kiểm tra của bạn không bị hỏng khi bản dịch được cập nhật. Lưu ý: Bộ chọn này có thể chậm hơn các bộ chọn khác trên các trang lớn.
$('button=Submit')✅ Luôn luônTốt nhất. Giống cách người dùng tương tác với trang và nhanh chóng. Nên sử dụng tệp dịch để kiểm tra của bạn không bị hỏng khi bản dịch được cập nhật.

CSS Query Selector

Nếu không có chỉ định khác, WebdriverIO sẽ truy vấn các phần tử sử dụng mẫu CSS selector, ví dụ:

selectors/example.js
loading...

Để lấy phần tử liên kết với một văn bản cụ thể, truy vấn văn bản bắt đầu bằng dấu bằng (=).

Ví dụ:

selectors/example.html
loading...

Bạn có thể truy vấn phần tử này bằng cách gọi:

selectors/example.js
loading...

Để tìm phần tử liên kết có văn bản hiển thị khớp một phần với giá trị tìm kiếm của bạn, truy vấn nó bằng cách sử dụng *= phía trước chuỗi truy vấn (ví dụ: *=driver).

Bạn cũng có thể truy vấn phần tử từ ví dụ trên bằng cách gọi:

selectors/example.js
loading...

Lưu ý: Bạn không thể kết hợp nhiều chiến lược bộ chọn trong một bộ chọn. Sử dụng nhiều truy vấn phần tử được nối chuỗi để đạt được cùng mục tiêu, ví dụ:

const elem = await $('header h1*=Welcome') // không hoạt động!!!
// sử dụng thay thế
const elem = await $('header').$('*=driver')

Phần tử với văn bản nhất định

Kỹ thuật tương tự có thể được áp dụng cho các phần tử. Ngoài ra, cũng có thể thực hiện việc so khớp không phân biệt chữ hoa/thường bằng cách sử dụng .= hoặc .*= trong truy vấn.

Ví dụ, đây là một truy vấn cho tiêu đề cấp 1 với văn bản "Welcome to my Page":

selectors/example.html
loading...

Bạn có thể truy vấn phần tử này bằng cách gọi:

selectors/example.js
loading...

Hoặc sử dụng truy vấn văn bản một phần:

selectors/example.js
loading...

Tương tự cho tên idclass:

selectors/example.html
loading...

Bạn có thể truy vấn phần tử này bằng cách gọi:

selectors/example.js
loading...

Lưu ý: Bạn không thể kết hợp nhiều chiến lược bộ chọn trong một bộ chọn. Sử dụng nhiều truy vấn phần tử được nối chuỗi để đạt được cùng mục tiêu, ví dụ:

const elem = await $('header h1*=Welcome') // không hoạt động!!!
// sử dụng thay thế
const elem = await $('header').$('h1*=Welcome')

Tag Name

Để truy vấn một phần tử với tên thẻ cụ thể, sử dụng <tag> hoặc <tag />.

selectors/example.html
loading...

Bạn có thể truy vấn phần tử này bằng cách gọi:

selectors/example.js
loading...

Name Attribute

Để truy vấn các phần tử với thuộc tính name cụ thể, bạn có thể sử dụng bộ chọn CSS3 thông thường hoặc chiến lược name được cung cấp từ JSONWireProtocol bằng cách truyền đại loại như [name="some-name"] làm tham số bộ chọn:

selectors/example.html
loading...
selectors/example.js
loading...

Lưu ý: Chiến lược bộ chọn này đã lỗi thời và chỉ hoạt động trong các trình duyệt cũ chạy bằng giao thức JSONWireProtocol hoặc khi sử dụng Appium.

xPath

Cũng có thể truy vấn các phần tử thông qua một xPath cụ thể.

Một bộ chọn xPath có định dạng như //body/div[6]/div[1]/span[1].

selectors/xpath.html
loading...

Bạn có thể truy vấn đoạn văn thứ hai bằng cách gọi:

selectors/example.js
loading...

Bạn có thể sử dụng xPath để duyệt lên và xuống cây DOM:

selectors/example.js
loading...

Accessibility Name Selector

Truy vấn các phần tử theo tên truy cập của chúng. Tên truy cập là những gì được công bố bởi trình đọc màn hình khi phần tử đó nhận được sự tập trung. Giá trị của tên truy cập có thể là cả nội dung trực quan hoặc văn bản thay thế ẩn.

thông tin

Bạn có thể đọc thêm về bộ chọn này trong bài đăng blog phát hành của chúng tôi

Lấy bằng aria-label

selectors/aria.html
loading...
selectors/example.js
loading...

Lấy bằng aria-labelledby

selectors/aria.html
loading...
selectors/example.js
loading...

Lấy bằng nội dung

selectors/aria.html
loading...
selectors/example.js
loading...

Lấy bằng title

selectors/aria.html
loading...
selectors/example.js
loading...

Lấy bằng thuộc tính alt

selectors/aria.html
loading...
selectors/example.js
loading...

ARIA - Role Attribute

Để truy vấn các phần tử dựa trên vai trò ARIA, bạn có thể chỉ định trực tiếp vai trò của phần tử như [role=button] làm tham số bộ chọn:

selectors/aria.html
loading...
selectors/example.js
loading...

ID Attribute

Chiến lược định vị "id" không được hỗ trợ trong giao thức WebDriver, người dùng nên sử dụng các chiến lược bộ chọn CSS hoặc xPath để tìm các phần tử bằng ID.

Tuy nhiên, một số trình điều khiển (ví dụ: Appium You.i Engine Driver) vẫn có thể hỗ trợ bộ chọn này.

Cú pháp bộ chọn ID được hỗ trợ hiện tại là:

//css locator
const button = await $('#someid')
//xpath locator
const button = await $('//*[@id="someid"]')
//id strategy
// Lưu ý: chỉ hoạt động trong Appium hoặc các framework tương tự hỗ trợ chiến lược định vị "ID"
const button = await $('id=resource-id/iosname')

JS Function

Bạn cũng có thể sử dụng các hàm JavaScript để lấy các phần tử bằng cách sử dụng các API gốc của web. Tất nhiên, bạn chỉ có thể làm điều này trong ngữ cảnh web (ví dụ: browser hoặc ngữ cảnh web trong di động).

Cho cấu trúc HTML sau:

selectors/js.html
loading...

Bạn có thể truy vấn phần tử anh em của #elem như sau:

selectors/example.js
loading...

Deep Selectors

cảnh báo

Bắt đầu từ phiên bản v9 của WebdriverIO, không cần bộ chọn đặc biệt này nữa vì WebdriverIO tự động xuyên qua Shadow DOM cho bạn. Khuyến nghị chuyển khỏi bộ chọn này bằng cách xóa >>> phía trước.

Nhiều ứng dụng frontend phụ thuộc nhiều vào các phần tử với shadow DOM. Về mặt kỹ thuật, không thể truy vấn các phần tử trong shadow DOM mà không có cách giải quyết. shadow$shadow$$ đã từng là những giải pháp có giới hạn. Với bộ chọn sâu, bây giờ bạn có thể truy vấn tất cả các phần tử trong bất kỳ shadow DOM nào bằng cách sử dụng lệnh truy vấn thông thường.

Giả sử chúng ta có một ứng dụng với cấu trúc sau:

Chrome Example

Với bộ chọn này, bạn có thể truy vấn phần tử <button /> được lồng trong một shadow DOM khác, ví dụ:

selectors/example.js
loading...

Mobile Selectors

Đối với kiểm tra di động lai, điều quan trọng là máy chủ tự động hóa phải ở đúng ngữ cảnh trước khi thực hiện lệnh. Để tự động hóa cử chỉ, trình điều khiển lý tưởng nên được đặt thành ngữ cảnh gốc. Nhưng để chọn các phần tử từ DOM, trình điều khiển sẽ cần được đặt thành ngữ cảnh webview của nền tảng. Chỉ sau đó các phương pháp đề cập ở trên mới có thể được sử dụng.

Đối với kiểm tra di động gốc, không có sự chuyển đổi giữa các ngữ cảnh, vì bạn phải sử dụng các chiến lược di động và sử dụng công nghệ tự động hóa thiết bị cơ bản trực tiếp. Điều này đặc biệt hữu ích khi kiểm tra cần một số kiểm soát chi tiết khi tìm các phần tử.

Android UiAutomator

Khung UI Automator của Android cung cấp một số cách để tìm các phần tử. Bạn có thể sử dụng UI Automator API, đặc biệt là lớp UiSelector để định vị các phần tử. Trong Appium, bạn gửi mã Java, dưới dạng chuỗi, đến máy chủ, nơi thực thi nó trong môi trường của ứng dụng, trả về phần tử hoặc các phần tử.

const selector = 'new UiSelector().text("Cancel").className("android.widget.Button")'
const button = await $(`android=${selector}`)
await button.click()

Android DataMatcher và ViewMatcher (chỉ Espresso)

Chiến lược DataMatcher của Android cung cấp một cách để tìm các phần tử bằng Data Matcher

const menuItem = await $({
"name": "hasEntry",
"args": ["title", "ViewTitle"]
})
await menuItem.click()

Và tương tự View Matcher

const menuItem = await $({
"name": "hasEntry",
"args": ["title", "ViewTitle"],
"class": "androidx.test.espresso.matcher.ViewMatchers"
})
await menuItem.click()

Android View Tag (chỉ Espresso)

Chiến lược thẻ xem cung cấp một cách thuận tiện để tìm các phần tử bằng thẻ của chúng.

const elem = await $('-android viewtag:tag_identifier')
await elem.click()

iOS UIAutomation

Khi tự động hóa một ứng dụng iOS, UI Automation framework của Apple có thể được sử dụng để tìm các phần tử.

API JavaScript này có các phương thức để truy cập vào khung nhìn và mọi thứ trên đó.

const selector = 'UIATarget.localTarget().frontMostApp().mainWindow().buttons()[0]'
const button = await $(`ios=${selector}`)
await button.click()

Bạn cũng có thể sử dụng tìm kiếm predicate trong iOS UI Automation trong Appium để tinh chỉnh việc chọn phần tử hơn nữa. Xem tại đây để biết chi tiết.

iOS XCUITest predicate strings và class chains

Với iOS 10 trở lên (sử dụng trình điều khiển XCUITest), bạn có thể sử dụng predicate strings:

const selector = `type == 'XCUIElementTypeSwitch' && name CONTAINS 'Allow'`
const switch = await $(`-ios predicate string:${selector}`)
await switch.click()

class chains:

const selector = '**/XCUIElementTypeCell[`name BEGINSWITH "D"`]/**/XCUIElementTypeButton'
const button = await $(`-ios class chain:${selector}`)
await button.click()

Accessibility ID

Chiến lược định vị accessibility id được thiết kế để đọc một định danh duy nhất cho một phần tử UI. Điều này có lợi ích là không thay đổi trong quá trình bản địa hóa hoặc bất kỳ quá trình nào khác có thể thay đổi văn bản. Ngoài ra, nó có thể hỗ trợ trong việc tạo ra các bài kiểm tra đa nền tảng, nếu các phần tử có chức năng giống nhau có cùng accessibility id.

  • Đối với iOS, đây là accessibility identifier được Apple đặt ra tại đây.
  • Đối với Android, accessibility id ánh xạ tới content-description cho phần tử, như được mô tả tại đây.

Đối với cả hai nền tảng, việc lấy một phần tử (hoặc nhiều phần tử) bằng accessibility id của chúng thường là phương pháp tốt nhất. Đây cũng là cách ưa thích hơn so với chiến lược name đã lỗi thời.

const elem = await $('~my_accessibility_identifier')
await elem.click()

Class Name

Chiến lược class name là một string đại diện cho một phần tử UI trên khung nhìn hiện tại.

  • Đối với iOS, đó là tên đầy đủ của lớp UIAutomation, và sẽ bắt đầu bằng UIA-, chẳng hạn như UIATextField cho một trường văn bản. Tham khảo đầy đủ có thể được tìm thấy tại đây.
  • Đối với Android, đó là tên đầy đủ của lớp UI Automator class, chẳng hạn như android.widget.EditText cho một trường văn bản. Tham khảo đầy đủ có thể được tìm thấy tại đây.
  • Đối với Youi.tv, đó là tên đầy đủ của lớp Youi.tv, và sẽ bắt đầu bằng CYI-, chẳng hạn như CYIPushButtonView cho một phần tử nút nhấn. Tham khảo đầy đủ có thể được tìm thấy tại trang GitHub của You.i Engine Driver
// Ví dụ iOS
await $('UIATextField').click()
// Ví dụ Android
await $('android.widget.DatePicker').click()
// Ví dụ Youi.tv
await $('CYIPushButtonView').click()

Chain Selectors

Nếu bạn muốn cụ thể hơn trong truy vấn của mình, bạn có thể nối các bộ chọn cho đến khi bạn tìm thấy đúng phần tử. Nếu bạn gọi element trước lệnh thực tế của bạn, WebdriverIO bắt đầu truy vấn từ phần tử đó.

Ví dụ, nếu bạn có cấu trúc DOM như:

<div class="row">
<div class="entry">
<label>Product A</label>
<button>Add to cart</button>
<button>More Information</button>
</div>
<div class="entry">
<label>Product B</label>
<button>Add to cart</button>
<button>More Information</button>
</div>
<div class="entry">
<label>Product C</label>
<button>Add to cart</button>
<button>More Information</button>
</div>
</div>

Và bạn muốn thêm sản phẩm B vào giỏ hàng, sẽ khó thực hiện điều đó chỉ bằng cách sử dụng bộ chọn CSS.

Với việc nối bộ chọn, nó dễ dàng hơn nhiều. Chỉ cần thu hẹp phần tử mong muốn từng bước một:

await $('.row .entry:nth-child(2)').$('button*=Add').click()

Appium Image Selector

Sử dụng chiến lược định vị -image, có thể gửi cho Appium một tệp hình ảnh đại diện cho phần tử bạn muốn truy cập.

Các định dạng tệp được hỗ trợ jpg,png,gif,bmp,svg

Tham khảo đầy đủ có thể được tìm thấy tại đây

const elem = await $('./file/path/of/image/test.jpg')
await elem.click()

Lưu ý: Cách Appium hoạt động với bộ chọn này là nó sẽ nội bộ tạo ảnh chụp (màn hình) và sử dụng bộ chọn hình ảnh được cung cấp để xác minh xem phần tử có thể được tìm thấy trong ảnh chụp (màn hình) đó hay không.

Hãy lưu ý rằng Appium có thể thay đổi kích thước ảnh chụp (màn hình) được chụp để làm cho nó phù hợp với kích thước CSS của màn hình (ứng dụng) của bạn (điều này sẽ xảy ra trên iPhone nhưng cũng trên máy Mac với màn hình Retina vì DPR lớn hơn 1). Điều này sẽ dẫn đến không tìm thấy kết quả khớp vì bộ chọn hình ảnh được cung cấp có thể đã được lấy từ ảnh chụp màn hình gốc. Bạn có thể khắc phục điều này bằng cách cập nhật cài đặt Appium Server, xem tài liệu Appium để biết cài đặt và bình luận này để có giải thích chi tiết.

React Selectors

WebdriverIO cung cấp cách chọn các component React dựa trên tên component. Để làm điều này, bạn có lựa chọn hai lệnh: react$react$$.

Các lệnh này cho phép bạn chọn các component từ React VirtualDOM và trả về một Phần tử WebdriverIO duy nhất hoặc một mảng các phần tử (tùy thuộc vào hàm nào được sử dụng).

Lưu ý: Các lệnh react$react$$ có chức năng tương tự, ngoại trừ react$$ sẽ trả về tất cả các trường hợp khớp dưới dạng một mảng các phần tử WebdriverIO, và react$ sẽ trả về trường hợp đầu tiên được tìm thấy.

Ví dụ cơ bản

// index.jsx
import React from 'react'
import ReactDOM from 'react-dom'

function MyComponent() {
return (
<div>
MyComponent
</div>
)
}

function App() {
return (<MyComponent />)
}

ReactDOM.render(<App />, document.querySelector('#root'))

Trong mã trên có một thể hiện MyComponent đơn giản bên trong ứng dụng, mà React đang hiển thị bên trong một phần tử HTML với id="root".

Với lệnh browser.react$, bạn có thể chọn một thể hiện của MyComponent:

const myCmp = await browser.react$('MyComponent')

Bây giờ bạn đã có phần tử WebdriverIO được lưu trong biến myCmp, bạn có thể thực thi các lệnh phần tử đối với nó.

Lọc components

Thư viện mà WebdriverIO sử dụng nội bộ cho phép lọc lựa chọn của bạn theo thuộc tính và/hoặc trạng thái của component. Để làm điều đó, bạn cần truyền một đối số thứ hai cho thuộc tính và/hoặc một đối số thứ ba cho trạng thái vào lệnh trình duyệt.

// index.jsx
import React from 'react'
import ReactDOM from 'react-dom'

function MyComponent(props) {
return (
<div>
Hello { props.name || 'World' }!
</div>
)
}

function App() {
return (
<div>
<MyComponent name="WebdriverIO" />
<MyComponent />
</div>
)
}

ReactDOM.render(<App />, document.querySelector('#root'))

Nếu bạn muốn chọn thể hiện của MyComponent có thuộc tính nameWebdriverIO, bạn có thể thực hiện lệnh như sau:

const myCmp = await browser.react$('MyComponent', {
props: { name: 'WebdriverIO' }
})

Nếu bạn muốn lọc lựa chọn của mình theo trạng thái, lệnh browser sẽ trông giống như sau:

const myCmp = await browser.react$('MyComponent', {
state: { myState: 'some value' }
})

Xử lý React.Fragment

Khi sử dụng lệnh react$ để chọn fragments của React, WebdriverIO sẽ trả về phần tử con đầu tiên của component đó làm node của component. Nếu bạn sử dụng react$$, bạn sẽ nhận được một mảng chứa tất cả các node HTML bên trong các fragments khớp với bộ chọn.

// index.jsx
import React from 'react'
import ReactDOM from 'react-dom'

function MyComponent() {
return (
<React.Fragment>
<div>
MyComponent
</div>
<div>
MyComponent
</div>
</React.Fragment>
)
}

function App() {
return (<MyComponent />)
}

ReactDOM.render(<App />, document.querySelector('#root'))

Với ví dụ trên, đây là cách các lệnh sẽ hoạt động:

await browser.react$('MyComponent') // trả về Phần tử WebdriverIO cho <div /> đầu tiên
await browser.react$$('MyComponent') // trả về Phần tử WebdriverIO cho mảng [<div />, <div />]

Lưu ý: Nếu bạn có nhiều thể hiện của MyComponent và bạn sử dụng react$$ để chọn các component fragment này, bạn sẽ nhận được một mảng một chiều của tất cả các node. Nói cách khác, nếu bạn có 3 thể hiện <MyComponent />, bạn sẽ nhận được một mảng có sáu phần tử WebdriverIO.

Custom Selector Strategies

Nếu ứng dụng của bạn yêu cầu một cách cụ thể để lấy các phần tử, bạn có thể tự định nghĩa một chiến lược bộ chọn mà bạn có thể sử dụng với custom$custom$$. Để làm điều đó, đăng ký chiến lược của bạn một lần vào đầu bài kiểm tra, ví dụ: trong một hook before:

queryElements/customStrategy.js
loading...

Với đoạn HTML sau:

queryElements/example.html
loading...

Sau đó sử dụng nó bằng cách gọi:

queryElements/customStrategy.js
loading...

Lưu ý: điều này chỉ hoạt động trong môi trường web trong đó lệnh execute có thể được chạy.

Welcome! How can I help?

WebdriverIO AI Copilot