我從事前端開發的時間足夠長,多年來看到了一種趨勢:年輕的開發人員在不了解其歷史背景的情況下使用新的程式設計範式。 當然,不知道某件事是完全可以理解的。網路是一個非常大的地方,擁有各種各樣的技能和專業,我們並不總是知道我們不知道什麼。這個領域的學習是一個持續的旅程,而不是一次發生就結束的事情。 舉個例子:我的團隊中有人詢問是否可以判斷使用者是否離開了 UI 中的特定標籤。我指出了 JavaScript 的 beforeunload 事件。但那些以前解決過這個問題的人知道這是可能的,因為他們收到過有關其他網站上未保存資料的警報,其中 beforeunload 是一個典型的用例。我還向我的同事指出了 pageHide 和visibilityChange 事件,以便更好地衡量。 我怎麼知道的?因為它是在另一個專案中出現的,而不是因為我最初學習 JavaScript 時研究過它。 事實是,現代前端框架正站在先前科技巨頭的肩膀上。他們抽象開發實踐,通常是為了獲得更好的開發人員體驗,從而減少甚至消除了解或接觸傳統上每個人可能都應該了解的基本前端概念的需要。 考慮 CSS 物件模型 (CSSOM)。您可能認為任何從事 CSS 和 JavaScript 工作的人都擁有豐富的 CSSOM 實務經驗,但情況並非總是如此。 我工作過的電子商務網站有一個 React 項目,我們需要為目前選擇的付款提供者載入樣式表。問題在於,當僅在特定頁面上真正需要樣式表時,樣式表會在每個頁面上載入。負責實現這一目標的開發人員從未動態載入過樣式表。同樣,當 React 抽像出您可能已經達到的傳統方法時,這是完全可以理解的。 CSSOM 可能不是您日常工作中需要的東西。但您可能需要在某個時刻與其進行交互,即使是在一次性的情況下。 這些經歷啟發了我寫這篇文章。有許多現有的 Web 功能和技術是您在日常工作中可能永遠不會直接接觸的。也許您對 Web 開發相當陌生,只是不知道它們,因為您沉浸在特定框架的抽像中,不需要您深入了解,甚至根本不了解。 我刻意談論 XML,我們很多人都知道 XML 是一種古老的語言,與 HTML 沒有完全不同。 我提出這個問題是因為最近 WHATWG 的討論建議應該從瀏覽器中刪除 XML 堆疊的很大一部分(稱為 XSLT 程式設計)。這正是我們多年來擁有的那種較舊的現有技術,可以用於像我的團隊所處的 CSSOM 情況這樣實用的事情。 您以前使用過 XSLT 嗎?讓我們看看我們是否嚴重依賴這種舊技術並在 XML 上下文之外利用其功能來解決當今的現實問題。 XPath:中央 API 最重要的 XML 技術(也許除了直接的 XML 視角之外也是最有用的)是 XPath,它是一種查詢語言,可讓您透過一個根元素來尋找標記樹中的任何節點或屬性。我對XSLT有個人感情,但這也依賴XPath,在重要性排名中必須拋開個人感情。 刪除 XSLT 的論點沒有提及 XPath,所以我認為它仍然是允許的。這很好,因為 XPath 是這套技術中最核心、最重要的 API,尤其是在嘗試尋找正常 XML 使用之外的內容時。這很重要,因為雖然 CSS 選擇器可用於尋找頁面中的大多數元素,但它們無法找到全部元素。此外,CSS 選擇器不能用於根據元素在 DOM 中的目前位置尋找元素。 XPath 可以。 現在,閱讀本文的有些人可能了解 XPath,有些人可能不了解。 XPath 是一個相當大的技術領域,我無法在像這樣的文章中真正教授所有基礎知識並向您展示如何使用它來完成很酷的事情。我實際上嘗試過寫那篇文章,但 Smashing 雜誌的出版物平均字數不會超過 5,000 字。我已經超過了2000字,基礎知識只完成了一半。 因此,我將開始使用 XPath 做一些很酷的事情,並為您提供一些鏈接,如果您發現這些內容有趣,您可以使用它們來了解基礎知識。 結合 XPath 和 CSS 在查詢元素時,XPath 可以做很多 CSS 選擇器不能做的事情。但 CSS 選擇器還可以做一些 XPath 不能做的事情,也就是以類別名稱查詢元素。
CSS X路徑 .myClass /*[包含(@class, "myClass")]
在此範例中,CSS 查詢包含 .myClass 類別名稱的元素。同時,XPath 範例查詢包含字串「myClass」的屬性類別的元素。換句話說,它選擇任何屬性中帶有 myClass 的元素,包括類別名為 .myClass 的元素,以及字串中帶有「myClass」的元素,例如 .myClass2。從這個意義上來說,XPath 更廣泛。 所以,不。我並不是建議我們應該拋棄 CSS 並開始透過 XPath 選擇所有元素。這不是重點。 關鍵是 XPath 可以做 CSS 不能做的事情,而且仍然非常有用,儘管它是瀏覽器堆疊中的一項較舊的技術,並且乍一看似乎並不明顯。 讓我們一起使用這兩種技術,不僅因為我們可以,而且因為我們將在過程中了解一些有關 XPath 的知識,使其成為您堆疊中的另一個工具 - 您可能不知道它一直存在! 問題在於 JavaScript 的 document.evaluate 方法和我們與 JavaScript 的 CSS API 一起使用的各種查詢選擇器方法不相容。 我已經製作了一個相容的查詢 API 來幫助我們開始,但不可否認的是,我沒有對此進行太多思考,因為它與我們在這裡所做的事情背道而馳。這是一個可重複使用查詢建構函數的相當簡單的工作範例: 請參閱 Bryan Rasmussen 的 Pen queryXPath [forked]。 我在文檔物件上新增了兩個方法:queryCSSSelectors(本質上是 querySelectorAll)和 queryXPaths。這兩個都回傳一個 queryResults 物件:
{ 查詢類型:節點|字串|數量 |布林值, results: any[] // html 元素、xml 元素、字串、數字、布林值、 queryCSSSelectors:(查詢:字串,修改:布林值)=> queryResults, queryXpaths:(查詢:字串,修改:布林值)=> queryResults }
當然,只要結果陣列是節點類型,queryCSSSelectors 和 queryXpaths 函數就會對結果陣列中的元素執行您提供的查詢。否則,它將傳回一個帶有空數組和一種節點類型的 queryResult。如果 amend 屬性設為 true,函數將變更自己的 queryResults。 在任何情況下都不應在生產環境中使用它。我這樣做純粹是為了示範同時使用兩個查詢 API 的各種效果。 查詢範例 我想展示一些不同 XPath 查詢的範例,這些範例展示了它們可以執行的一些強大功能以及如何使用它們來代替其他方法。 第一個範例是 //li/text()。這會查詢所有 li 元素並傳回它們的文字節點。因此,如果我們要查詢以下 HTML:
- 一
- 兩個
- 三個
....這是回傳的內容:
{“queryType”:“xpathEvaluate”,“結果”:[“一”,“二”,“三”],“resultType”:“字串”}
換句話說,我們得到以下數組:[“一”,“二”,“三”]。 通常,您會查詢 li 元素來取得該內容,將該查詢的結果轉換為數組,映射該數組,然後返回每個元素的文字節點。但我們可以使用 XPath 更簡潔地做到這一點: document.queryXPaths("//li/text()").results.
請注意,獲取文字節點的方法是使用 text(),它看起來像函數簽名 - 確實如此。它傳回元素的文字節點。在我們的範例中,標記中有三個 li 元素,每個元素都包含文字(「一」、「二」和「三」)。
讓我們再來看一個 text() 查詢的範例。假設這是我們的標記:
讓我們來寫一個傳回 href 屬性值的查詢: document.queryXPaths("//a[text() = '登入']/@href").results.
這是對目前文件的 XPath 查詢,就像上一個範例一樣,但這次我們傳回包含文字「Sign In」的連結(元素)的 href 屬性。實際回傳的結果是[“/login.html”]。 XPath 函數概述 有許多 XPath 函數,您可能不熟悉它們。我認為有幾個值得了解的內容,包括以下內容:
starts-with如果文字以特定的其他文字範例開頭,則如果 href 屬性以 http: 開頭,starts-with(@href, 'http:') 將傳回 true。 contains如果文字包含特定的其他文字範例,且文字節點中的任意位置包含單字“Smashing Magazine”,則 contains(text(), "Smashing Magazine") 會傳回 true。 count 傳回查詢有多少符合項目的計數。例如,count(//*[starts-with(@href, 'http:']) 傳回上下文節點中有多少連結的計數,其中包含帶有 href 屬性的元素,該屬性包含以 http: 開頭的文字。 substring 與 JavaScript 子字串類似,只不過您將字串作為參數傳遞。例如,substring("my text", 2, 4) 傳回「y t」。 substring-before 傳回一個字串在另一個字串之前的部分。例如,substing-before("my text", " ") 傳回「my」。類似地, substring-before("hi","bye") 傳回一個空字串。 substring-after 傳回字串中位於另一個字串之後的部分。例如,substing-after("my text", " ") 傳回「text」。類似地,substring-after("hi","bye") 傳回一個空字串。 Normalize-space 傳回帶有標準化空白的參數字串,方法是移除前導和尾隨空白並將空白字元序列替換為單一空格。 如果參數為 false,則 not 傳回布林值 true,否則傳回 false。 true 傳回布林值 true。 false傳回布林值 false。 concat與 JavaScript concat 相同,只是您不將其作為字串上的方法運行。相反,您輸入要連接的所有字串。 string-length這與 JavaScript string-length 不同,而是傳回作為參數給出的字串的長度。 translateThis 接受一個字串並將第二個參數更改為第三個參數。例如,translate("abcdef", "abc", "XYZ") 輸出 XYZdef。
除了這些特定的 XPath 函數之外,還有許多其他函數的工作方式與 JavaScript 對應函數(或基本上任何程式語言中的對應函數)相同,您可能也會發現這些函數很有用,例如下限、上限、圓、總和等。 以下演示說明了每個功能: 請參閱 Bryan Rasmussen 的 Pen XPath 數值函數 [forked]。 請注意,與大多數字串操作函數一樣,許多數字函數採用單一輸入。當然,這是因為它們應該用於查詢,如最後一個 XPath 範例所示: //li[地板(text()) > 250]/@val
如果像大多數範例一樣使用它們,您最終將在與路徑匹配的第一個節點上運行它。 還有一些型別轉換函數你可能應該避免,因為 JavaScript 已經有自己的型別轉換問題。但有時您可能希望將字串轉換為數字,以便將其與其他數字進行檢查。 設定某種類型的函數有布林值、數字、字串和節點。這些是重要的 XPath 資料類型。 正如您可能想像的那樣,這些函數中的大多數都可以用於非 DOM 節點的資料類型。例如,substring-after 接受我們已經介紹過的字串,但它也可以是來自 href 屬性的字串。它也可以只是一個字串:
const testSubstringAfter = document.queryXPaths("substring-after('hello world',' ')");
顯然,這個例子將會傳回結果數組[“world”]。為了實際演示這一點,我使用函數針對非 DOM 節點建立了一個演示頁面: 請參閱 Bryan Rasmussen 的 Pen queryXPath [forked]。 您應該注意到翻譯函數的令人驚訝的方面,即如果第二個參數(即要翻譯的字符列表)中有一個字符並且沒有要翻譯的匹配字符,則該字符將從輸出中刪除。 因此,這個:
翻譯('你好,我的名字是伊尼戈蒙托亞,你殺了我的父親,準備去死','abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,','*')
...字串結果,包括空格: [“ * * ** ”]
這意味著字母「a」將被翻譯為星號 (*),但在給定目標字串的情況下,所有其他沒有翻譯的字元將被完全刪除。空白是我們剩下的全部位於翻譯後的“a”字元之間。 再說一次,這個查詢:
翻譯('你好,我的名字是伊尼戈蒙托亞,你殺了我的父親,準備去死','abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,','********************************************************')”)
……沒有問題並輸出如下所示的結果:
“********************************************************************************”
您可能會發現 JavaScript 中沒有簡單的方法可以完全完成 XPath 翻譯函數的功能,儘管對於許多用例,使用正規表示式的 ReplaceAll 可以處理它。 您可以使用我演示的相同方法,但如果您只想翻譯字串,那麼這不是最佳選擇。以下示範包裝了 XPath 的翻譯函數以提供 JavaScript 版本: 請參閱 Bryan Rasmussen 的筆翻譯函數 [forked]。 你可能會在哪裡使用這樣的東西?考慮具有三位偏移量的凱撒密碼加密(例如,公元前 48 年的頂級加密):
translate("凱撒正計劃渡過盧比孔河!", “ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz”, “XYZABCDEFGHIJKLMNOPQRSTUVWxyzabcdefghijklmnopqrstuvw”)
輸入文字“凱撒正計劃渡過盧比孔河! 」結果是「Zxbpxo fp mixkkfkd ql zolpp qeb Oryfzlk! 」 為了給出不同可能性的另一個簡單示例,我創建了一個金屬函數,它接受字串輸入並使用翻譯函數返回文本,包括所有帶有變音符號的字元。 請參閱 Bryan Rasmussen 的 Pen 金屬函數 [forked]。
常量金屬 = (str) => { returntranslate(str, "AOUaou","äÖÜäöü"); }
並且,如果給出文本“Motley Cruerules, rock on dudes!”,則返回“Mötley Crüe rüles, röck ön düdes!” 顯然,人們可能會對該函數進行各種模仿。如果您是這樣,這篇 TVTropes 文章應該能為您提供充足的靈感。 將 CSS 與 XPath 結合使用 請記住我們將 CSS 選擇器與 XPath 一起使用的主要原因:CSS 非常了解類別是什麼,而使用 XPath 可以做的最好的事情就是類別屬性的字串比較。這在大多數情況下都有效。 但是,如果您遇到這樣的情況,例如,有人創建了名為 .primaryLinks 和 .primaryLinks2 的類,而您使用 XPath 來獲取 .primaryLinks 類,那麼您可能會遇到問題。只要不存在這樣愚蠢的事情,您就可能會使用 XPath。但我很遺憾地告訴大家,我曾在人們做這類愚蠢事情的地方工作過。 這是另一個結合使用 CSS 和 XPath 的示範。它顯示了當我們使用程式碼在不是文檔節點的上下文節點上運行 XPath 時會發生什麼。 請參閱 Bryan Rasmussen 的 Pen css 和 xpath [forked]。 CSS 查詢是 .latedarticles a,它取得分配了 .latedarticles 類別的 div 中的兩個 a 元素。 之後是三個「壞」查詢,也就是說,在使用這些元素作為上下文節點運行時,查詢沒有執行我們希望它們執行的操作。 我可以解釋為什麼他們的行為與你想像的不同。有問題的三個錯誤查詢是:
//text():傳回文檔中的所有文字。 //a/text():傳回文件中連結內的所有文字。 ./a/text():不回傳結果。
產生這些結果的原因是,雖然您的上下文是從 CSS 查詢傳回的元素,但 // 違反了整個文件。這就是 XPath 的優點; CSS 不能從一個節點往上到達祖先,然後到達該祖先的兄弟節點,然後往下走到該兄弟節點的後代。但 XPath 可以。 同時,./查詢目前節點的子節點,其中點(.)代表目前節點,正斜線(/)代表前往某個子節點-它是屬性、元素還是文字由路徑的下一部分決定。但是 CSS 查詢沒有選擇子 a 元素,因此該查詢也不會傳回任何內容。 最後一個演示中有三個很好的查詢:
.//文本(), ./文本(), 標準化空間(./text())。
規範化空間查詢演示了 XPath 函數的用法,但也修復了其他查詢中包含的問題。 HTML 的結構如下:
使用 Selenium WebDriver 自動化您的功能測試
查詢傳回文字節點開頭和結尾的換行符,並且標準化空間刪除了這個。 使用任何傳回布林值以外的值且輸入 XPath 的 XPath 函數適用於其他函數。以下演示顯示了許多範例: 請參閱 Bryan Rasmussen 的 Pen xpath 函數範例 [forked]。 第一個範例顯示了您應該注意的問題。具體來說,程式碼如下:
document.queryXPaths("substring-after(//a/@href,'https://')");
....回傳一個字串:
“www.smashingmagazine.com/2018/04/feature-testing-selenium-webdriver/”
這是有道理的,對吧?這些函數不會傳回數組,而是傳回單一字串或單一數字。在具有多個結果的任何地方運行函數僅傳回第一個結果。 第二個結果顯示了我們真正想要的:
document.queryCSSSelectors("a").queryXPaths("substring-after(./@href,'https://')");
它傳回兩個字串的陣列:
[“www.smashingmagazine.com/2018/04/feature-testing-selenium-webdriver/”,”www.smashingmagazine.com/2022/11/automated-test-results-improve-accessibility/”]
XPath 函數可以像 JavaScript 中的函數一樣巢狀。因此,如果我們知道 Smashing Magazine URL 結構,我們可以執行以下操作(建議使用範本文字): `翻譯( 子串( substring-after(./@href, ‘www.smashingmagazine.com/’) ,9), '/','')`
這變得有點太複雜了,需要註釋來描述它的作用:從 www.smashingmagazine.com/ 之後的 href 屬性中獲取所有 URL,刪除前九個字符,然後將正斜杠 (/) 字符轉換為空,以便擺脫結尾的正斜杠。 結果數組:
[“功能測試selenium-webdriver”,“自動測試結果改進可訪問性”]
更多 XPath 用例 XPath 在測試中確實可以大放異彩。原因不難看出,因為 XPath 可以用於從 DOM 中的任何位置取得 DOM 中的每個元素,而 CSS 則不能。 您不能指望 CSS 類別在許多現代構建系統中保持一致,但是使用 XPath,我們能夠對元素的文本內容進行更穩健的匹配,而不管 DOM 結構如何變化。 人們已經對允許您進行彈性 XPath 測試的技術進行了研究。沒有什麼比僅僅因為 CSS 選擇器因某些內容被重新命名或刪除而不再工作而導致測試失敗並失敗更糟糕的了。 XPath 在多定位器擷取方面也非常出色。使用 XPath 查詢來符合元素的方法有不只一種。 CSS 也是如此。但是 XPath 查詢可以以更有針對性的方式深入了解內容,從而限制返回的內容,從而使您能夠在可能存在多個可能匹配的情況下找到特定的匹配。 例如,我們可以使用 XPath 傳回一個特定的 h2 元素,該元素包含在緊接在同級 div 後面的 div 中,而該同級 div 又包含一個帶有 data-testID="leader" 屬性的子圖像元素:
不明白這個標題
也不要得到這個標題
領導者圖像的標題
這是查詢: 文檔.queryXPaths(` //div[ 以下兄弟姊妹::div[1] /img[@data-testID='領導者'] ] /h2/ 文字() `);
讓我們看一下演示,看看這一切是如何結合在一起的: 請參閱 Bryan Rasmussen 的 Pen Complex H2 查詢 [forked]。 所以,是的。使用 XPath 的測試中的任何元素都有許多可能的路徑。 XSLT 1.0 棄用 我之前提到 Chrome 團隊計劃從瀏覽器中刪除 XSLT 1.0 支援。這很重要,因為 XSLT 1.0 使用以 XML 為中心的程式設計進行文件轉換,而文件轉換又依賴 XPath 1.0(大多數瀏覽器都採用 XPath 1.0)。 當這種情況發生時,我們將失去 XPath 的關鍵組件。但鑑於 XPath 確實非常適合編寫測試,我發現 XPath 整體上不太可能很快消失。 也就是說,我注意到人們會對某個功能被刪除時感興趣。在 XSLT 1.0 被棄用的情況下確實如此。駭客新聞上正在進行一場完整的討論,充滿了反對棄用的論點。這篇文章本身就是使用 XSLT 創建部落格框架的一個很好的例子。你您可以自己閱讀討論,但它涉及如何使用 JavaScript 作為 XLST 的填充程式來處理此類情況。 我還看到建議瀏覽器應該使用 SaxonJS,它是 JavaScript 的 Saxon XSLT、XQUERY 和 XPath 引擎的連接埠。這是一個有趣的想法,特別是 Saxon-JS 實作了這些規範的當前版本,而沒有瀏覽器實現任何高於 1.0 的 XPath 或 XSLT 版本,也沒有瀏覽器實作 XQuery。 我聯繫了 Saxonica 的 Norm Tovey-Walsh,該公司是 SaxonJS 和其他版本 Saxon 引擎背後的公司。他說: 「如果任何瀏覽器供應商有興趣將 SaxonJS 作為將現代 XML 技術整合到瀏覽器中的起點,我們將很高興與他們討論。」— Norm Tovey-Walsh
但也補充道: 「如果有人認為採用當前形式的 SaxonJS 並將其原封不動地放入瀏覽器構建中將是理想的方法,我會感到非常驚訝。瀏覽器供應商,本質上是他們構建瀏覽器的事實,可以比我們‘從外部’更深層次地實現集成。」—— Norm Tovey-Walsh
值得注意的是,Tovey-Walsh 的評論是在 XSLT 棄用聲明發布前一周左右發表的。 結論 我還可以繼續說下去。但我希望這已經展示了 XPath 的強大功能,並為您提供了大量範例來演示如何使用它來實現偉大的目標。它是瀏覽器堆疊中舊技術的完美範例,即使您從未知道它的存在或從未考慮過使用它,但如今仍然具有大量實用性。 進一步閱讀
「利用自然語言增強自動化 Web 測試的彈性」(ACM 數位圖書館)作者:Maroun Ayli、Youssef Bakouny、Nader Jalloul 和 Rima Kilany 本文提供了許多用於編寫彈性測試的 XPath 範例。 XPath (MDN) 如果您想要詳細了解 XPath 如何運作的技術解釋,這是一個很好的起點。 XPath 教學 (ZVON) 我發現教學對我自己的學習最有幫助,這要歸功於大量的範例和清晰的解釋。 XPather 這個互動式工具可讓您直接使用程式碼。