Skip to main content

其他定位器

簡介

note

查看主要的定位器指南,了解最常見和推薦的定位器。

除了推薦的定位器如 Page.GetByRole()Page.GetByText() 外,Playwright 支援本指南中描述的各種其他定位器。

CSS 定位器

note

我們建議優先考慮使用 使用者可見的定位器,例如文字或可訪問的角色,而不是使用與實作綁定的 CSS,因為當頁面變更時可能會失效。

Playwright 可以透過 CSS 選擇器定位元素。

await page.Locator("css=button").ClickAsync();

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\")").ClickAsync();
    // 正確,只匹配 <article> 元素
    await page.Locator("article:has-text(\"Playwright\")").ClickAsync();
  • #nav-bar :text("Home") - :text() 偽類匹配包含指定文本的最小元素。匹配不區分大小寫,會去除空白並搜尋子字串。

    例如,這將在 #nav-bar 元素內找到包含文本 "Home" 的元素:

    await page.Locator("#nav-bar :text('Home')").ClickAsync();
  • #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-like regex 相匹配的文本內容的最小元素。

    例如,:text-matches("Log\s*in", "i") 匹配 <button>Login</button><button>log IN</button>

note

文字匹配總是會正規化空白。例如,它會將多個空格變成一個空格,將換行符變成空格,並忽略前後的空白。

note

Input 元素類型 buttonsubmit 是通過它們的 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").ClickAsync();
  • 這將只會找到第二個按鈕,因為它是可見的,然後點擊它。

    await page.Locator("button:visible").ClickAsync();

CSS: 包含其他元素的元素

:has() 偽類別是一個實驗性的 CSS 偽類別。如果任何作為參數傳遞的選擇器相對於給定元素的 :scope 匹配至少一個元素,它會返回一個元素。

以下程式碼片段返回具有 <div class=promo><article> 元素的文本內容。

await page.Locator("article:has(div.promo)").TextContentAsync();

CSS: 符合其中一個條件的元素

逗號分隔的 CSS 選擇器列表將匹配該列表中任一選擇器可以選擇的所有元素。

// Clicks a <button> that has either a "Log in" or "Sign in" text.
await page.Locator("button:has-text(\"Log in\"), button:has-text(\"Sign in\")").ClickAsync();

:is() 偽類別是一個實驗性 CSS 偽類別,可能對於指定元素上的額外條件列表很有用。

CSS: 根據佈局匹配元素

note

根據版面配置進行匹配可能會產生意想不到的結果。例如,當版面配置改變一個像素時,可能會匹配到不同的元素。

有時候,當目標元素缺乏明顯特徵時,很難想出一個好的選擇器。在這種情況下,使用 Playwright 佈局 CSS 偽類可能會有所幫助。這些可以與常規 CSS 結合,以精確定位多個選擇中的一個。

例如,input:right-of(:text("Password")) 匹配位於 "Password" 文字右側的輸入欄位 - 當頁面有多個難以區分的輸入欄位時非常有用。

請注意,佈局偽類別除了其他東西外(如 input)也很有用。如果單獨使用佈局偽類別,如 :right-of(:text("Password")),很可能你會得到的不是你要找的 input,而是文本和目標 input 之間的一些空元素。

佈局偽類別使用 bounding client rect 計算元素的距離和相對位置。

  • :right-of(div > button) - 匹配在任何符合內部選擇器的元素右側的元素,在任何垂直位置。
  • :left-of(div > button) - 匹配在任何符合內部選擇器的元素左側的元素,在任何垂直位置。
  • :above(div > button) - 匹配在任何符合內部選擇器的元素上方的元素,在任何水平位置。
  • :below(div > button) - 匹配在任何符合內部選擇器的元素下方的元素,在任何水平位置。
  • :near(div > button) - 匹配在任何符合內部選擇器的元素附近(50 個 CSS 像素內)的元素。

請注意,結果匹配是根據它們與錨點元素的距離排序的,因此您可以使用 Locator.First 來選擇最近的一個。如果您有類似元素的列表,其中最近的顯然是正確的,這才有用。然而,在其他情況下使用 Locator.First 很可能不會如預期般工作——它不會定位到您正在搜索的元素,而是一些碰巧最近的其他元素,如隨機的空 <div>,或是滾動出來且當前不可見的元素。

// Fill an input to the right of "Username".
await page.Locator("input:right-of(:text(\"Username\"))").FillAsync("value");

// Click a button near the promo card.
await page.Locator("button:near(.promo-card)").ClickAsync();

// Click the radio input in the list closest to the "Label 3".
await page.Locator("[type=radio]:left-of(:text(\"Label 3\"))").First.ClickAsync();

所有布局偽類別支持可選的最大像素距離作為最後一個參數。例如 button:near(:text("Username"), 120) 匹配距離文本為 "Username" 的元素最多 120 個 CSS 像素的按鈕。

CSS: 從查詢結果中選擇第 n 個匹配

note

通常可以通過某些屬性或文本內容來區分元素,這樣對頁面變更更具彈性。

有時頁面包含許多相似的元素,很難選擇特定的一個。例如:

<section> <button>Buy</button> </section>
<article><div> <button>Buy</button> </div></article>
<div><div> <button>Buy</button> </div></div>

在這種情況下,:nth-match(:text("Buy"), 3) 將會選擇上面程式碼片段中的第三個按鈕。請注意,索引是從 1 開始的。

// Click the third "Buy" button
await page.Locator(":nth-match(:text('Buy'), 3)").ClickAsync();

:nth-match() 也可用於等待直到出現指定數量的元素,使用 Locator.WaitForAsync()

// Wait until all three buttons are visible
await page.Locator(":nth-match(:text('Buy'), 3)").WaitForAsync();
note

:nth-child() 不同,元素不必是兄弟元素,它們可以在頁面的任何位置。在上面的程式碼片段中,所有三個按鈕都符合 :text("Buy") 選擇器,而 :nth-match() 選擇了第三個按鈕。

第 N 個元素定位器

您可以使用 nth= 定位器傳遞從零開始的索引來縮小查詢到第 n 個匹配項。

// Click first button
await page.Locator("button").Locator("nth=0").ClickAsync();

// Click last button
await page.Locator("button").Locator("nth=-1").ClickAsync();

父元素定位器

當你需要針對某個元素的父元素時,大多數情況下你應該通過子定位器 Locator.Filter()。例如,考慮以下 DOM 結構:

<li><label>Hello</label></li>
<li><label>World</label></li>

如果你想針對帶有文字 "Hello" 的標籤的父 <li>,使用 Locator.Filter() 最有效:

var child = page.GetByText("Hello");
var parent = page.GetByRole(AriaRole.Listitem).Filter(new () { Has = child });

或者,如果您無法找到適合的父元素定位器,請使用 xpath=..。請注意,這種方法不太可靠,因為對 DOM 結構的任何更改都會破壞您的測試。盡可能偏好使用 Locator.Filter()

var parent = page.GetByText("Hello").Locator("xpath=..");

React locator

note

React 定位器是實驗性的,並以 _ 為前綴。其功能未來可能會改變。

React locator 允許通過元件名稱和屬性值來查找元素。語法與 CSS attribute selectors 非常相似,並支援所有 CSS 屬性選擇器運算子。

在 React 定位器中,元件名稱以 CamelCase 轉錄。

await page.Locator("_react=BookItem").ClickAsync();

更多範例:

  • 根據 component 匹配: _react=BookItem
  • 根據 component 和 精確屬性值 匹配,區分大小寫: _react=BookItem[author = "Steven King"]
  • 僅根據屬性值匹配,不區分大小寫: _react=[author = "steven king" i]
  • 根據 component 和 真值屬性值 匹配: _react=MyButton[enabled]
  • 根據 component 和 布林值 匹配: _react=MyButton[enabled = false]
  • 根據屬性 值子串 匹配: _react=[author *= "King"]
  • 根據 component 和 多個屬性 匹配: _react=BookItem[author *= "king" i][year = 1990]
  • 根據 嵌套 屬性值匹配: _react=[some.nested.value = 12]
  • 根據 component 和屬性值 前綴 匹配: _react=BookItem[author ^= "Steven"]
  • 根據 component 和屬性值 後綴 匹配: _react=BookItem[author $= "Steven"]
  • 根據 component 和 key 匹配: _react=BookItem[key = '2']
  • 根據屬性值 正則表達式 匹配: _react=[author = /Steven(\\s+King)?/i]

要在樹中找到 React 元素名稱,請使用 React DevTools

note

React locator 支援 React 15 及以上版本。

note

React 定位器,以及 React DevTools,僅適用於 未壓縮 的應用程式建構。

Vue 定位器

note

Vue 定位器是實驗性的,並以 _ 為前綴。該功能未來可能會改變。

Vue 定位器允許通過元件名稱和屬性值來查找元素。語法與 CSS 屬性選擇器 非常相似,並且支持所有 CSS 屬性選擇器運算符。

在 Vue 定位器中,元件名稱以 kebab-case 轉錄。

await page.Locator("_vue=book-item").ClickAsync();

更多範例:

  • 根據 component 匹配: _vue=book-item
  • 根據 component 和 精確屬性值 匹配,區分大小寫: _vue=book-item[author = "Steven King"]
  • 僅根據屬性值匹配,不區分大小寫: _vue=[author = "steven king" i]
  • 根據 component 和 真值屬性值 匹配: _vue=my-button[enabled]
  • 根據 component 和 布林值 匹配: _vue=my-button[enabled = false]
  • 根據屬性 值子字串 匹配: _vue=[author *= "King"]
  • 根據 component 和 多個屬性 匹配: _vue=book-item[author *= "king" i][year = 1990]
  • 根據 嵌套 屬性值匹配: _vue=[some.nested.value = 12]
  • 根據 component 和屬性值 前綴 匹配: _vue=book-item[author ^= "Steven"]
  • 根據 component 和屬性值 後綴 匹配: _vue=book-item[author $= "Steven"]
  • 根據屬性值 正則表達式 匹配: _vue=[author = /Steven(\\s+King)?/i]

要在樹中找到 Vue 元素名稱,請使用 Vue DevTools

note

Vue 定位器支援 Vue2 及以上版本。

note

Vue 定位器,以及 Vue DevTools,僅適用於 未壓縮 的應用程式建構。

XPath 定位器

warning

我們建議優先考慮使用用戶可見的定位器,例如文本或可訪問的角色,而不是使用與實現綁定並且在頁面更改時容易中斷的 XPath。

XPath 定位器相當於呼叫 Document.evaluate

await page.Locator("xpath=//button").ClickAsync();
note

任何以 //.. 開頭的選擇器字串都被認為是 xpath 選擇器。例如,Playwright 將 '//html/body' 轉換為 'xpath=//html/body'

note

XPath 無法穿透 shadow roots。

XPath union

管道運算子(|)可用於在 XPath 中指定多個選擇器。它將匹配該列表中任一選擇器可以選擇的所有元素。

// Waits for either confirmation dialog or load spinner.
await page.Locator("//span[contains(@class, 'spinner__loading')]|//div[@id='confirmation']").WaitForAsync();

標籤到表單控制重新定位

warning

我們建議通過標籤文字定位 而不是依賴標籤到控制的重新定位。

在 Playwright 中,針對性的輸入操作會自動區分標籤和控制項,因此您可以針對標籤來對相關的控制項執行操作。

例如,考慮以下 DOM 結構: <label for="password">Password:</label><input id="password" type="password">。你可以使用 Page.GetByText() 根據其 "Password" 文字來定位標籤。然而,以下操作將在 input 上執行而不是在標籤上:

// Fill the input by targeting the label.
await page.GetByText("Password").FillAsync("secret");

然而,其他方法將針對標籤本身,例如 Expect(Locator).ToHaveTextAsync() 會斷言標籤的文字內容,而不是輸入欄位。

// Fill the input by targeting the label.
await Expect(Page.Locator("label")).ToHaveTextAsync("Password");

舊版文字定位器

warning

我們推薦使用現代的 文字定位器

傳統文本定位器匹配包含傳遞文本的元素。

await page.Locator("text=Log in").ClickAsync();

舊版文字定位器有幾種變體:

  • text=Log in - 預設匹配不區分大小寫,會去除空白並搜尋子字串。例如,text=Log 匹配 <button>Log in</button>

    await page.Locator("text=Log in").ClickAsync();
  • 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'").ClickAsync();
  • /Log\s*in/i - 內容可以是用 / 符號包裹的 JavaScript-like regex。例如,text=/Log\s*in/i 匹配 <button>Login</button><button>log IN</button>

    await page.Locator("text=/Log\\s*in/i").ClickAsync();
note

以引號("')開始和結束的字串選擇器被假設為舊版文字定位器。例如,"Log in" 會在內部轉換為 text="Log in"

note

匹配總是會正規化空白。例如,它會將多個空格變成一個空格,將換行符變成空格,並忽略前導和尾隨的空白。

note

Input 元素的類型 buttonsubmit 是通過它們的 value 而不是文本內容來匹配的。例如,text=Log in 匹配 <input type=button value="Log in">

id, data-testid, data-test-id, data-test 選擇器

warning

我們建議改用透過測試 ID 定位

Playwright 支援使用某些屬性的簡寫來選擇元素。目前,只支援以下屬性:

  • id
  • data-testid
  • data-test-id
  • data-test
// Fill an input with the id "username"
await page.Locator("id=username").FillAsync("value");

// Click an element with data-test-id "submit"
await page.Locator("data-test-id=submit").ClickAsync();
note

屬性選擇器不是 CSS 選擇器,因此任何特定於 CSS 的內容(如 :enabled)都不受支援。要獲取更多功能,請使用適當的 css 選擇器,例如 css=[data-test="login"]:enabled

鏈接選擇器

warning

我們建議改用鏈接定位器

選擇器定義為 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"

中階匹配

warning

我們建議透過另一個定位器過濾來定位包含其他元素的元素。

預設情況下,鏈式選擇器會解析為由最後一個選擇器查詢的元素。選擇器可以用 * 作為前綴,以捕獲由中間選擇器查詢的元素。

例如,css=article >> text=Hello 會捕捉到包含文字 Hello 的元素,而 *css=article >> text=Hello(注意 *)會捕捉到包含某個包含文字 Hello 元素的 article 元素。