其他定位器
簡介
請查看主要的定位器指南以了解最常用和推薦的定位器。
除了推薦的定位器如 page.getByRole()
和 page.getByText()
之外,Playwright 還支援本指南中描述的各種其他定位器。
CSS 定位器
我們建議優先使用使用者可見的定位器,如文字或無障礙角色,而非使用與實作細節綁定且在頁面變更時可能失效的 CSS。
Playwright 可以透過 CSS 選擇器定位元素。
await page.locator('css=button').click();
Playwright 以兩種方式增強標準 CSS 選擇器:
- CSS 選擇器可穿透開放的 shadow DOM。
- Playwright 新增自訂偽類別,如
:visible
、:has-text()
、:has()
、:is()
、:nth-match()
等等。
CSS:依文字比對
Playwright 包含多個 CSS 偽類別來依文字內容比對元素。
-
article:has-text("Playwright")
-:has-text()
比對在內部某處(可能在子元素或後代元素中)包含指定文字的任何元素。比對不區分大小寫,會修剪空白字元並搜尋子字串。例如,
article:has-text("Playwright")
比對<article><div>Playwright</div></article>
。請注意,
:has-text()
應該與其他 CSS 指定符一起使用,否則將比對所有包含指定文字的元素,包括<body>
。// 錯誤,將比對包括 <body> 在內的許多元素
await page.locator(':has-text("Playwright")').click();
// 正確,只比對 <article> 元素
await page.locator('article:has-text("Playwright")').click(); -
#nav-bar :text("Home")
-:text()
偽類別比對包含指定文字的最小元素。比對不區分大小寫,會修剪空白字元並搜尋子字串。例如,這將在
#nav-bar
元素內部某處找到包含文字 "Home" 的元素:await page.locator('#nav-bar :text("Home")').click();
-
#nav-bar :text-is("Home")
-:text-is()
偽類別比對具有完全相同文字的最小元素。嚴格比對區分大小寫,會修剪空白字元並搜尋完整字串。例如,
:text-is("Log")
不會比對<button>Log in</button>
,因為<button>
包含單一文字節點"Log in"
,這不等於"Log"
。然而,:text-is("Log")
會比對<button> Log <span>in</span></button>
,因為<button>
包含文字節點" Log "
。同樣地,
:text-is("Download")
不會比對<button>download</button>
,因為這是區分大小寫的。
-
#nav-bar :text-matches("reg?ex", "i")
-:text-matches()
偽類別比對文字內容符合 JavaScript 式正規表示式的最小元素。例如,
:text-matches("Log\s*in", "i")
比對<button>Login</button>
和<button>log IN</button>
。
文字比對總是會正規化空白字元。例如,它將多個空格轉為一個,將換行符轉為空格,並忽略前導和尾隨的空白字元。
類型為 button
和 submit
的輸入元素會依其 value
而非文字內容進行比對。例如,:text("Log in")
比對 <input type=button value="Log in">
。
CSS:只比對可見元素
Playwright 在 CSS 選擇器中支援 :visible
偽類別。例如,css=button
比對頁面上的所有按鈕,而 css=button:visible
只比對可見的按鈕。這對於區分非常相似但可見性不同的元素很有用。
考慮一個有兩個按鈕的頁面,第一個不可見,第二個可見。
<button style='display: none'>Invisible</button>
<button>Visible</button>
-
這將找到兩個按鈕並拋出嚴格性違反錯誤:
await page.locator('button').click();
-
這將只找到第二個按鈕,因為它是可見的,然後點擊它。
await page.locator('button:visible').click();
CSS:包含其他元素的元素
:has()
偽類別是一個實驗性的 CSS 偽類別。如果相對於給定元素的 :scope
傳遞作為參數的任何選擇器至少比對一個元素,它就會回傳該元素。
下面的程式碼片段回傳內部有 <div class=promo>
的 <article>
元素的文字內容。
await page.locator('article:has(div.promo)').textContent();
CSS:符合其中一個條件的元素
以逗號分隔的 CSS 選擇器清單將比對所有可以被該清單中任一選擇器選取的元素。
// 點擊包含 "Log in" 或 "Sign in" 文字的 <button>
await page.locator('button:has-text("Log in"), button:has-text("Sign in")').click();
:is()
偽類別是一個實驗性的 CSS 偽類別,可能對於在元素上指定額外條件清單很有用。
CSS:依版面配置比對元素
依版面配置的比對可能產生意外的結果。例如,當版面配置因一個像素而改變時,可能會比對到不同的元素。
有時,當目標元素缺乏獨特特徵時,很難想出好的選擇器來選取它。在這種情況下,使用 Playwright 版面配置 CSS 偽類別可能會有幫助。這些可以與常規 CSS 結合,以精確定位多個選擇中的一個。
例如,input:right-of(:text("Password"))
比對位於文字 "Password" 右側的輸入欄位 - 當頁面有多個難以區分的輸入欄位時很有用。
請注意,版面配置偽類別除了其他選擇器(如 input
)之外很有用。如果您單獨使用版面配置偽類別,如 :right-of(:text("Password"))
,您很可能得到的不是您要找的輸入欄位,而是文字和目標輸入欄位之間的某個空元素。
版面配置偽類別使用邊界客戶端矩形來計算元素的距離和相對位置。
:right-of(div > button)
- 比對位於任何符合內部選擇器的元素右側的元素,在任何垂直位置。:left-of(div > button)
- 比對位於任何符合內部選擇器的元素左側的元素,在任何垂直位置。:above(div > button)
- 比對位於任何符合內部選擇器的元素上方的元素,在任何水平位置。:below(div > button)
- 比對位於任何符合內部選擇器的元素下方的元素,在任何水平位置。:near(div > button)
- 比對接近(在 50 CSS 像素內)任何符合內部選擇器的元素的元素。
請注意,結果比對會依其與錨點元素的距離排序,因此您可以使用 locator.first()
來選取最近的一個。這只有在您有類似元素清單且最近的元素明顯是正確的情況下才有用。然而,在其他情況下使用 locator.first()
很可能不會如預期運作 - 它不會定位到您正在搜尋的元素,而是某個恰好最近的其他元素,如隨機的空 <div>
,或是已捲動出去且目前不可見的元素。
// 填寫 "Username" 右側的輸入欄位
await page.locator('input:right-of(:text("Username"))').fill('value');
// 點擊促銷卡片附近的按鈕
await page.locator('button:near(.promo-card)').click();
// 點擊清單中最接近 "Label 3" 的單選輸入
await page.locator('[type=radio]:left-of(:text("Label 3"))').first().click();
所有版面配置偽類別都支援可選的最大像素距離作為最後一個參數。例如,button:near(:text("Username"), 120)
比對距離包含文字 "Username" 的元素最多 120 CSS 像素的按鈕。
CSS:從查詢結果中選取第 n 個比對
通常可以透過某些屬性或文字內容來區分元素,這對頁面變更更具彈性。
有時頁面包含許多相似的元素,很難選取特定的一個。例如:
<section> <button>Buy</button> </section>
<article><div> <button>Buy</button> </div></article>
<div><div> <button>Buy</button> </div></div>
在這種情況下,:nth-match(:text("Buy"), 3)
會選取上面程式碼片段中的第三個按鈕。請注意,索引是從一開始的。
// 點擊第三個 "Buy" 按鈕
await page.locator(':nth-match(:text("Buy"), 3)').click();
:nth-match()
對於等待指定數量的元素出現也很有用,可使用 locator.waitFor()
。
// 等待所有三個按鈕都可見
await page.locator(':nth-match(:text("Buy"), 3)').waitFor();
與 :nth-child()
不同,元素不必是兄弟節點,它們可以在頁面的任何地方。在上面的程式碼片段中,所有三個按鈕都符合 :text("Buy")
選擇器,而 :nth-match()
選取第三個按鈕。
第 n 個元素定位器
您可以使用 nth=
定位器並傳遞從零開始的索引,將查詢縮小到第 n 個比對。
// 點擊第一個按鈕
await page.locator('button').locator('nth=0').click();
// 點擊最後一個按鈕
await page.locator('button').locator('nth=-1').click();
父元素定位器
當您需要定位某個其他元素的父元素時,大多數時候您應該使用 locator.filter()
依子定位器進行篩選。例如,考慮以下 DOM 結構:
<li><label>Hello</label></li>
<li><label>World</label></li>
如果您想要定位包含文字 "Hello"
的標籤的父 <li>
,使用 locator.filter()
效果最好:
const child = page.getByText('Hello');
const parent = page.getByRole('listitem').filter({ has: child });
或者,如果您無法為父元素找到合適的定位器,請使用 xpath=..
。請注意,這種方法不如上述方法可靠,因為 DOM 結構的任何變更都會破壞您的測試。盡可能時請優先使用 locator.filter()
。
const parent = page.getByText('Hello').locator('xpath=..');
React 定位器
React 定位器是實驗性的,前綴為 _
。功能可能在未來會變更。
React 定位器允許依組件名稱和屬性值尋找元素。語法與 CSS 屬性選擇器非常相似,並支援所有 CSS 屬性選擇器運算子。
在 React 定位器中,組件名稱以 CamelCase 轉錄。
await page.locator('_react=BookItem').click();
更多範例:
- 依組件比對:
_react=BookItem
- 依組件和確切屬性值比對,區分大小寫:
_react=BookItem[author = "Steven King"]
- 僅依屬性值比對,不區分大小寫:
_react=[author = "steven king" i]
- 依組件和真值屬性值比對:
_react=MyButton[enabled]
- 依組件和布林值比對:
_react=MyButton[enabled = false]
- 依屬性值子字串比對:
_react=[author *= "King"]
- 依組件和多個屬性比對:
_react=BookItem[author *= "king" i][year = 1990]
- 依巢狀屬性值比對:
_react=[some.nested.value = 12]
- 依組件和屬性值前綴比對:
_react=BookItem[author ^= "Steven"]
- 依組件和屬性值後綴比對:
_react=BookItem[author $= "Steven"]
- 依組件和 key 比對:
_react=BookItem[key = '2']
- 依屬性值正規表示式比對:
_react=[author = /Steven(\\s+King)?/i]
要在樹中尋找 React 元素名稱,請使用 React DevTools。
React 定位器支援 React 15 及以上版本。
React 定位器以及 React DevTools 只能在未壓縮的應用程式建置中運作。
Vue 定位器
Vue 定位器是實驗性的,前綴為 _
。功能可能在未來會變更。
Vue 定位器允許依組件名稱和屬性值尋找元素。語法與 CSS 屬性選擇器非常相似,並支援所有 CSS 屬性選擇器運算子。
在 Vue 定位器中,組件名稱以 kebab-case 轉錄。
await page.locator('_vue=book-item').click();
更多範例:
- 依組件比對:
_vue=book-item
- 依組件和確切屬性值比對,區分大小寫:
_vue=book-item[author = "Steven King"]
- 僅依屬性值比對,不區分大小寫:
_vue=[author = "steven king" i]
- 依組件和真值屬性值比對:
_vue=my-button[enabled]
- 依組件和布林值比對:
_vue=my-button[enabled = false]
- 依屬性值子字串比對:
_vue=[author *= "King"]
- 依組件和多個屬性比對:
_vue=book-item[author *= "king" i][year = 1990]
- 依巢狀屬性值比對:
_vue=[some.nested.value = 12]
- 依組件和屬性值前綴比對:
_vue=book-item[author ^= "Steven"]
- 依組件和屬性值後綴比對:
_vue=book-item[author $= "Steven"]
- 依屬性值正規表示式比對:
_vue=[author = /Steven(\\s+King)?/i]
要在樹中尋找 Vue 元素名稱,請使用 Vue DevTools。
Vue 定位器支援 Vue2 及以上版本。
Vue 定位器以及 Vue DevTools 只能在未壓縮的應用程式建置中運作。
XPath 定位器
我們建議優先使用使用者可見的定位器,如文字或無障礙角色,而非使用與實作細節綁定且在頁面變更時容易失效的 XPath。
XPath 定位器等同於呼叫 Document.evaluate
。
await page.locator('xpath=//button').click();
任何以 //
或 ..
開頭的選擇器字串都會被視為 xpath 選擇器。例如,Playwright 會將 '//html/body'
轉換為 'xpath=//html/body'
。
XPath 不會穿透 shadow 根。
XPath 聯集
管道運算子(|
)可用於在 XPath 中指定多個選擇器。它會比對所有可以被該清單中任一選擇器選取的元素。
// 等待確認對話框或載入旋轉器
await page.locator(
`//span[contains(@class, 'spinner__loading')]|//div[@id='confirmation']`
).waitFor();
標籤到表單控制項重新定位
我們建議依標籤文字定位,而非依賴標籤到控制項重新定位。
Playwright 中的目標輸入動作會自動區分標籤和控制項,因此您可以定位標籤來對關聯的控制項執行動作。
例如,考慮以下 DOM 結構:<label for="password">Password:</label><input id="password" type="password">
。您可以使用 page.getByText()
依其 "Password" 文字定位標籤。然而,以下動作將在輸入欄位而非標籤上執行:
locator.click()
會點擊標籤並自動聚焦輸入欄位;locator.fill()
會填寫輸入欄位;locator.inputValue()
會回傳輸入欄位的值;locator.selectText()
會選取輸入欄位中的文字;locator.setInputFiles()
會為type=file
的輸入欄位設定檔案;locator.selectOption()
會從選取方塊中選取選項。
// 透過定位標籤來填寫輸入欄位
await page.getByText('Password').fill('secret');
然而,其他方法會定位標籤本身,例如 expect(locator).toHaveText()
會斷言標籤的文字內容,而非輸入欄位。
// 填寫輸入欄位並定位標籤
await expect(page.locator('label')).toHaveText('Password');
舊版文字定位器
我們建議使用現代的文字定位器。
舊版文字定位器比對包含傳遞文字的元素。
await page.locator('text=Log in').click();
舊版文字定位器有幾個變化:
-
text=Log in
- 預設比對不區分大小寫,會修剪空白字元並搜尋子字串。例如,text=Log
比對<button>Log in</button>
。await page.locator('text=Log in').click();
-
text="Log in"
- 文字主體可以用單引號或雙引號轉義,以搜尋在修剪空白字元後具有確切內容的文字節點。例如,
text="Log"
不會比對<button>Log in</button>
,因為<button>
包含單一文字節點"Log in"
,這不等於"Log"
。然而,text="Log"
會比對<button> Log <span>in</span></button>
,因為<button>
包含文字節點" Log "
。這種確切模式意味著區分大小寫的比對,所以text="Download"
不會比對<button>download</button>
。引號主體遵循通常的轉義規則,例如使用
\"
在雙引號字串中轉義雙引號:text="foo\"bar"
。await page.locator('text="Log in"').click();
-
/Log\s*in/i
- 主體可以是包在/
符號中的 JavaScript 式正規表示式。例如,text=/Log\s*in/i
比對<button>Login</button>
和<button>log IN</button>
。await page.locator('text=/Log\\s*in/i').click();
以引號("
或 '
)開始和結束的字串選擇器會被視為舊版文字定位器。例如,"Log in"
在內部會轉換為 text="Log in"
。
比對總是會正規化空白字元。例如,它將多個空格轉為一個,將換行符轉為空格,並忽略前導和尾隨的空白字元。
類型為 button
和 submit
的輸入元素會依其 value
而非文字內容進行比對。例如,text=Log in
比對 <input type=button value="Log in">
。
id、data-testid、data-test-id、data-test 選擇器
我們建議改為依測試 ID 定位。
Playwright 支援使用特定屬性選取元素的簡寫。目前只支援以下屬性:
id
data-testid
data-test-id
data-test
// 填寫 id 為 "username" 的輸入欄位
await page.locator('id=username').fill('value');
// 點擊 data-test-id 為 "submit" 的元素
await page.locator('data-test-id=submit').click();
屬性選擇器不是 CSS 選擇器,因此不支援任何 CSS 特定的語法,如 :enabled
。對於更多功能,請使用適當的 css 選擇器,例如 css=[data-test="login"]:enabled
。
選擇器鏈結
我們建議改為鏈結定位器。
定義為 engine=body
或簡寫形式的選擇器可以與 >>
符號結合,例如 selector1 >> selector2 >> selectors3
。當選擇器被鏈結時,下一個選擇器會相對於前一個的結果進行查詢。
例如,
css=article >> css=.bar > .baz >> css=span[attr=value]
等同於
document
.querySelector('article')
.querySelector('.bar > .baz')
.querySelector('span[attr=value]');
如果選擇器需要在主體中包含 >>
,應該在字串內轉義以免與鏈結分隔符混淆,例如 text="some >> text"
。
中間比對
我們建議依另一個定位器篩選來定位包含其他元素的元素。
預設情況下,鏈結選擇器會解析為由最後一個選擇器查詢的元素。選擇器可以前綴 *
來擷取由中間選擇器查詢的元素。
例如,css=article >> text=Hello
擷取包含文字 Hello
的元素,而 *css=article >> text=Hello
(請注意 *
)擷取包含某個含有文字 Hello
的元素的 article
元素。