การใช้งาน clojure บริสุทธิ์ของโปรโตคอล WebDriver ใช้ห้องสมุดนั้นเพื่อทำให้เบราว์เซอร์ทดสอบพฤติกรรมส่วนหน้าของคุณจำลองการกระทำของมนุษย์หรืออะไรก็ตามที่คุณต้องการ
มันได้รับการตั้งชื่อตาม Etaoin Shrdlu - เครื่องพิมพ์ที่มีชีวิตอยู่หลังจากมีการผลิตความลึกลับ
ตั้งแต่ 0.4.0
อินสแตนซ์ของไดรเวอร์เป็น แผนที่ แต่ไม่ใช่อะตอม เหมือนที่เคยเป็น มันเป็นทางออกที่ยากในการตัดสินใจ แต่เราได้กำจัดอะตอมเพื่อติดตามวิธีการของ Clojure ในรหัสของเรา โดยทั่วไปแล้วคุณไม่เคยขับรถหรือเก็บอะไรไว้ข้างใน ฟังก์ชั่นภายในทั้งหมดที่ใช้ในการแก้ไขอินสแตนซ์ตอนนี้เพียงส่งคืนแผนที่เวอร์ชันใหม่ ถ้าคุณมี swap!
หรือสิ่งที่คล้ายกันในรหัสของคุณสำหรับไดรเวอร์โปรด refactor รหัสของคุณก่อนที่คุณจะอัปเดต
ตั้งแต่ 0.4.0
ห้องสมุดรองรับการกระทำของ WebDriver การกระทำคือคำสั่งที่ส่งไปยังไดรเวอร์ในชุด ดูส่วนที่เกี่ยวข้องโดยละเอียดใน TOC
ตั้งแต่ 0.4.0
, Etaoin สามารถเล่นไฟล์สคริปต์ที่สร้างขึ้นในซีลีเนียม IDE แบบโต้ตอบ ดูส่วนที่เกี่ยวข้องด้านล่าง
Ct t
ตามปกติคุณสามารถส่ง บริษัท ของคุณไปยังรายการนั้นได้
มีสองขั้นตอนในการติดตั้ง:
etaoin
ลงในรหัส Clojure ของคุณ เพิ่มสิ่งต่อไปนี้ลงใน :dependencies
ในไฟล์ project.clj
ของคุณ:
[etaoin "0.4.6"]
ทำงานร่วมกับ Clojure 1.9 ขึ้นไป
หน้านี้มีคำแนะนำเกี่ยวกับวิธีการติดตั้งไดรเวอร์ที่คุณต้องใช้ในการทำให้เบราว์เซอร์ของคุณเป็นไปโดยอัตโนมัติ
ติดตั้งเบราว์เซอร์ Chrome และ Firefox ดาวน์โหลดจากเว็บไซต์อย่างเป็นทางการ จะไม่มีปัญหากับแพลตฟอร์มทั้งหมด
ติดตั้งไดรเวอร์เฉพาะที่คุณต้องการ:
ไดรเวอร์ Google Chrome:
brew cask install chromedriver
สำหรับผู้ใช้ Mac2.28
2.27
และด้านล่างมีข้อผิดพลาดที่เกี่ยวข้องกับการเพิ่มหน้าต่างให้สูงสุด (ดู [[การแก้ไขปัญหา]])Geckodriver คนขับรถสำหรับ Firefox:
brew install geckodriver
สำหรับผู้ใช้ MacPhantom.js เบราว์เซอร์:
brew install phantomjs
สำหรับผู้ใช้ Macไดรเวอร์ Safari (สำหรับ Mac เท่านั้น):
ตอนนี้ตรวจสอบการติดตั้งของคุณเรียกใช้คำสั่งใด ๆ เหล่านี้ สำหรับแต่ละคำสั่งกระบวนการที่ไม่มีที่สิ้นสุดด้วยเซิร์ฟเวอร์ HTTP ในเครื่องควรเริ่มต้น
chromedriver
geckodriver
phantomjs --wd
safaridriver -p 0
คุณสามารถเรียกใช้การทดสอบสำหรับการเปิดตัวห้องสมุดนี้:
lein test
คุณจะเห็นหน้าต่างเบราว์เซอร์เปิดและปิดเป็นอนุกรม การทดสอบใช้ไฟล์ HTML ในเครื่องพร้อมเค้าโครงพิเศษเพื่อตรวจสอบความถูกต้องของกรณีส่วนใหญ่
ดูด้านล่างสำหรับส่วนการแก้ไขปัญหาหากคุณมีปัญหา
ข่าวดีที่คุณสามารถทำให้เบราว์เซอร์ของคุณได้โดยตรงจาก REPL:
( use 'etaoin.api)
( require '[etaoin.keys :as k])
( def driver ( firefox )) ; ; here, a Firefox window should appear
; ; let's perform a quick Wiki session
( go driver " https://en.wikipedia.org/ " )
( wait-visible driver [{ :id :simpleSearch } { :tag :input :name :search }])
; ; search for something
( fill driver { :tag :input :name :search } " Clojure programming language " )
( fill driver { :tag :input :name :search } k/enter)
( wait-visible driver { :class :mw-search-results })
; ; select an `option` in select-box by visible text
; ; <select id="country">
; ; <option value="rf">Russia</option>
; ; <option value="usa">United States</option>
; ; <option value="uk">United Kingdom</option>
; ; <option value="fr">France</option>
; ;</select>
( select driver :country " France " )
( get-element-value driver :country )
; ;=> "fr"
; ; I'm sure the first link is what I was looking for
( click driver [{ :class :mw-search-results } { :class :mw-search-result-heading } { :tag :a }])
( wait-visible driver { :id :firstHeading })
; ; let's ensure
( get-url driver) ; ; "https://en.wikipedia.org/wiki/Clojure"
( get-title driver) ; ; "Clojure - Wikipedia"
( has-text? driver " Clojure " ) ; ; true
; ; navigate on history
( back driver)
( forward driver)
( refresh driver)
( get-title driver) ; ; "Clojure - Wikipedia"
; ; stops Firefox and HTTP server
( quit driver)
คุณเห็นว่าฟังก์ชั่นใด ๆ ต้องใช้อินสแตนซ์ไดรเวอร์เป็นอาร์กิวเมนต์แรก ดังนั้นคุณสามารถทำให้มันง่ายขึ้นโดยใช้ macros doto
:
( def driver ( firefox ))
( doto driver
( go " https://en.wikipedia.org/ " )
( wait-visible [{ :id :simpleSearch } { :tag :input :name :search }])
; ; ...
( fill { :tag :input :name :search } k/enter)
( wait-visible { :class :mw-search-results })
( click :some-button )
; ; ...
( wait-visible { :id :firstHeading })
; ; ...
( quit ))
ในกรณีนี้รหัสของคุณดูเหมือน DSL ที่ออกแบบมาเพื่อวัตถุประสงค์ดังกล่าว
คุณสามารถใช้ fill-multi
เพื่อย่อรหัสเช่น:
( fill driver :login " login " )
( fill driver :password " pass " )
( fill driver :textarea " some text " )
เข้าไปข้างใน
( fill-multi driver { :login " login "
:password " pass "
:textarea " some text " })
หากมีข้อยกเว้นใด ๆ เกิดขึ้นในช่วงเซสชันเบราว์เซอร์กระบวนการภายนอกอาจแขวนอยู่ตลอดไปจนกว่าคุณจะฆ่ามันด้วยตนเอง เพื่อป้องกันให้ใช้ with-<browser>
ดังนี้:
( with-firefox {} ff ; ; additional options first, then bind name
( doto ff
( go " https://google.com " )
...))
ไม่ว่าจะเกิดอะไรขึ้นระหว่างเซสชันกระบวนการจะหยุดลง
ฟังก์ชั่นส่วนใหญ่เช่น click
fill
ฯลฯ ต้องใช้คำสืบค้นเพื่อค้นหาองค์ประกอบในหน้า ตัวอย่างเช่น:
( click driver { :tag :button })
( fill driver { :id " searchInput " } " Clojure " )
ห้องสมุดรองรับประเภทและค่าการสืบค้นต่อไปนี้
:active
Stand สำหรับองค์ประกอบที่ใช้งานอยู่ในปัจจุบัน เมื่อเปิดหน้า Google Page มันจะมุ่งเน้นเคอร์เซอร์ในอินพุตการค้นหาหลัก ดังนั้นจึงไม่จำเป็นต้องคลิกด้วยตนเอง ตัวอย่าง:
( fill driver :active " Let's search for something " keys/enter)
คำหลักอื่น ๆ ที่ระบุ ID ขององค์ประกอบ สำหรับ Google Page คือ :lst-ib
หรือ "lst-ib"
(รองรับสตริงด้วย) เรื่องรีจิสทรี ตัวอย่าง:
( fill driver :lst-ib " What is the Matrix? " keys/enter)
สตริงที่มีนิพจน์ XPath ระวังเมื่อเขียนด้วยตนเอง ตรวจสอบส่วน Troubleshooting
ด้านล่าง ตัวอย่าง:
( fill driver " .//input[@id='lst-ib'][@name='q'] " " XPath in action! " keys/enter)
แผนที่ที่มี :xpath
หรือ :css
พร้อมนิพจน์สตริงของไวยากรณ์ที่สอดคล้องกัน ตัวอย่าง:
( fill driver { :xpath " .//input[@id='lst-ib'] " } " XPath selector " keys/enter)
( fill driver { :css " input#lst-ib[name='q'] " } " CSS selector " keys/enter)
ดูคู่มือการเลือก CSS สำหรับข้อมูลเพิ่มเติม
แบบสอบถามอาจเป็นแผนที่อื่น ๆ ที่แสดงถึงการแสดงออกของ XPath เป็นข้อมูล กฎคือ:
:tag
แสดงชื่อของแท็ก มันจะกลายเป็น *
เมื่อไม่ผ่าน:index
ขยายไปสู่ประโยคต่อท้าย [x]
มีประโยชน์เมื่อคุณต้องการเลือกแถวที่สามจากตารางตัวอย่างเช่น:fn/
Namespace และขยายไปสู่สิ่งที่เฉพาะเจาะจงตัวอย่าง:
ค้นหาแท็ก div
แรก
( query driver { :tag :div })
; ; expands into .//div
ค้นหาแท็ก n-th div
( query driver { :tag :div :index 1 })
; ; expands into .//div[1]
ค้นหาแท็ก a
ด้วยแอตทริบิวต์คลาสเท่ากับ active
( query driver { :tag :a :class " active " })
; ; ".//a[@class="active"]"
ค้นหาแบบฟอร์มโดยคุณลักษณะของมัน:
( query driver { :tag :form :method :GET :class :message })
; ; expands into .//form[@method="GET"][@class="message"]
ค้นหาปุ่มตามข้อความ (การจับคู่ที่แน่นอน):
( query driver { :tag :button :fn/text " Press Me " })
; ; .//button[text()="Press Me"]
ค้นหาองค์ประกอบ nth ( p
, a
, อะไรก็ได้) ด้วยข้อความ "ดาวน์โหลด":
( query driver { :fn/has-text " download " :index 3 })
; ; .//*[contains(text(), "download")][3]
ค้นหาองค์ประกอบที่มีคลาสต่อไปนี้:
( query driver { :tag :div :fn/has-class " overlay " })
; ; .//div[contains(@class, "overlay")]
ค้นหาองค์ประกอบที่มีโดเมนต่อไปนี้ใน href:
( query driver { :tag :a :fn/link " google.com " })
; ; .//a[contains(@href, "google.com")]
ค้นหาองค์ประกอบที่มีคลาสต่อไปนี้ในครั้งเดียว:
( query driver { :fn/has-classes [ :active :sticky :marked ]})
; ; .//*[contains(@class, "active")][contains(@class, "sticky")][contains(@class, "marked")]
ค้นหาวิดเจ็ตอินพุตที่เปิดใช้งาน/ปิดใช้งาน:
; ; first input
( query driver { :tag :input :fn/disabled true })
; ; .//input[@disabled=true()]
( query driver { :tag :input :fn/enabled true })
; ; .//input[@enabled=true()]
; ; all inputs
( query-all driver { :tag :input :fn/disabled true })
; ; .//input[@disabled=true()]
แบบสอบถามอาจเป็นเวกเตอร์ที่ประกอบด้วยการแสดงออกใด ๆ ที่กล่าวถึงข้างต้น ในแบบสอบถามดังกล่าวทุกคำต่อไปจะค้นหาจากบทก่อนหน้านี้ซ้ำ ๆ
ตัวอย่างง่ายๆ:
( click driver [{ :tag :html } { :tag :body } { :tag :a }])
คุณอาจรวมทั้งการแสดงออกของ XPATH และ CSS ด้วย (ให้ความสนใจกับ DOT ชั้นนำในการแสดงออก XPATH:
( click driver [{ :tag :html } { :css " div.class " } " .//a[@class='download'] " ])
บางครั้งคุณอาจต้องโต้ตอบกับองค์ประกอบ nth ของแบบสอบถามเช่นเมื่อต้องการคลิกที่ลิงค์ที่สองในตัวอย่างนี้:
< ul >
< li class =" search-result " >
< a href =" a " > a </ a >
</ li >
< li class =" search-result " >
< a href =" b " > b </ a >
</ li >
< li class =" search-result " >
< a href =" c " > c </ a >
</ li >
</ ul >
ในกรณีนี้คุณอาจใช้ :index
ที่รองรับการแสดงออกของ XPath เช่นนี้:
( click driver [{ :tag :li :class :search-result :index 2 } { :tag :a }])
หรือคุณสามารถใช้เคล็ดลับเด็ก nth-child กับนิพจน์ CSS เช่นนี้:
( click driver { :css " li.search-result:nth-child(2) a " })
ในที่สุดก็เป็นไปได้ที่จะได้รับองค์ประกอบ nth โดยตรงโดยใช้ query-all
:
( click-el driver ( nth ( query-all driver { :css " li.search-result a " }) 2 ))
หมายเหตุการใช้ click-el
ที่นี่เนื่องจาก query-all
ส่งคืนองค์ประกอบไม่ใช่ตัวเลือกที่สามารถส่งผ่านไปยัง click
โดยตรง
query-tree
ใช้ตัวเลือกและทำหน้าที่เหมือนต้นไม้ ทุกองค์ประกอบค้นหาตัวเลือกถัดไปจากองค์ประกอบก่อนหน้านี้ ตัวเลือกกำปั้นอาศัยการค้นหาองค์ประกอบและส่วนที่เหลือใช้การค้นหาองค์ประกอบจาก
( query-tree driver { :tag :div } { :tag :a })
วิธี
{:tag :div} -> [div1 div2 div3]
div1 -> [a1 a2 a3]
div2 -> [a4 a5 a6]
div3 -> [a7 a8 a9]
ดังนั้นผลลัพธ์จะเป็น [A1 ... A9]
ในการโต้ตอบกับองค์ประกอบที่พบผ่านการสืบค้นคุณต้องส่งผลการสอบถามไปยัง click-el
หรือ fill-el
:
( click-el driver ( first ( query-all driver { :tag :a })))
ดังนั้นคุณสามารถรวบรวมองค์ประกอบลงในเวกเตอร์และโต้ตอบกับพวกเขาได้ตลอดเวลา:
( def elements ( query-all driver { :tag :input :type :text })
( fill-el driver ( first elements) " This is a test " )
( fill-el driver ( rand-nth elements) " I like tests! " )
เพื่อจุดประสงค์ในการเลียนแบบการป้อนข้อมูลของมนุษย์คุณสามารถใช้ฟังก์ชั่น fill-human
ตัวเลือกต่อไปนี้เปิดใช้งานโดยค่าเริ่มต้น:
{ :mistake-prob 0.1 ; ; a real number from 0.1 to 0.9, the higher the number, the more typos will be made
:pause-max 0.2 } ; ; max typing delay in seconds
และคุณสามารถกำหนดใหม่ได้:
( fill-human driver q text { :mistake-prob 0.5
:pause-max 1 })
; ; or just use default opts by omitting them
( fill-human driver q text)
สำหรับการป้อนข้อมูลหลายครั้งด้วยการจำลองของมนุษย์ให้ใช้ fill-human-multi
( fill-human-multi driver { :login " login "
:pass " password "
:textarea " some text " }
{ :mistake-prob 0.5
:pause-max 1 })
ฟังก์ click
ทริกเกอร์เมาส์ซ้ายคลิกที่องค์ประกอบที่พบโดยคำสืบค้น:
( click driver { :tag :button })
ฟังก์ชั่น click
ใช้เฉพาะองค์ประกอบแรกที่พบโดยแบบสอบถามซึ่งบางครั้งนำไปสู่การคลิกที่รายการที่ไม่ถูกต้อง เพื่อให้แน่ใจว่ามีองค์ประกอบหนึ่งและเพียงหนึ่งองค์ประกอบให้ใช้ฟังก์ชั่น click-single
มันทำหน้าที่เหมือนกัน แต่ยกข้อยกเว้นเมื่อสอบถามหน้ากลับองค์ประกอบหลายองค์ประกอบ:
( click-single driver { :tag :button :name " search " })
การคลิกสองครั้งนั้นใช้ในเว็บไม่ค่อยเป็นไปได้ด้วยฟังก์ชั่น double-click
(Chrome, Phantom.js):
( double-click driver { :tag :dbl-click-btn })
นอกจากนี้ยังมีฟังก์ชั่นการคลิก "ตาบอด" พวกเขากระตุ้นการคลิกเมาส์ในตำแหน่งเมาส์ปัจจุบัน:
( left-click driver)
( middle-click driver)
( right-click driver)
ฟังก์ชั่นอื่นทำเช่นเดียวกัน แต่ย้ายตัวชี้เมาส์ไปยังองค์ประกอบที่ระบุก่อนคลิกที่พวกเขา:
( left-click-on driver { :tag :img })
( middle-click-on driver { :tag :img })
( right-click-on driver { :tag :img })
การคลิกเมาส์กลางมีประโยชน์เมื่อเปิดลิงก์ในแท็บพื้นหลังใหม่ บางครั้งการคลิกขวาใช้เพื่อเลียนแบบเมนูบริบทในเว็บแอปพลิเคชัน
ห้องสมุดรองรับการกระทำของ WebDriver โดยทั่วไปการกระทำเป็นคำสั่งที่อธิบายอุปกรณ์อินพุตเสมือนจริง
{ :actions [{ :type " key "
:id " some name "
:actions [{ :type " keyDown " :value cmd}
{ :type " keyDown " :value " a " }
{ :type " keyUp " :value " a " }
{ :type " keyUp " :value cmd}
{ :type " pause " :duration 100 }]}
{ :type " pointer "
:id " UUID or some name "
:parameters { :pointerType " mouse " }
:actions [{ :type " pointerMove " :origin " pointer " :x 396 :y 323 }
; ; double click
{ :type " pointerDown " :duration 0 :button 0 }
{ :type " pointerUp " :duration 0 :button 0 }
{ :type " pointerDown " :duration 0 :button 0 }
{ :type " pointerUp " :duration 0 :button 0 }]}]}
คุณสามารถสร้างแผนที่ด้วยตนเองและส่งไปยังวิธี perform-actions
:
( def keyboard-input { :type " key "
:id " some name "
:actions [{ :type " keyDown " :value cmd}
{ :type " keyDown " :value " a " }
{ :type " keyUp " :value " a " }
{ :type " keyUp " :value cmd}
{ :type " pause " :duration 100 }]})
( perform-actions driver keyboard-input)
หรือใช้ wrappers ก่อนอื่นคุณต้องสร้างอุปกรณ์อินพุตเสมือนจริงเช่น:
( def keyboard ( make-key-input ))
จากนั้นเติมด้วยการกระทำที่จำเป็น:
( -> keyboard
( add-key-down keys/shift-left)
( add-key-down " a " )
( add-key-up " a " )
( add-key-up keys/shift-left))
ตัวอย่างขยาย:
( let [driver ( chrome )
_ ( go driver " https://google.com " )
search-box ( query driver { :name :q })
mouse ( -> ( make-mouse-input )
( add-pointer-click-el search-box))
keyboard ( -> ( make-key-input )
add-pause
( with-key-down keys/shift-left
( add-key-press " e " ))
( add-key-press " t " )
( add-key-press " a " )
( add-key-press " o " )
( add-key-press " i " )
( add-key-press " n " )
( add-key-press keys/enter))]
( perform-actions driver keyboard mouse)
( quit driver))
หากต้องการล้างสถานะของอุปกรณ์อินพุตเสมือนจริงปล่อยปุ่มกดทั้งหมด ฯลฯ ให้ใช้วิธี release-actions
:
( release-actions driver)
การคลิกที่ปุ่มอินพุตไฟล์จะเปิดกล่องโต้ตอบเฉพาะระบบปฏิบัติการที่คุณไม่ได้รับอนุญาตให้โต้ตอบกับการใช้โปรโตคอล WebDriver ใช้ฟังก์ชั่น upload-file
เพื่อแนบไฟล์โลคัลกับวิดเจ็ตอินพุตไฟล์ ฟังก์ชั่นใช้ตัวเลือกที่ชี้ไปที่อินพุตไฟล์และเส้นทางเต็มรูปแบบเป็นสตริงหรืออินสแตนซ์ java.io.File
แบบดั้งเดิม ไฟล์ควรมีอยู่หรือคุณจะได้รับข้อยกเว้นเป็นอย่างอื่น ตัวอย่างการใช้งาน:
( def driver ( chrome ))
; ; open a web page that serves uploaded files
( go driver " http://nervgh.github.io/pages/angular-file-upload/examples/simple/ " )
; ; bound selector to variable; you may also specify an id, class, etc
( def input { :tag :input :type :file })
; ; upload an image with the first one file input
( def my-file " /Users/ivan/Downloads/sample.png " )
( upload-file driver input my-file)
; ; or pass a native Java object:
( require '[clojure.java.io :as io])
( def my-file ( io/file " /Users/ivan/Downloads/sample.png " ))
( upload-file driver input my-file)
การเรียกฟังก์ชันภาพ screenshot
ทิ้งหน้าปัจจุบันลงในภาพ PNG บนดิสก์ของคุณ:
( screenshot driver " page.png " ) ; ; relative path
( screenshot driver " /Users/ivan/page.png " ) ; ; absolute path
รองรับวัตถุไฟล์ Java เนทีฟด้วย:
; ; when imported as `[clojure.java.io :as io]`
( screenshot driver ( io/file " test.png " ))
; ; native object
( screenshot driver ( java.io.File. " test-native.png " ))
ด้วย Firefox และ Chrome คุณอาจไม่ได้จับภาพทั้งหน้า แต่เป็นองค์ประกอบเดียวพูดว่า Div วิดเจ็ตอินพุตหรืออะไรก็ตาม ตอนนี้ไม่ทำงานกับเบราว์เซอร์อื่น ๆ ตัวอย่าง:
( screenshot-element driver { :tag :div :class :smart-widget } " smart_widget.png " )
ด้วย with-screenshots
แมโครคุณสามารถสร้างภาพหน้าจอหลังจากแต่ละแบบฟอร์ม
( with-screenshots driver " ../screenshots "
( fill driver :simple-input " 1 " )
( fill driver :simple-input " 2 " )
( fill driver :simple-input " 3 " ))
เทียบเท่ากับบันทึก:
( fill driver :simple-input " 1 " )
( screenshot driver " ../screenshots/chrome-...123.png " )
( fill driver :simple-input " 2 " )
( screenshot driver " ../screenshots/chrome-...124.png " )
( fill driver :simple-input " 3 " )
( screenshot driver " ../screenshots/chrome-...125.png " )
เมื่อเร็ว ๆ นี้ Google Chrome และ Firefox ในภายหลังเริ่มรองรับคุณสมบัติชื่อ Headless Mode เมื่อไม่มีหัวศีรษะไม่มีหน้าต่าง UI เกิดขึ้นบนหน้าจอมีเพียงเอาต์พุต stdout เท่านั้นที่จะเข้าสู่คอนโซล คุณสมบัตินี้ช่วยให้คุณสามารถเรียกใช้การทดสอบการรวมบนเซิร์ฟเวอร์ที่ไม่มีอุปกรณ์เอาต์พุตกราฟิก
ตรวจสอบให้แน่ใจว่าเบราว์เซอร์ของคุณรองรับโหมดหัวขาดโดยการตรวจสอบว่ามันยอมรับ --headles
บรรทัดคำสั่ง -หัวเมื่อเรียกใช้จากเทอร์มินัล ไดรเวอร์ Phantom.js นั้นไม่มีความมุ่งมั่นตามธรรมชาติ (ไม่เคยได้รับการพัฒนาสำหรับการแสดงผล UI)
เมื่อเริ่มต้นไดรเวอร์ส่งผ่าน :headless
เพื่อเปลี่ยนเป็นโหมดหัวขาด หมายเหตุรองรับ Chrome และ Firefox รุ่นล่าสุดเท่านั้น สำหรับไดรเวอร์อื่น ๆ ธงจะถูกเพิกเฉย
( def driver ( chrome { :headless true })) ; ; runs headless Chrome
หรือ
( def driver ( firefox { :headless true })) ; ; runs headless Firefox
ในการตรวจสอบไดรเวอร์ใด ๆ ที่ทำงานในโหมดหัวขาดให้ใช้ headless?
ภาคแสดง:
( headless? driver) ; ; true
หมายเหตุมันจะกลับมาเป็นจริงสำหรับอินสแตนซ์ phantom.js เสมอ
มีทางลัดหลายทางที่จะเรียกใช้ Chrome หรือ Firefox ในโหมด Headless โดยค่าเริ่มต้น:
( def driver ( chrome-headless ))
; ; or
( def driver ( firefox-headless {...})) ; ; with extra settings
; ; or
( with-chrome-headless nil driver
( go driver " http://example.com " ))
( with-firefox-headless {...} driver ; ; extra settings
( go driver " http://example.com " ))
นอกจากนี้ยังมี macroses when-headless
และ when-not-headless
ที่อนุญาตให้ทำคำสั่งพวงได้ก็ต่อเมื่อเบราว์เซอร์อยู่ในโหมดหัวขาดหรือไม่ตามลำดับ:
( with-chrome nil driver
...
( when-not-headless driver
... some actions that might be not available in headless mode)
... common actions for both versions)
ในการเชื่อมต่อกับไดรเวอร์ที่ทำงานบนโฮสต์ในพื้นที่หรือระยะไกลคุณต้องระบุ :host
ซึ่งอาจเป็นชื่อโฮสต์ (LocalHost, Some.remote.host.net) หรือที่อยู่ IP (127.0.0.1, 183.102.156.31 ) และ :port
หากไม่ได้ระบุพอร์ตพอร์ตเริ่มต้นจะถูกตั้งค่า
ตัวอย่าง:
; ; Chrome
( def driver ( chrome { :host " 127.0.0.1 " :port 9515 })) ; ; for connection to driver on localhost on port 9515
; ; Firefox
( def driver ( firefox { :host " 192.168.1.11 " })) ; ; the default port for firefox is 4444
ในการทำงานกับไดรเวอร์ใน Docker คุณสามารถถ่ายภาพสำเร็จรูปได้:
ตัวอย่างสำหรับ Chrome:
docker run --name chromedriver -p 9515:4444 -d -e CHROMEDRIVER_WHITELISTED_IPS='' robcherry/docker-chromedriver:latest
สำหรับ Firefox:
docker run --name geckodriver -p 4444:4444 -d instrumentisto/geckodriver
ในการเชื่อมต่อกับไดรเวอร์คุณเพียงแค่ต้องระบุ :host
เป็น localhost
หรือ 127.0.0.1
และ :port
ที่กำลังทำงานอยู่ หากไม่ได้ระบุพอร์ตพอร์ตเริ่มต้นจะถูกตั้งค่า
( def driver ( chrome-headless { :host " localhost " :port 9515 :args [ " --no-sandbox " ]}))
( def driver ( firefox-headless { :host " localhost " })) ; ; will try to connect to port 4444
ในการตั้งค่าการตั้งค่าพร็อกซีให้ใช้ตัวแปรสภาพแวดล้อม HTTP_PROXY
/ HTTPS_PROXY
หรือผ่านแผนที่ประเภทต่อไปนี้:
{ :proxy { :http " some.proxy.com:8080 "
:ftp " some.proxy.com:8080 "
:ssl " some.proxy.com:8080 "
:socks { :host " myproxy:1080 " :version 5 }
:bypass [ " http://this.url " " http://that.url " ]
:pac-url " localhost:8888 " }}
; ; example
( chrome { :proxy { :http " some.proxy.com:8080 "
:ssl " some.proxy.com:8080 " }})
หมายเหตุ: A: PAC-URL สำหรับไฟล์การกำหนดค่าอัตโนมัติพร็อกซี ใช้กับ Safari เป็นตัวเลือกพร็อกซีอื่นไม่ทำงานในเบราว์เซอร์นั้น
ในการปรับแต่งพร็อกซีคุณสามารถใช้วัตถุต้นฉบับและส่งผ่านไปยังความสามารถ:
{ :capabilities { :proxy { :proxyType " manual "
:proxyAutoconfigUrl " some.proxy.com:8080 "
:ftpProxy " some.proxy.com:8080 "
:httpProxy " some.proxy.com:8080 "
:noProxy [ " http://this.url " " http://that.url " ]
:sslProxy " some.proxy.com:8080 "
:socksProxy " some.proxy.com:1080 "
:socksVersion 5 }}}
( chrome { :capabilities { :proxy {...}}})
ด้วยการอัปเดตล่าสุดห้องสมุดนำคุณสมบัติที่ยอดเยี่ยม ตอนนี้คุณสามารถติดตามกิจกรรมที่มาจากแผง Devtools หมายความว่าทุกสิ่งที่คุณเห็นในคอนโซลนักพัฒนาซอฟต์แวร์ตอนนี้มีให้บริการผ่าน API ตอนนี้ใช้งานได้กับ Google Chrome เท่านั้น
ในการเริ่มต้นไดรเวอร์ที่มีการตั้งค่าการพัฒนาพิเศษเพียงแค่ผ่านแผนที่ว่างไปยังฟิลด์ :dev
เมื่อเรียกใช้ไดรเวอร์:
( def c ( chrome { :dev {}}))
ค่าจะต้องไม่เป็น nil
เมื่อมันเป็นแผนที่เปล่าฟังก์ชั่นพิเศษจะมีค่าเริ่มต้น นี่คือเวอร์ชันเต็มของการตั้งค่า DEV พร้อมค่าที่เป็นไปได้ทั้งหมดที่ระบุ
( def c ( chrome { :dev
{ :perf
{ :level :all
:network? true
:page? true
:interval 1000
:categories [ :devtools
:devtools.network
:devtools.timeline ]}}}))
ภายใต้ประทุนมันเติมพจนานุกรม perfLoggingPrefs
พิเศษภายในวัตถุ chromeOptions
ตอนนี้เบราว์เซอร์ของคุณสะสมกิจกรรมเหล่านี้คุณสามารถอ่านได้โดยใช้เนมส dev
ซแบบพิเศษ
( go c " http://google.com " )
; ; wait until the page gets loaded
; ; load the namespace
( require '[etaoin.dev :as dev])
มามีรายการคำขอ HTTP ทั้งหมดที่เกิดขึ้นระหว่างหน้ากำลังโหลด
( def reqs ( dev/get-requests c))
; ; reqs is a vector of maps
( count reqs)
; ; 19
; ; what were their types?
( set ( map :type reqs))
; ; #{:script :other :document :image :xhr}
; ; we've got Js requests, images, AJAX and other stuff
; ; check the last one request, it's an image named tia.png
( -> reqs last clojure.pprint/pprint)
{ :state 4 ,
:id " 1000052292.8 " ,
:type :image ,
:xhr? false ,
:url " https://www.gstatic.com/inputtools/images/tia.png " ,
:with-data? nil,
:request
{ :method :get ,
:headers
{ :Referer " https://www.google.com/ " ,
:User-Agent
" Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36 " }},
:response
{ :status 200 ,
:headers {}, ; ; truncated
:mime " image/png " ,
:remote-ip " 173.194.73.94 " },
:done? true }
เนื่องจากเราสนใจคำขอ AJAX เป็นส่วนใหญ่จึงมีฟังก์ชั่น get-ajax
ที่ทำเช่นเดียวกัน แต่ตัวกรองคำขอ XHR:
( -> c dev/get-ajax last clojure.pprint/pprint)
{ :state 4 ,
:id " 1000051989.41 " ,
:type :xhr ,
:xhr? true ,
:url
" https://www.google.com/complete/search?q=clojure%20spec&cp=12&client=psy-ab&xssi=t&gs_ri=gws-wiz&hl=ru&authuser=0&psi=4iUbXdapJsbmrgTVt7H4BA.1562060259137&ei=4iUbXdapJsbmrgTVt7H4BA " ,
:with-data? nil,
:request
{ :method :get ,
:headers
{ :Referer " https://www.google.com/ " ,
:User-Agent
" Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36 " }},
:response
{ :status 200 ,
:headers {}, ; ; truncated
:mime " application/json " ,
:remote-ip " 74.125.131.99 " },
:done? true }
รูปแบบทั่วไปของการใช้งาน get-ajax
นั้นมีดังต่อไปนี้ คุณต้องการตรวจสอบว่ามีการยื่นคำขอบางอย่างไปยังเซิร์ฟเวอร์หรือไม่ ดังนั้นคุณกดปุ่มรอสักครู่แล้วอ่านคำขอที่ทำโดยเบราว์เซอร์ของคุณ
มีรายการคำขอคุณค้นหาสิ่งที่คุณต้องการ (เช่น URL) จากนั้นตรวจสอบสถานะของมัน :state
มีความหมายเดียวกันเช่น XMLHttpRequest.readyState
มี มันเป็นจำนวนเต็มตั้งแต่ 1 ถึง 4 ที่มีพฤติกรรมเดียวกัน
หากต้องการตรวจสอบว่ามีการร้องขอเสร็จแล้วเสร็จหรือล้มเหลวให้ใช้ภาคการศึกษาเหล่านี้:
( def req ( last reqs))
( dev/request-done? req)
; ; true
( dev/request-failed? req)
; ; false
( dev/request-success? req)
; ; true
โปรดทราบว่า request-done?
ไม่ได้หมายความว่าคำขอประสบความสำเร็จ มันหมายความว่าไปป์ไลน์ของมันถึงขั้นตอนสุดท้ายเท่านั้น
คำเตือน: เมื่อคุณอ่านบันทึกการพัฒนาคุณจะกินพวกเขาจากบัฟเฟอร์ภายในที่ถูกล้างออก การโทรครั้งที่สองเพื่อ get-requests
หรือ get-ajax
จะส่งคืนรายการที่ว่างเปล่า
บางทีคุณอาจต้องการรวบรวมบันทึกเหล่านี้ด้วยตัวคุณเอง ฟังก์ชั่น dev/get-performance-logs
ส่งคืนรายการบันทึกเพื่อให้คุณสะสมในอะตอมหรืออะไรก็ตาม:
( def logs ( atom []))
; ; repeat that form from time to time
( do ( swap! logs concat ( dev/get-performance-logs c))
true )
( count @logs)
; ; 76
มี logs->requests
และ logs->ajax
ที่แปลงบันทึกเป็นคำขอ ซึ่งแตกต่างจาก get-requests
และ get-ajax
พวกเขาเป็นฟังก์ชั่นที่บริสุทธิ์และจะไม่ล้างอะไรเลย
( dev/logs->requests @logs)
เมื่อทำงานกับบันทึกและคำขอให้ให้ความสนใจกับการนับและขนาดของพวกเขา แผนที่มีกุญแจมากมายและปริมาณของรายการในคอลเลกชันอาจมีขนาดใหญ่มาก การพิมพ์กิจกรรมทั้งหมดอาจทำให้บรรณาธิการของคุณหยุดลง พิจารณาใช้ฟังก์ชั่น clojure.pprint/pprint
เนื่องจากขึ้นอยู่กับระดับสูงสุดและขีดจำกัดความยาว
บางครั้งอาจเป็นเรื่องยากที่จะค้นพบสิ่งที่ผิดพลาดในระหว่างการทดสอบ UI ครั้งล่าสุด แมโครพิเศษ with-postmortem
บันทึกข้อมูลที่เป็นประโยชน์บางอย่างบนดิสก์ก่อนที่จะมีการทริกเกอร์ข้อยกเว้น ข้อมูลเหล่านั้นเป็นบันทึกภาพหน้าจอรหัส HTML และบันทึกคอนโซล JS หมายเหตุ: เบราว์เซอร์ทั้งหมดไม่สนับสนุนการรับบันทึก JS
ตัวอย่าง:
( def driver ( chrome ))
( with-postmortem driver { :dir " /Users/ivan/artifacts " }
( click driver :non-existing-element ))
ข้อยกเว้นจะเพิ่มขึ้น แต่ใน /Users/ivan/artifacts
จะมีไฟล์สามไฟล์ที่ชื่อโดยเทมเพลต <browser>-<host>-<port>-<datetime>.<ext>
firefox-127.0.0.1-4444-2017-03-26-02-45-07.png
: ภาพหน้าจอจริงของหน้าเบราว์เซอร์;firefox-127.0.0.1-4444-2017-03-26-02-45-07.html
: เนื้อหา HTML ของเบราว์เซอร์ปัจจุบัน;firefox-127.0.0.1-4444-2017-03-26-02-45-07.json
: ไฟล์ JSON พร้อมบันทึกคอนโซล; นั่นคือเวกเตอร์ของวัตถุตัวจัดการใช้แผนที่ตัวเลือกด้วยคีย์ต่อไปนี้ พวกเขาทั้งหมดอาจขาดหายไป
{ ; ; default directory where to store artifacts
; ; might not exist, will be created otherwise. pwd is used when not passed
:dir " /home/ivan/UI-tests "
; ; a directory where to store screenshots; :dir is used when not passed
:dir-img " /home/ivan/UI-tests/screenshots "
; ; the same but for HTML sources
:dir-src " /home/ivan/UI-tests/HTML "
; ; the same but for console logs
:dir-log " /home/ivan/UI-tests/console "
; ; a string template to format a date; See SimpleDateFormat Java class
:date-format " yyyy-MM-dd-HH-mm-ss " }
ฟังก์ชั่น (get-logs driver)
ส่งคืนบันทึกของเบราว์เซอร์เป็นเวกเตอร์ของแผนที่ แต่ละแผนที่มีโครงสร้างต่อไปนี้:
{ :level :warning ,
:message " 1,2,3,4 anonymous (:1) " ,
:timestamp 1511449388366 ,
:source nil,
:datetime #inst " 2017-11-23T15:03:08.366-00:00 " }
ปัจจุบันบันทึกมีอยู่ใน Chrome และ Phantom.js เท่านั้น โปรดทราบว่าข้อความข้อความและประเภทต้นทางสูงขึ้นอยู่กับเบราว์เซอร์ Chrome เช็ดบันทึกเมื่ออ่านแล้ว Phantom.js เก็บไว้ แต่จนกว่าคุณจะเปลี่ยนหน้าเท่านั้น
เมื่อเรียกใช้อินสแตนซ์ของไดรเวอร์แผนที่พารามิเตอร์เพิ่มเติมอาจถูกส่งผ่านไปยังการปรับแต่งพฤติกรรมของเบราว์เซอร์:
( def driver ( chrome { :path " /path/to/driver/binary " }))
ด้านล่างนี่คือแผนที่ของพารามิเตอร์ที่รองรับไลบรารี พวกเขาทั้งหมดอาจถูกข้ามหรือมีค่าไม่มี บางส่วนของพวกเขาถ้าไม่ผ่านจะถูกนำมาจากแผนที่ defaults
{ ; ; Host and port for webdriver's process. Both are taken from defaults
; ; when are not passed. If you pass a port that has been already taken,
; ; the library will try to take a random one instead.
:host " 127.0.0.1 "
:port 9999
; ; Path to webdriver's binary file. Taken from defaults when not passed.
:path-driver " /Users/ivan/Downloads/geckodriver "
; ; Path to the driver's binary file. When not passed, the driver discovers it
; ; by its own.
:path-browser " /Users/ivan/Downloads/firefox/firefox "
; ; Extra command line arguments sent to the browser's process. See your browser's
; ; supported flags.
:args [ " --incognito " " --app " " http://example.com " ]
; ; Extra command line arguments sent to the webdriver's process.
:args-driver [ " -b " " /path/to/firefox/binary " ]
; ; Sets browser's minimal logging level. Only messages with level above
; ; that one will be collected. Useful for fetching Javascript logs. Possible
; ; values are: nil (aliases :off, :none), :debug, :info, :warn (alias :warning),
; ; :err (aliases :error, :severe, :crit, :critical), :all. When not passed,
; ; :all is set.
:log-level :err ; ; to show only errors but not debug
; ; Sets driver's log level.
; ; The value is a string. Possible values are:
; ; chrome: [ALL, DEBUG, INFO, WARNING, SEVERE, OFF]
; ; phantomjs: [ERROR, WARN, INFO, DEBUG] (default INFO)
; ; firefox [fatal, error, warn, info, config, debug, trace]
:driver-log-level
; ; Paths to the driver's log files as strings.
; ; When not set, the output goes to /dev/null (or NUL on Windows)
:log-stdout
:log-stderr
; ; Path to a custorm browser profile. See the section below.
:profile " /Users/ivan/Library/Application Support/Firefox/Profiles/iy4iitbg.Test "
; ; Env variables sent to the driver's process.
:env { :MOZ_CRASHREPORTER_URL " http://test.com " }
; ; Initial window size.
:size [ 1024 680 ]
; ; Default URL to open. Works only in FF for now.
:url " http://example.com "
; ; Override the default User-Agent. Useful for headless mode.
:user-agent " Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1) "
; ; Where to download files.
:download-dir " /Users/ivan/Desktop "
; ; Driver-specific options. Make sure you have read the docs before setting them.
:capabilities { :chromeOptions { :args [ " --headless " ]}}}
เมื่อคุณนำทางไปยังหน้าหนึ่งไดรเวอร์จะรอจนกว่าทั้งหน้าจะถูกโหลดอย่างสมบูรณ์ สิ่งที่ดีในกรณีส่วนใหญ่ยังไม่ได้สะท้อนให้เห็นถึงวิธีการที่มนุษย์มีปฏิสัมพันธ์กับอินเทอร์เน็ต
เปลี่ยนพฤติกรรมเริ่มต้นนี้ด้วย :load-strategy
มีสามค่าที่เป็นไปได้สำหรับสิ่งนั้น:: :none
, :eager
และ :normal
ซึ่งเป็นค่าเริ่มต้นเมื่อไม่ผ่าน
เมื่อคุณผ่าน :none
ไดรเวอร์ตอบกลับทันทีดังนั้นคุณสามารถดำเนินการตามคำแนะนำต่อไปได้ ตัวอย่างเช่น:
( def c ( chrome ))
( go c " http://some.slow.site.com " )
; ; you'll hang on this line until the page loads
( do-something )
ตอนนี้เมื่อผ่านตัวเลือกกลยุทธ์การโหลด:
( def c ( chrome { :load-strategy :none }))
( go c " http://some.slow.site.com " )
; ; no pause, acts immediately
( do-something )
สำหรับ :eager
มันใช้งานได้กับ Firefox ในขณะที่เพิ่มคุณสมบัติลงในห้องสมุด
มีตัวเลือกในการป้อนชุดคีย์พร้อมกัน นั่นเป็นประโยชน์ในการเลียนแบบการถือคีย์ระบบเช่นการควบคุมเปลี่ยนหรืออะไรก็ตามเมื่อพิมพ์
namespace etaoin.keys
มีค่าคงที่หลักรวมถึงชุดของฟังก์ชั่นที่เกี่ยวข้องกับอินพุต
( require '[etaoin.keys :as keys])
ตัวอย่างอย่างรวดเร็วของการป้อนอักขระธรรมดาที่ถือกะ:
( def c ( chrome ))
( go c " http://google.com " )
( fill-active c ( keys/with-shift " caps is great " ))
อินพุตหลักได้รับการเติมด้วย "caps is great" ตอนนี้คุณต้องการลบคำสุดท้าย ใน Chrome สิ่งนี้ทำได้โดยการกด backspace holding alt มาทำอย่างนั้น:
( fill-active c ( keys/with-alt keys/backspace))
ตอนนี้คุณมีเพียง "แคปคือ" ในอินพุต
พิจารณาตัวอย่างที่ซับซ้อนมากขึ้นซึ่งทำซ้ำพฤติกรรมของผู้ใช้จริง คุณต้องการลบทุกอย่างออกจากอินพุต ก่อนอื่นคุณย้าย Caret ในตอนแรก จากนั้นย้ายไปยังการหยุดพักเพื่อให้ทุกอย่างได้รับการคัดเลือก ในที่สุดคุณกดลบเพื่อล้างข้อความที่เลือก
คอมโบคือ:
( fill-active c keys/home ( keys/with-shift keys/end) keys/delete)
นอกจากนี้ยังมีฟัง with-ctrl
และ with-command
ที่ทำหน้าที่เหมือนกัน
ให้ความสนใจฟังก์ชั่นเหล่านี้ไม่ได้ใช้กับทางลัดของเบราว์เซอร์ทั่วโลก ตัวอย่างเช่น "คำสั่ง + + r" หรือ "คำสั่ง + t" โหลดหน้าใหม่หรือเปิดแท็บใหม่
ฟังก์ชั่น keys/with-*
ทั้งหมดเป็นเพียงการห่อหุ้มตามฟังก์ชัน keys/chord
ที่อาจใช้สำหรับกรณีที่ซับซ้อน
ในการระบุไดเรกทอรีของคุณเองที่จะดาวน์โหลดไฟล์ผ่าน :download-dir
ลงในแผนที่ตัวเลือกเมื่อเรียกใช้ไดรเวอร์:
( def driver ( chrome { :download-dir " /Users/ivan/Desktop " }))
ตอนนี้เมื่อคุณคลิกที่ลิงค์ควรใส่ไฟล์ลงในโฟลเดอร์นั้น ปัจจุบันรองรับ Chrome และ Firefox เท่านั้น
Firefox ต้องระบุประเภท MIME ของไฟล์เหล่านั้นที่ควรดาวน์โหลดโดยไม่แสดงกล่องโต้ตอบระบบ โดยค่าเริ่มต้นเมื่อ :download-dir
ถูกส่งผ่านไลบรารีจะเพิ่มประเภท MIME ที่พบบ่อยที่สุด: จดหมายเหตุ, ไฟล์สื่อ, เอกสารสำนักงาน ฯลฯ หากคุณต้องการเพิ่มหนึ่งของคุณเองแทนที่การตั้งค่านั้นด้วยตนเอง:
( def driver ( firefox { :download-dir " /Users/ivan/Desktop "
:prefs { :browser.helperApps.neverAsk.saveToDisk
" some-mime/type-1;other-mime/type-2 " }}))
หากต้องการตรวจสอบว่ามีการดาวน์โหลดไฟล์ในระหว่างการทดสอบ UI หรือไม่โปรดดูส่วนการทดสอบด้านล่าง
ตั้งค่าส่วนหัวของตัวแทนผู้ใช้ที่กำหนดเองด้วย :user-agent
เมื่อสร้างไดรเวอร์ตัวอย่างเช่น:
( def f ( firefox { :user-agent " Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1) " }))
ในการรับค่าปัจจุบันของส่วนหัวในรันไทม์ให้ใช้ฟังก์ชั่น:
( get-user-agent f)
; ; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
การตั้งค่าส่วนหัวนั้นค่อนข้างสำคัญสำหรับเบราว์เซอร์ที่ไม่มีหัวเนื่องจากไซต์ส่วนใหญ่ตรวจสอบว่าตัวแทนผู้ใช้มีสตริง "หัวหัว" หรือไม่ สิ่งนี้อาจนำไปสู่การตอบสนอง 403 หรือพฤติกรรมแปลก ๆ ของเว็บไซต์
เมื่อเรียกใช้ Chrome หรือ Firefox คุณอาจระบุโปรไฟล์พิเศษที่ทำขึ้นเพื่อการทดสอบ โปรไฟล์เป็นโฟลเดอร์ที่เก็บการตั้งค่าเบราว์เซอร์ประวัติบุ๊คมาร์คและข้อมูลเฉพาะผู้ใช้อื่น ๆ
ลองนึกภาพคุณต้องการเรียกใช้การทดสอบการรวมกับผู้ใช้ที่ปิดการดำเนินการ JavaScript หรือการแสดงภาพ การเตรียมโปรไฟล์พิเศษสำหรับงานนั้นจะเป็นทางเลือกที่ดี
chrome://version/
หน้า คัดลอกพา ธ ไฟล์ที่อยู่ใต้คำบรรยาย Profile Path
-P
, -p
หรือ -ProfileManager
Key ตามที่หน้าอย่างเป็นทางการอธิบายabout:support
ใกล้กับคำบรรยาย Profile Folder
กดปุ่ม Show in Finder
หน้าต่างโฟลเดอร์ใหม่ควรปรากฏขึ้น คัดลอกเส้นทางจากที่นั่น เมื่อคุณมีเส้นทางโปรไฟล์แล้วให้เปิดไดรเวอร์ด้วยคีย์ :profile
พิเศษดังนี้:
; ; Chrome
( def chrome-profile
" /Users/ivan/Library/Application Support/Google/Chrome/Profile 2/Default " )
( def chrm ( chrome { :profile chrome-profile}))
; ; Firefox
( def ff-profile
" /Users/ivan/Library/Application Support/Firefox/Profiles/iy4iitbg.Test " )
( def ff ( firefox { :profile ff-profile}))
ห้องสมุดจัดส่งชุดของฟังก์ชั่นเพื่อเลื่อนหน้า
สิ่งที่สำคัญที่สุดคือ scroll-query
กระโดดองค์ประกอบแรกที่พบในคำค้นหา:
( def driver ( chrome ))
; ; the form button placed somewhere below
( scroll-query driver :button-submit )
; ; the main article
( scroll-query driver { :tag :h1 })
หากต้องการข้ามไปยังตำแหน่งสัมบูรณ์เพียงใช้ scroll
ดังนี้:
( scroll driver 100 600 )
; ; or pass a map with x and y keys
( scroll driver { :x 100 :y 600 })
หากต้องการเลื่อนค่อนข้างใช้ scroll-by
โดยค่าออฟเซ็ต:
; ; keeps the same horizontal position, goes up for 100 pixels
( scroll-by driver 0 -100 ) ; ; map parameter is also supported
มีทางลัดสองทางที่จะกระโดดด้านบนหรือด้านล่างของหน้า:
( scroll-bottom driver) ; ; you'll see the footer...
( scroll-top driver) ; ; ...and the header again
ฟังก์ชั่นต่อไปนี้เลื่อนหน้าในทุกทิศทาง:
( scroll-down driver 200 ) ; ; scrolls down by 200 pixels
( scroll-down driver) ; ; scrolls down by the default (100) number of pixels
( scroll-up driver 200 ) ; ; the same, but scrolls up...
( scroll-up driver)
( scroll-left driver 200 ) ; ; ...left
( scroll-left driver)
( scroll-right driver 200 ) ; ; ... and right
( scroll-right driver)
หมายเหตุหนึ่งในทุกกรณีการกระทำของสกรอลล์จะเสิร์ฟพร้อมกับจาวาสคริปต์ ตรวจสอบให้แน่ใจว่าเบราว์เซอร์ของคุณเปิดใช้งานแล้ว
ในขณะที่ทำงานกับหน้าเว็บคุณไม่สามารถโต้ตอบกับรายการที่ใส่ลงในเฟรมหรือ iframe ฟังก์ชั่นด้านล่างสลับบริบทปัจจุบันบนเฟรมเฉพาะ:
( switch-frame driver :frameId ) ; ; now you are inside an iframe with id="frameId"
( click driver :someButton ) ; ; click on a button inside that iframe
( switch-frame-top driver) ; ; switches on the top of the page again
เฟรมสามารถซ้อนกันได้ ฟังก์ชั่นคำนึงถึงสิ่งนั้น สมมติว่าคุณมีเค้าโครง HTML เช่นนี้:
< iframe src =" ... " >
< iframe src =" ... " >
< button id =" the-goal " >
</ frame >
</ frame >
เพื่อให้คุณสามารถเข้าถึงปุ่มด้วยรหัสต่อไปนี้:
( switch-frame-first driver) ; ; switches to the first top-level iframe
( switch-frame-first driver) ; ; the same for an iframe inside the previous one
( click driver :the-goal )
( switch-frame-parent driver) ; ; you are in the first iframe now
( switch-frame-parent driver) ; ; you are at the top
เพื่อลดจำนวนรหัสบรรทัดมีมาโครพิเศษ with-frame
มันสลับเฟรมชั่วคราวในขณะที่ดำเนินการร่างกายที่ส่งคืนนิพจน์สุดท้ายและเปลี่ยนไปใช้เฟรมก่อนหน้าหลังจากนั้น
( with-frame driver { :id :first-frame }
( with-frame driver { :id :nested-frame }
( click driver { :id :nested-button })
42 ))
รหัสด้านบนส่งคืน 42
การพักที่เฟรมเดียวกันกับก่อนหน้านี้ก่อนประเมินมาโคร
เพื่อประเมินรหัส JavaScript ในเบราว์เซอร์ให้เรียกใช้:
( js-execute driver " alert(1) " )
คุณอาจส่งพารามิเตอร์เพิ่มเติมใด ๆ ลงในการโทรและทำให้พวกเขาอยู่ในสคริปต์ที่มีวัตถุ arguments
อาร์เรย์:
( js-execute driver " alert(arguments[2].foo) " 1 false { :foo " hello! " })
เป็นผลให้ hello!
สตริงจะปรากฏขึ้นภายในกล่องโต้ตอบ
หากต้องการส่งคืนข้อมูลใด ๆ ลงใน Clojure เพียงเพิ่ม return
เข้าสู่สคริปต์ของคุณ:
( js-execute driver " return {foo: arguments[2].foo, bar: [1, 2, 3]} "
1 false { :foo " hello! " })
; ; {:bar [1 2 3], :foo "hello!"}
หากสคริปต์ของคุณดำเนินการตามคำขอ AJAX หรือดำเนินการใน setTimeout
หรือสิ่งอื่น ๆ Async คุณจะไม่สามารถ return
ผลลัพธ์ได้ ควรเรียกโทรกลับพิเศษกับข้อมูลที่คุณต้องการให้สำเร็จ WebDriver ผ่านการเรียกกลับนี้เป็นอาร์กิวเมนต์สุดท้ายสำหรับสคริปต์ของคุณและอาจถึงวัตถุ arguments
อาร์เรย์
ตัวอย่าง:
( js-async
driver
" var args = arguments; // preserve the global args
var callback = args[args.length-1];
setTimeout(function() {
callback(args[0].foo.bar.baz);
},
1000); "
{ :foo { :bar { :baz 42 }}})
ส่งคืน 42
ไปยังรหัส Clojure
ในการประเมินสคริปต์แบบอะซิงโครนัสคุณต้องตั้งค่าการหมดเวลาพิเศษสำหรับสิ่งนั้น:
( set-script-timeout driver 5 ) ; ; in seconds
หรือห่อรหัสลงในมาโครที่ทำชั่วคราว:
( with-script-timeout driver 30
( js-async driver " some long script " ))
ความแตกต่างที่สำคัญระหว่างโปรแกรมและมนุษย์คือสิ่งแรกที่ทำงานได้อย่างรวดเร็วมาก มันหมายถึงเร็วมากจนบางครั้งเบราว์เซอร์ไม่สามารถแสดงผล HTML ใหม่ได้ในเวลา ดังนั้นหลังจากการกระทำแต่ละครั้งคุณควรใส่ฟังก์ชั่น wait-<something>
ที่เพียงโพลเบราว์เซอร์จนกระทั่งภาคแสดงจะประเมินเป็นจริง หรือเพียงแค่ (wait <seconds>)
หากคุณไม่สนใจเกี่ยวกับการเพิ่มประสิทธิภาพ
แมโคร with-wait
อาจเป็นประโยชน์เมื่อคุณต้องการเตรียมการแต่ละครั้งด้วย (wait n)
ตัวอย่างเช่นแบบฟอร์มต่อไปนี้
( with-chrome {} driver
( with-wait 3
( go driver " http://site.com " )
( click driver { :id " search_button " })))
กลายเป็นสิ่งนี้:
( with-chrome {} driver
( wait 3 )
( go driver " http://site.com " )
( wait 3 )
( click driver { :id " search_button " }))
และส่งคืนผลลัพธ์ของรูปแบบสุดท้ายของร่างกายดั้งเดิม
มีมาโครอีกตัวหนึ่ง (doto-wait n driver & body)
ที่ทำหน้าที่เหมือน doto
มาตรฐาน แต่ล่วงหน้าแต่ละแบบด้วย (wait n)
ตัวอย่างเช่น:
( with-chrome {} driver
( doto-wait 1 driver
( go " http://site.com " )
( click :this-link )
( click :that-button )
...etc))
แบบฟอร์มสุดท้ายจะเป็นแบบนี้:
( with-chrome {} driver
( doto driver
( wait 1 )
( go " http://site.com " )
( wait 1 )
( click :this-link )
( wait 1 )
( click :that-button )
...etc))
นอกเหนือจาก with-wait
และ do-wait
ยังมีฟังก์ชั่นการรอคอยจำนวนมาก: wait-visible
wait-has-alert
wait-predicate
ฯลฯ (ดูรายการทั้งหมดในส่วนสหสัมพันธ์) พวกเขายอมรับค่าการหมดเวลา/ช่วงเวลาเริ่มต้นที่สามารถนิยามใหม่โดยใช้ with-wait-timeout
และ with-wait-interval
ตามลำดับ
ตัวอย่างจากการทดสอบ Etaoin:
( deftest test-wait-has-text
( testing " wait for text simple "
( with-wait-timeout 15 ; ; time in seconds
( doto *driver*
( refresh )
( wait-visible { :id :document-end })
( click { :id :wait-button })
( wait-has-text :wait-span " -secret- " ))
( is true " text found " ))))
รอข้อความ:
wait-has-text
รอจนกว่าองค์ประกอบจะมีข้อความอยู่ข้างใน (รวมถึง HTML ภายใน)
( wait-has-text driver :wait-span " -secret- " )
wait-has-text-everywhere
เช่น wait-has-text
แต่ค้นหาข้อความตลอดทั้งหน้า
( wait-has-text-everywhere driver " -secret- " )
เพื่อให้การทดสอบของคุณไม่ได้ขึ้นอยู่กับกันและกันคุณต้องห่อไว้ในการติดตั้งที่จะสร้างอินสแตนซ์ใหม่ของไดรเวอร์และปิดมันอย่างถูกต้องในตอนท้ายหากการทดสอบแต่ละครั้ง
ทางออกที่ดีอาจมีตัวแปรทั่วโลก (ไม่ผูกมัดโดยค่าเริ่มต้น) ซึ่งจะชี้ไปที่ไดรเวอร์เป้าหมายในระหว่างการทดสอบ
( ns project.test.integration
" A module for integration tests "
( :require [clojure.test :refer :all ]
[etaoin.api :refer :all ]))
( def ^:dynamic *driver*)
( defn fixture-driver
" Executes a test running a driver. Bounds a driver
with the global *driver* variable. "
[f]
( with-chrome {} driver
( binding [*driver* driver]
( f ))))
( use-fixtures
:each ; ; start and stop driver for each test
fixture-driver)
; ; now declare your tests
( deftest ^:integration
test-some-case
( doto *driver*
( go url-project)
( click :some-button )
( refresh )
...
))
หากด้วยเหตุผลบางอย่างที่คุณต้องการใช้อินสแตนซ์เดียวคุณสามารถใช้อุปกรณ์ติดตั้งเช่นนี้:
( ns project.test.integration
" A module for integration tests "
( :require [clojure.test :refer :all ]
[etaoin.api :refer :all ]))
( def ^:dynamic *driver*)
( defn fixture-browser [f]
( with-chrome-headless { :args [ " --no-sandbox " ]} driver
( disconnect-driver driver)
( binding [*driver* driver]
( f ))
( connect-driver driver)))
; ; creating a session every time that automatically erases resources
( defn fixture-clear-browser [f]
( connect-driver *driver*)
( go *driver* " http://google.com " )
( f )
( disconnect-driver *driver*))
; ; this is run `once` before running the tests
( use-fixtures
:once
fixture-browser)
; ; this is run `every` time before each test
( use-fixtures
:each
fixture-clear-browser)
...some tests
สำหรับการทดสอบที่เร็วขึ้นคุณสามารถใช้ตัวอย่างนี้:
.....
( defn fixture-browser [f]
( with-chrome-headless { :args [ " --no-sandbox " ]} driver
( binding [*driver* driver]
( f ))))
; ; note that resources, such as cookies, are deleted manually,
; ; so this does not guarantee that the tests are clean
( defn fixture-clear-browser [f]
( delete-cookies *driver*)
( go *driver* " http://google.com " )
( f ))
......
ในตัวอย่างด้านบนเราตรวจสอบกรณีเมื่อคุณเรียกใช้การทดสอบกับไดรเวอร์ประเภทเดียว อย่างไรก็ตามคุณอาจต้องการทดสอบไซต์ของคุณในหลาย ๆ ไดรเวอร์พูด Chrome และ Firefox ในกรณีนี้การติดตั้งของคุณอาจซับซ้อนขึ้นเล็กน้อย:
( def driver-type [ :firefox :chrome ])
( defn fixture-drivers [f]
( doseq [type driver-types]
( with-driver type {} driver
( binding [*driver* driver]
( testing ( format " Testing in %s browser " ( name type))
( f ))))))
ตอนนี้การทดสอบแต่ละครั้งจะทำงานสองครั้งทั้งในเบราว์เซอร์ Firefox และ Chrome โปรดทราบว่าการโทรทดสอบจะถูกเตรียมไว้ล่วงหน้าด้วย testing
มาโครที่ใส่ชื่อไดรเวอร์ลงในรายงาน เมื่อคุณมีข้อผิดพลาดคุณจะพบสิ่งที่ไดรเวอร์ล้มเหลวในการทดสอบอย่างแน่นอน
เพื่อบันทึกสิ่งประดิษฐ์บางอย่างในกรณีที่มีข้อยกเว้นให้ห่อศพของการทดสอบของคุณลงในตัวจัดการ with-postmortem
ดังนี้:
( deftest test-user-login
( with-postmortem *driver* { :dir " /path/to/folder " }
( doto *driver*
( go " http://127.0.0.1:8080 " )
( click-visible :login )
; ; any other actions...
)))
ตอนนี้หากมีข้อยกเว้นใด ๆ เกิดขึ้นในการทดสอบนั้นจะมีการบันทึกสิ่งประดิษฐ์
หากต้องการคัดลอกและวางแผนที่ตัวเลือกประกาศไว้ที่ด้านบนของโมดูล หากคุณใช้ Circle CI มันจะเป็นการดีที่จะบันทึกข้อมูลลงในไดเรกทอรีสิ่งประดิษฐ์พิเศษที่อาจดาวน์โหลดเป็นไฟล์ zip เมื่อบิลด์เสร็จสิ้น:
( def pm-dir
( or ( System/getenv " CIRCLE_ARTIFACTS " ) ; ; you are on CI
" /some/local/path " )) ; ; local machine
( def pm-opt
{ :dir pm-dir})
ตอนนี้ผ่านแผนที่นั้นทุกที่ใน PM Handler:
; ; test declaration
( with-postmortem *driver* pm-opt
; ; test body goes here
)
เมื่อเกิดข้อผิดพลาดคุณจะพบภาพ PNG ที่แสดงหน้าเบราว์เซอร์ของคุณในขณะที่มีข้อยกเว้นและการถ่ายโอนข้อมูล HTML
เนื่องจากการทดสอบ UI อาจใช้เวลานานในการผ่านมันจึงเป็นวิธีปฏิบัติที่ดีในการผ่านการทดสอบทั้งเซิร์ฟเวอร์และ UI อย่างอิสระจากกันและกัน
ก่อนอื่นเพิ่ม ^:integration
กับการทดสอบทั้งหมดที่ทำงานในเบราว์เซอร์ดังนี้:
( deftest ^:integration
test-password-reset-pipeline
( doto *driver*
( go url-password-reset)
( click :reset-btn )
...
จากนั้นเปิดไฟล์ project.clj
ของคุณและเพิ่มตัวเลือกทดสอบ:
:test-selectors { :default ( complement :integration )
:integration :integration }
ตอนนี้เมื่อคุณเปิด lein test
คุณจะทำการทดสอบทั้งหมดยกเว้นเบราว์เซอร์ ในการเรียกใช้การทดสอบการรวมให้เปิด lein test :integration
ความแตกต่างที่สำคัญระหว่างโปรแกรมและมนุษย์คือคนแรกทำงานได้อย่างรวดเร็ว มันหมายถึงเร็วมากจนบางครั้งเบราว์เซอร์ไม่สามารถแสดงผล HTML ใหม่ได้ในเวลา ดังนั้นหลังจากการกระทำแต่ละครั้งคุณต้องใส่ฟังก์ชั่น wait-<something>
ที่เพิ่งสำรวจเบราว์เซอร์เพื่อตรวจสอบเพรดิเคต o เพียงแค่ (wait <seconds>)
หากคุณไม่สนใจเกี่ยวกับการเพิ่มประสิทธิภาพ
บางครั้งไฟล์จะเริ่มดาวน์โหลดโดยอัตโนมัติเมื่อคุณคลิกที่ลิงค์หรือเพียงแค่เข้าชมบางหน้า ในการทดสอบคุณต้องตรวจสอบให้แน่ใจว่าไฟล์ได้รับการดาวน์โหลดเรียบร้อยแล้ว สถานการณ์ทั่วไปคือ:
ตัวอย่าง:
; ; Local helper that checks whether it is really an Excel file.
( defn xlsx? [file]
( -> file
.getAbsolutePath
( str/ends-with? " .xlsx " )))
; ; Top-level declarations
( def DL-DIR " /Users/ivan/Desktop " )
( def driver ( chrome { :download-dir DL-DIR}))
; ; Later, in tests...
( click-visible driver :download-that-application )
( wait driver 7 ) ; ; wait for a file has been downloaded
; ; Now, scan the directory and try to find a file:
( let [files ( file-seq ( io/file DL-DIR))
found ( some xlsx? files)]
( is found ( format " No *.xlsx file found in %s directory. " DL-DIR)))
Etaoin สามารถเล่นไฟล์ที่ผลิตโดย Selenium IDE มันเป็นยูทิลิตี้อย่างเป็นทางการในการสร้างสถานการณ์แบบโต้ตอบ IDE มาเป็นส่วนขยายของเบราว์เซอร์ของคุณ เมื่อติดตั้งแล้วจะบันทึกการกระทำของคุณลงในไฟล์ JSON ด้วยส่วน .side
. คุณสามารถบันทึกไฟล์นั้นและเรียกใช้ด้วย Etaoin
ลองจินตนาการว่าคุณได้ติดตั้ง IDE และบันทึกการกระทำบางอย่างตามที่กำหนดไว้อย่างเป็นทางการ ตอนนี้คุณมีไฟล์ test.side
แล้วทำสิ่งนี้:
( require '[etaoin.ide.flow :as flow])
( def driver ( chrome ))
( def ide-file ( io/resource " ide/test.side " ))
( def opt
{ ; ; The base URL redefines the one from the file.
; ; For example, the file was written on the local machine
; ; (http://localhost:8080), and we want to perform the scenario
; ; on staging (https://preprod-001.company.com)
:base-url " https://preprod-001.company.com "
; ; keywords :test-.. and :suite-.. (id, ids, name, names)
; ; are used to select specific tests. When not passed,
; ; all tests get run. For example:
:test-id " xxxx-xxxx... " ; ; a single test by its UUID
:test-name " some-test " ; ; a single test by its name
:test-ids [ " xxxx-xxxx... " , ...] ; ; multiple tests by their ids
:test-names [ " some-test1 " , ...] ; ; multiple tests by their names
; ; the same for suites:
:suite-id ...
:suite-name ...
:suite-ids [...]
:suite-names [...]})
( flow/run-ide-script driver ide-file opt)
ทุกอย่างที่เกี่ยวข้องกับ IDE จะถูกเก็บไว้ภายใต้แพ็คเกจ etaoin.ide
คุณอาจเรียกใช้สคริปต์จากบรรทัดคำสั่ง นี่คือตัวอย่างของ lein run
:
lein run -m etaoin.ide.main -d firefox -p ' {:port 8888 :args ["--no-sandbox"]} ' -r ide/test.side
เช่นเดียวกับจาก Uberjar ในกรณีนี้ Etaoin จะต้องอยู่ในการพึ่งพาหลักไม่ใช่ :dev
หรือ :test
ที่เกี่ยวข้อง
java -cp .../poject.jar -m etaoin.ide.main -d firefox -p ' {:port 8888} ' -f ide/test.side
เราสนับสนุนอาร์กิวเมนต์ต่อไปนี้ (ตรวจสอบพวกเขาโดยใช้คำสั่ง lein run -m etaoin.ide.main -h
)::
-d, --driver-name name :chrome The name of driver. The default is `:chrome`
-p, --params params {} Parameters for the driver represented as an
EDN string, e.g '{:port 8080}'
-f, --file path Path to an IDE file on disk
-r, --resource path Path to an IDE resource
--test-ids ids Comma-separeted test ID(s)
--suite-ids ids Comma-separeted suite ID(s)
--test-names names Comma-separeted test name(s)
--suite-names names Comma-separeted suite name(s)
--base-url url Base URL for tests
-h, --help
ให้ความสนใจกับ --params
One นี่จะต้องเป็นสตริง EDN ที่แสดงถึงแผนที่ Clojure นั่นคือแผนที่เดียวกันกับที่คุณส่งผ่านไปยังไดรเวอร์เมื่อเริ่มต้น
โปรดทราบว่าการสนับสนุน IDE ยังคงทดลองอยู่ หากคุณพบพฤติกรรมที่ไม่คาดคิดอย่าลังเลที่จะเปิดปัญหา ในขณะนี้เรารองรับ Chrome และ Firefox สำหรับไฟล์ IDE เท่านั้น
ตัวอย่าง:
etaoin.api> ( def driver ( chrome ))
#'etaoin.api/driver
etaoin.api> ( maximize driver)
ExceptionInfo throw+: { :response {
:sessionId " 2672b934de785aabb730fd19330cf40c " ,
:status 13 ,
:value { :message " unknown error: cannot get automation extension n from unknown error: page could not be found: chrome-extension://aapnijgdinlhnhlmodcfapnahmbfebeb/_generated_background_page.html n
(Session info: chrome=57.0.2987.133) n (Driver info: chromedriver=2.27.440174
(e97a722caafc2d3a8b807ee115bfb307f7d2cfd9),platform=Mac OS X 10.11.6 x86_64) " }},
...
วิธีแก้ปัญหา: เพียงอัปเดต chromedriver
ของคุณเป็นเวอร์ชันล่าสุด ทดสอบด้วย 2.29 ทำงานได้ดี มีคนบอกว่ามันก็พากันเช่นกันตั้งแต่ 2.28
โปรดจำไว้ว่า brew
Package Manager มีเวอร์ชันที่ล้าสมัย 2.27 คุณอาจต้องดาวน์โหลดไบนารีจากเว็บไซต์อย่างเป็นทางการ
ดูปัญหาที่เกี่ยวข้องในโครงการซีลีเนียม
เมื่อผ่านการสืบค้นแบบเวกเตอร์ให้พูด [{:tag :p} "//*[text()='foo')]]"}]
ระวังด้วยการแสดงออก XPath ที่เขียนด้วยมือ ในเวกเตอร์นิพจน์ทุกครั้งจะค้นหาจากอันก่อนหน้าในลูป มีข้อผิดพลาดที่ซ่อนอยู่ที่นี่: หากไม่มีจุดนำชั้นนำ "//..."
ประโยคหมายถึงการค้นหาองค์ประกอบจากรูทของทั้งหน้า ด้วยจุดนั้นหมายถึงการค้นหาจากโหนดปัจจุบันซึ่งเป็นหนึ่งจากแบบสอบถามก่อนหน้าและอื่น ๆ
นั่นเป็นเหตุผลว่าทำไมจึงเป็นเรื่องง่ายที่จะเลือกสิ่งที่แตกต่างอย่างสิ้นเชิงกับสิ่งที่คุณต้องการ นิพจน์ที่เหมาะสมจะเป็น: [{:tag :p} ".//*[text()='foo')]]"}]
ตัวอย่าง:
etaoin.api> ( click driver :some-id )
ExceptionInfo throw+: { :response {
:sessionId " d112ce8ddb49accdae78a769d5809eae " ,
:status 11 ,
:value { :message " element not visible n (Session info: chrome=57.0.2987.133) n
(Driver info: chromedriver=2.29.461585
(0be2cd95f834e9ee7c46bcc7cf405b483f5ae83b),platform=Mac OS X 10.11.6 x86_64) " }},
...
วิธีแก้ปัญหา: คุณกำลังพยายามคลิกองค์ประกอบที่มองไม่เห็นหรือการทำชิ้นส่วนของมันนั้นน้อยที่สุดเท่าที่จะเป็นไปไม่ได้ที่มนุษย์จะคลิก คุณควรผ่านตัวเลือกอื่น
ปัญหา: เมื่อคุณมุ่งเน้นไปที่หน้าต่างอื่นเซสชัน WebDriver ที่ทำงานภายใต้ Google Chrome ล้มเหลว
วิธีแก้ปัญหา: Google Chrome อาจระงับแท็บเมื่อไม่ได้ใช้งานมาระยะหนึ่งแล้ว เมื่อหน้าถูกระงับจะไม่สามารถดำเนินการได้ ไม่มีการคลิกการดำเนินการ JS ฯลฯ ดังนั้นลองให้หน้าต่างโครเมี่ยมทำงานในระหว่างการทดสอบ
ปัญหา: เมื่อคุณพยายามเริ่มไดรเวอร์คุณจะได้รับข้อผิดพลาด:
user=> ( use 'etaoin.api)
user=> ( def driver ( firefox { :headless true }))
ข้อผิดพลาดไวยากรณ์ (ExceptionInfo) รวบรวมที่ (REPL: 1: 13) Throw+: {: การตอบสนอง {: ค่า {: ข้อผิดพลาด "ข้อผิดพลาดที่ไม่รู้จัก",: ข้อความ "อาร์กิวเมนต์ไม่ถูกต้อง: ไม่สามารถฆ่ากระบวนการที่ออกได้" ....
สาเหตุที่เป็นไปได้: "เรียกใช้ Firefox เป็นรูทในเซสชันของผู้ใช้ปกติไม่รองรับ"
การแก้ไข: ในการตรวจสอบเรียกใช้ไดรเวอร์ด้วยพา ธ ไปยังไฟล์บันทึกและระดับบันทึก "ติดตาม" และสำรวจเอาต์พุตของพวกเขา
( def driver ( firefox { :log-stdout " ffout.log " :log-stderr " fferr.log " :driver-log-level " trace " }))
ปัญหาที่คล้ายกัน: Mozilla/Geckodriver#1655
ปัญหา: เมื่อคุณพยายามเริ่ม Chromedriver คุณจะได้รับข้อผิดพลาด:
clojure.lang.exceptionInfo: throw+: {: การตอบสนอง {: sessionid "..... " ,: สถานะ 13,: ค่า {: ข้อความ "ข้อผิดพลาดที่ไม่รู้จัก: Chrome ล้มเหลวในการเริ่มต้น: ออกผิดปกติ n (ข้อผิดพลาดที่ไม่รู้จัก ไฟล์ไม่มีอยู่) ...
สาเหตุที่เป็นไปได้:
สาเหตุที่พบบ่อยสำหรับโครเมี่ยมที่จะเกิดความผิดพลาดในระหว่างการเริ่มต้นคือการเรียกใช้ Chrome ในฐานะผู้ใช้รูท (ผู้ดูแลระบบ) บน Linux ในขณะที่เป็นไปได้ที่จะแก้ไขปัญหานี้โดยผ่าน-No-Sandbox Flag เมื่อสร้างเซสชัน WebDriver ของคุณการกำหนดค่าดังกล่าวไม่ได้รับการสนับสนุนและท้อแท้อย่างมาก You need to configure your environment to run Chrome as a regular user instead.
Solution: Run driver with an argument --no-sandbox
. คำเตือน! This is a bypass OS security model.
( def driver ( chrome { :args [ " --no-sandbox " ]}))
A similar problem is described here
The etaoin.api2
namespace brings some bits of alternative macros and functions. They provide better syntax and live in a separate namespace to prevent the old API from breaking.
At the moment, the api2
module provides a set of with-...
macros with a let
-like binding form:
( ns ...
( :require
[etaoin.api :as api]
[etaoin.api2 :as api2]))
( api2/with-chrome [driver {}]
( api/go driver " http://ya.ru " ))
The options map can be skipped so you have only a binding symbol:
( api2/with-firefox [ff]
( api/go ff " http://ya.ru " ))
The project is open for your improvements and ideas. If any of unit tests fall on your machine please submit an issue giving your OS version, browser and console output.
Copyright © 2017—2020 Ivan Grishaev.
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.